How to display Json nested objects (maps) in Flutter? - json

I am new to flutter, I want to display json nested objects "nom" values (So I can later on create a tabBar using these values).
I am getting this error
type 'Null' is not a subtype of type 'Map<dynamic, dynamic>'
This is the json file I am working on
This is the main code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'tabbarclass.dart';
Future<stations> fetchDonnee() async {
print('fetch0');
final response = await http.get(Uri.parse('uri'));
if (response.statusCode == 200) {
print('fecth1');
// If the server did return a 200 OK response, then parse the JSON.
return stations.fromJson(jsonDecode(response.body));
}
else {
// If the server did not return a 200 OK response, then throw an exception.
throw Exception('Failed to load album');
}
}
class MyApp3 extends StatefulWidget {
const MyApp3({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp3> {
late Future<stations> futureDonnee;
Timer? timer;
#override
void initState() {
print('initstate');
futureDonnee=fetchDonnee();
timer= Timer.periodic(const Duration(seconds:1), (Timer t){
futureDonnee=fetchDonnee();
setState(() {});
print('initstate1');}
);
print('hi1');
super.initState();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
print('builder');
FutureBuilder f1;
print('ok');
Center(
child:
f1=FutureBuilder<stations>(
future: futureDonnee,
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data!.comp);
//snapshot.data!.comp.forEach((k, v) => print(k));
return Scaffold(
body: Container(
width: size.width,
height: 85,
margin: EdgeInsets.fromLTRB(20, 20, 20, 0),
padding: const EdgeInsets.fromLTRB(10, 0, 10, 20),
decoration: const BoxDecoration(
color: Colors.white
),
child: Text('${snapshot.data!.ond}',style: TextStyle(fontSize: 13)),
)
);
}
else if (snapshot.hasError) {
return Text('${snapshot.error}');}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
)
);
return f1;
}
}
This is the class I defined
class stations {
final Map ond;
final Map comp;
final Map gaz;
final Map eau;
final Map nom;
const stations({
required this.ond,
required this.comp,
required this.eau,
required this.gaz,
required this.nom,
});
factory stations.fromJson(Map<String, dynamic>json) {
return stations(
ond: json['ond'],
comp: json['comp'],
eau: json['eau'],
gaz: json['gaz'],
nom: json['nom'],
);
}
}
And this is the response I am getting
Any help is much appreciated.

Related

flutter LateError (LateInitializationError: Field 'user Data' has not been initialized.)

Below is the code I wrote to extract data from MySQL database with flutter rest API. However, I am getting the following error while printing the data I received to the mobile screen. How can I fix it?
LateError (LateInitializationError: Field 'user Data' has not been initialized.)
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: main1(),
);
}
}
class main1 extends StatefulWidget {
main1({Key? key}) : super(key: key);
#override
State<main1> createState() => _main1State();
}
class _main1State extends State<main1> {
late List userData;
late Map data;
#override
void initState() {
// TODO: implement initState
loaddata();
super.initState();
}
void loaddata() async {
var url = "http://192.168.1.106/server/data.php";
var res = await http.get(Uri.parse(url));
data = json.decode(res.body);
userData = data["data"];
print(userData);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Fake Friends"),
backgroundColor: Colors.green,
),
body: ListView.builder(
itemCount: userData == null ? 0 : userData.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Text(userData[index]["projename"]),
);
}),
);
}
}
<?php
require_once("dbconfig.php");
$json["error"] = false;
$json["errmsg"] = "";
$json["data"] = array();
//Fetch 3 rows from actor table
$sql = "SELECT * FROM projeinfo";
$res = mysqli_query($db, $sql);
$numrows = mysqli_num_rows($res);
if($numrows > 0){
//check if there is any data
$namelist = array();
while($array = mysqli_fetch_assoc($res)){
array_push($json["data"], $array);
//push fetched array to $json["data"]
}
}else{
$json["error"] = true;
$json["errmsg"] = "No any data to show.";
}
mysqli_close($db);
header('Content-Type: application/json');
// tell browser that its a json data
echo json_encode($json);
?>
If you set a variable to late it cannot be null. So in your build function it assumes, that it is initialized, but it isn't, because load data is a future and the result is ready after the build function gets called first.
2 possible ways.
1.
List? userData;
Map? data;
Use a FutureBuilder
And: You don't call setState in your loadData(), so it does not get rebuild if the data is available. For the first approach, this is necessary!

Future Builder for using Json response on map

I am calling an API and utilising the return Json data to post markers on a map
I have the Call returning a ListView fine on a separate application
I tried to implement my Future builder from my other application calling the same HTTP, with some modifications.
I am now getting errors from the Future Builder construction after trying to implement a Future Builder, which I have been told will fix the previous error as it is mandatory, to the best of my beginners ability!
I am pulling the stations.place.location.lat & stations.place.location.lng Json to use in GeoCoordinates to place the marker from my API call
Here is the Dart code I am using, any guidance is appreciated. I will exclude code which is irrelevant to this issue.
The Future Builder (of which there is some rough code left in my example below) needs to go into my Main.dart
void main() {
SdkContext.init(IsolateOrigin.main);
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Stations> stations;
#override
void initState() {
stations = API_Call().fetchStations();
super.initState();
}
BuildContext _context;
MapMarkerExample _mapMarkerExample;
#override
Widget build(BuildContext context) {
_context = context;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Example 1'),
),
body: Container(
child:
FutureBuilder<Stations>(
future: stations,
builder: (BuildContext context, AsyncSnapshot<Stations> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Stack(
children: [
HereMap(onMapCreated: _onMapCreated),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
button('Call', _anchoredMapMarkersButtonClicked),
button('Clear', _clearButtonClicked),
],
),
],
),
],
);
}
);
}
}
)
)
)
);
}
...
api_manager.dart
class MapMarkerExample{
void showAnchoredMapMarkers() {
var stations;
stations = API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
}
GeoCoordinates geoCoordinates = stations.coordinates;
_addPOIMapMarker(geoCoordinates, 1);
}
...
api_call.dart
class API_Call {
Future<Stations> fetchStations() async {
var client = http.Client();
final response = await client.get(
'https://transit.hereapi.com/v8/stations?in=x,-x&return=transport&apiKey=MY_API_KEY');
if (response.statusCode == 200) {
return Stations.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load stations');
}
}
}
If your service is returning a list, you should handle it as List.
List<Station> stations = [];
final responseData = json.decode(response.body);
stations = responseData.map((model) => Station.fromJson(model)).toList();
And don't forget the itemLength value for ListView.builder()

A non-null String must be provided to a Text widget./Failed assertion: line 380 pos 10: 'data != null'

I just want to get a weather forecast data from the Internet. I wrote this code below but every time I got the error:
A non-null String must be provided to a Text widget
For some reason I could not take decoded json data
import 'package:flutter/material.dart';
import 'network.dart';
class HomePage extends StatefulWidget {
static String id;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Map<String,dynamic> currentPositionWeatherData;
String temperature;
String weatherDescription;
String name;
void initState(){
super.initState();
getCurrentLocationWeather();
updateUI();
}
void getCurrentLocationWeather()async{
currentPositionWeatherData=await NetworkHelperForWeatherInfo().getCurrentPositionWeatherData();
}
void updateUI()async{
setState(() {
weatherDescription = currentPositionWeatherData["weather"][0]["description"];
temperature = currentPositionWeatherData["main"]["temp"];
name = currentPositionWeatherData["name"];
});
}
#override
Widget build(BuildContext context) {
return Center(
child: Container(
color: Colors.black,
child: Text(
temperature,
),
),
);
}
}
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart';
const openWeatherURL="http://api.openweathermap.org/data/2.5/weather";
const apikey="57aad03f4e48ca815bb1184e74624f46";
class NetworkHelperForWeatherInfo{
Future getCurrentPositionWeatherData()async{
Position position = await Geolocator.getCurrentPosition();
Response response = await get("$openWeatherURL?lat=${position.latitude}&lon=${position.latitude}&appid=$apikey");
if(response.statusCode ==200){
String currentPositionWeatherData = response.body;
return jsonDecode(currentPositionWeatherData);
}
else{
return response.statusCode;
}
}
}
import 'package:flutter/material.dart';
import 'homepage.dart';
import 'package:geolocator/geolocator.dart';
class LocationFinder{
bool serviceEnabled;
LocationPermission permission;
Future<Position> determineCurrentPosition()async{
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if(serviceEnabled != true){
return Future.error("Location services are disabled");
}
permission = await Geolocator.checkPermission();
if (permission==LocationPermission.deniedForever) {
return Future.error("Location permissions are permantly denied,we cannot request permissions.");
}
if (permission == LocationPermission.denied){
permission = await Geolocator.requestPermission();
if(permission != LocationPermission.whileInUse &&
permission != LocationPermission.whileInUse){
return Future.error("Location permission are denied/ actual value:$permission");
}
}
Position position =await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
return position;
}
}
child: Text(
temperature ?? 'waiting data...',
)
When _HomePageState State widget is first built, temperature variable will be null (until data is retrieved and the widget rebuilt through setState call). A null value isn't allowed by the Text widget which asserts the text value not be null.
The above change will supply the text waiting data... when temperature is null.

Flutter: convert future to stream

I am getting data as Future from my json API. And use my Flutter Mobile app.
Here is my code for getting data from API-
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
Future<List<Book>> fetchBooks(http.Client client) async {
final response =
await client.get('json URL');
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parseBooks, response.body);
}
// A function that converts a response body into a List<Photo>.
List<Book> parseBooks(String responseBody) {
final parsed = jsonDecode(responseBody).cast<String, dynamic>();
return parsed['data'].map<Book>((json) => Book.fromJson(json)).toList();
}
class Book {
final String name;
final String author;
final String genreClass;
final String pdf;
final int category;
Book({
this.name,
this.author,
this.genreClass,
this.pdf,
this.category,
});
factory Book.fromJson(Map<String, dynamic> json) {
return Book(
name: json['name'] as String,
pdf: json['pdf'] as String,
author: json['author'] as String,
genreClass: json['genre_class'] as String,
category: json['category'] as int,
);
}
}
But I want to get it as Stream.Please someone help me, how can I convert my code from Future to Stream ?
Abir Ahsan try this, call(use) your function like this : fetchBooks(client).asStream()...
If you're intend to get steam with your builder function is called. You will need to declare a StreamController, it creates a simple stream that can be listened to and you basically push streams of events to the stream, using the sink. A simple snippet can help in terms of displaying a ticking crypto price every 3 seconds:
StreamController<DataModel> _streamController = StreamController();
#override
void dispose() {
// stop streaming when app close
_streamController.close();
}
#override
void initState() {
// TODO: implement initState
super.initState();
// A Timer method that run every 3 seconds
Timer.periodic(Duration(seconds: 3), (timer) {
getCryptoPrice();
});
}
// a future method that fetch data from API
Future<void> getCryptoPrice() async{
var url = Uri.parse('https://api.nomics.com/v1/currencies/ticker?key=your_api_key&ids=DOGE');
final response = await http.get(url);
final databody = json.decode(response.body).first;
DataModel dataModel = new DataModel.fromJson(databody);
// add API response to stream controller sink
_streamController.sink.add(dataModel);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<DataModel>(
stream: _streamController.stream,
builder: (context,snapdata){
switch(snapdata.connectionState){
case ConnectionState.waiting: return Center(child: CircularProgressIndicator(),);
default: if(snapdata.hasError){
return Text('Please Wait....');
}else{
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${dataModel.name}',style: TextStyle(fontSize: 25),),
SizedBox(height: 20,),
SvgPicture.network('${dataModel.image}',width: 150,height: 150,),
SizedBox(height: 20,),
Text('\$${dataModel.price}',style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold),)
],
),
);
}
}
},
),
),
);
}
More about the code here: https://protocoderspoint.com/flutter-dart-stream-basic-example-fetch-crypto-currency-api-data/
You can Learn more: using these references
https://dart.dev/articles/libraries/creating-streams#creating-a-stream-from-scratch
https://api.dart.dev/stable/2.16.2/dart-async/StreamController-class.html

What's the best way to serialize data from Firebase into Dart object for Flutter?

What is the best way to serialize a list of data from Firebase? Firebase provides an object with a list of properties for the list which makes it more challenging to come up with a good conversion technique.
How would you serialize this data from Firebase:
{
"-KiRg_F-qC59xxlfZ6ej": {
"first":"Brandon",
"last":"Donnelson"
},
"-KiRgmsISBsJSWfXhrdD": {
"first":"Danny",
"last":"Kirk"
}
}
What I came up with — see _loadData()) —:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Text(
'click',
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _test,
tooltip: 'Increment',
child: new Icon(Icons.add),
),);
}
void _test() {
_loadData();
}
_loadData() async {
String url = 'https://dev-xxxxxxx.firebaseio.com/names.json';
var httpClient = createHttpClient();
var response = await httpClient.read(url);
print('response=' + response);
// response = {
// "-KiRg_F-qC59xxlfZ6ej":{"first":"Brandon","last":"Donnelson"},
// "-KiRgmsISBsJSWfXhrdD":{"first":"Danny","last":"Kirk"}
// }
NamesData namesData = new NamesData(JSON.decode(response));
print("names.len=" + namesData.names.length.toString());
}
}
class NamesData {
final List<NameData> names = new List();
NamesData(Map data) {
data.values.forEach((Map map) => names.add(new NameData.fromJson(map)));
}
}
class NameData {
String first;
String last;
NameData.fromJson(Map map) {
first = map['first'];
last = map['last'];
}
}
I found the JSON decoder has a better method for instantiating classes with the reviver function. This feels much better, but I think I can do better.
_loadData() async {
String url = 'https://dev-xxxxxxx.firebaseio.com/names.json';
var httpClient = createHttpClient();
var response = await httpClient.read(url);
print('response=' + response);
// response = {
// "-KiRg_F-qC59xxlfZ6ej":{"first":"Brandon","last":"Donnelson"},
// "-KiRgmsISBsJSWfXhrdD":{"first":"Danny","last":"Kirk"}
// }
var extendedJson = new JsonCodec(reviver: _reviver);
var o = extendedJson.decode(response);
print('end');
}
// https://github.com/dart-lang/sdk/blob/master/tests/lib/convert
// /json_toEncodable_reviver_test.dart
_reviver(key, value) {
if (value != null && value is Map && key.toString().contains("-")) {
return new NameData2(value);
}
return value;
}
}
class NameData2 {
String first;
String last;
NameData2(Map map) {
first = map['first'];
last = map['last'];
}
}
I personally like writing a tiny Codec sometimes:
DartPad example
import 'dart:convert';
void main() {
final decoder = const FirebaseNamesDecoder();
print(decoder.convert(exampleFirebaseData));
}
class NamedData {
final String id;
final String firstName;
final String lastName;
const NamedData(this.id, this.firstName, this.lastName);
#override
String toString() => '$NamedData {$id: $firstName $lastName}';
}
class FirebaseNamesDecoder extends Converter<Map, Iterable<NamedData>> {
const FirebaseNamesDecoder();
#override
Iterable<NamedData> convert(Map<String, Map> raw) {
return raw.keys.map((id) => new NamedData(id, raw[id]['first'], raw[id]['last']));
}
}
final exampleFirebaseData = {
"-KiRg_F-qC59xxlfZ6ej": {
"first":"Brandon",
"last":"Donnelson"
},
"-KiRgmsISBsJSWfXhrdD": {
"first":"Danny",
"last":"Kirk"
}
};
Results in:
(
NamedData {-KiRg_F-qC59xxlfZ6ej: Brandon Donnelson},
NamedData {-KiRgmsISBsJSWfXhrdD: Danny Kirk}
)
Dart 2 needs modification to the overridden method:
Iterable<NamedData> convert(Map<dynamic,dynamic> raw) {
return raw.keys
.map((id) => new NamedData(id, raw[id]['first'], raw[id]['last']));
}
Serializing JSON manually using dart:convert
Basic JSON serialization in Flutter is very simple. Flutter has a built-in dart:convert library which includes a straightforward JSON encoder and decoder.
The following sample JSON implements a simple user model.
{"name":"John Smith","email":"john#example.com"}
With dart:convert, you can serialize this JSON model in two ways.
1) Serializing JSON inline
   
Map<String, dynamic> user = jsonDecode(jsonString);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
2) Serializing JSON inside model classes
class User
{
final Stringname;
final Stringemail;
User(this.name,this.email);
User.fromJson(Map<String,dynamic>json):name=json['name'],email=json['email'];
Map<String,dynamic>toJson()=>
{
'name':name,
'email':email,
};
}
The responsibility of the decoding logic is now moved inside the model itself. With this new approach, you can decode a user easily.
Map userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
I would recommend using json_serializable it is developed by google developers and it can handle the boilerplate code easily.