How to parse a document from my Firestore Database - dart

How can I parse the data from my Firestore. My Document looks like:
with this code, I get my data.
StreamBuilder(
stream: Firestore.instance.collection("Benutzer").document("Anton").collection("Einkaufsliste").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index){
return ExpansionTile(
title: Text(snapshot.data.documents[index].documentID),
children: buildCheckBoxListTile(index, snapshot),
in the buildCheckBoxListTile(index, snapshot) Function I wanted to get the data from the maps. So, how can I parse these Data?
Thank you for help.
Edit:
I created a class Produkte:
class Produkte{
Produkte({this.produktList});
List<Produkt> produktList;
factory Produkte.fromJson(Map<String, dynamic> parsedJson){
var list = parsedJson["Produkte"] as List;
List<Produkt> products = list.map((i) => Produkt.fromJson(i)).toList();
return Produkte(
produktList: products,
);
}
}
And a class Produkt:
class Produkt{
Produkt({this.name, this.anzahl});
String name;
int anzahl;
factory Produkt.fromJson(Map<String, dynamic> parsedJson){
return Produkt(
name: parsedJson["Name"],
anzahl: parsedJson["Anzahl"]
);
}
}
Now when i call this: Produkte Hans = Produkte.fromJson(snapshot.data.documents[index].data);
the follwoing Exception is thrown:
I/flutter (18038): Another exception was thrown: type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'

i had the same issue. I am using the json_serializable package for automatic generation.
For me it helped, to remove the cast types from the map
factory Produkt.fromJson(Map parsedJson)
and allowed any map on the annoation
#JsonSerializable(anyMap: true)
There was an issue with some discussion over this topic in the flutter repository. HerrNiklasRaab posted a other workaround there as well:
factory Chat.fromJson(Map<String, dynamic> json) {
json["members"] = (json['members'] as List)
?.map((e) =>
e == null ? null : Map<String, dynamic>.from(e))
?.toList();
return _$ChatFromJson(json);
}

Related

Fetching all entries from db(Firestore) to a list using a model

Trying to fetch a List from firestore using a Model which works without the model but not with it.
In a StatefulWidget I got an empty List of Products. basically comes from the model like this
class Product {
int id;
String title, price, message;
Product(this.id, this.title, this.price, this.message);
}
The List of Products like this
import 'package:presentation_app/models/product.dart';
class _ProductsListState extends State<ProductsList> {
List<Product> products = [];
#override
Widget build(BuildContext context) {
return Scaffold(
...
then added a StreamBuilder with the firestore collection like this
StreamBuilder(
stream: Firestore.instance.collection('products').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (!snapshot.hasData) return CircularProgressIndicator();
return BuildListView(products);
}
),
and in the BuildListView class I've then called the List of Products and used it like so:
class BuildListView extends StatelessWidget {
final List<Product> products;
BuildListView(this.products);
in the listViewBuilder I then tried to access the data like so:
ListView.builder(
itemCount: products.length,
itemBuilder: (BuildContext context, int index) {
int id = products[index].id;
String title = products[index].title;
String message = products[index].message.toString();
String price = products[index].price.toString();
I get the CircularProgressIndicator but after it finishes loading nothing pops up.
using this approach it works, but of course not with the model
body: StreamBuilder(
stream: Firestore.instance.collection('products').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (!snapshot.hasData) return CircularProgressIndicator();
return BuildListView(products: snapshot.data.documents);
}
),
);
}
}
class BuildListView extends StatelessWidget {
final List<DocumentSnapshot> products;
BuildListView({this.products});
...
In this case the model is not needed, because I was not working with a business logic, so the model is obsolete, If using Bloc and a db like firebase the bloc using a model should look something like this.
Future<void> createData(docId) {
final Tracker createTracker = Tracker(
id: docId,
comment: _commentController.value,
exercise: _exerciseController.value,
number: _numberController.value,
repetition: _repetitionController.value,
sets: _setsController.value,
weight: _weightController.value
);
print(_commentController.value + _numberController.value.toString());
return trackerDb.createData(docId, createTracker);
}
and the db model for creating data something like this for example.
Future createData(String docId, Tracker tracker) async {
await db.document(docId).setData(convertTrackerToMap(tracker));
}

Flutter: showDialog: build function returned null

I have a StatefulWidget. Then when I click a button, it shows an alert dialog. When I implement:
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Hello"),
);
}
}
Everything works fine. But when I transfered the things inside the builder to a different StatefulWidget, then this error occurs:
A build function returned null.
I/flutter ( 3647): The offending widget is: Builder
I/flutter ( 3647): Build functions must never return null. To return an empty space that causes the building widget to
I/flutter ( 3647): fill available room, return "new Container()". To return an empty space that takes as little room as
I/flutter ( 3647): possible, return "new Container(width: 0.0, height: 0.0)".
Here is the code:
Here is the calling StatefulWidget:
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
LastVacDialog(
currentDose: currDose,
currentDate: currDate,
currentIndex: i,
setValue: changeDoseValueAndDate,
);
},
);
},
Here is the new StatefulWidget:
class LastVacDialog extends StatefulWidget {
LastVacDialog({
this.currentDose,
this.currentDate,
this.setValue,
this.currentIndex,
});
final int currentDose;
final DateTime currentDate;
final void Function(int, DateTime, int) setValue;
final currentIndex;
#override
LastVacDialogState createState() => new LastVacDialogState();
}
class LastVacDialogState extends State<LastVacDialog> {
int _dose;
DateTime _today;
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Last Dose"),
);
}
}
Is there something wrong with my code? I just omitted some variables for simplicity.
Add the Word Return in Front of - LastVacDialog
builder: (BuildContext context) {
return LastVacDialog(
...
As Error is Stating Build function must never return null. So return your LastVacDialog Widget by adding return in front of it.

type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<Map<String, dynamic>>'

I'm little bit stuck on some situation.
1) type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<Map<String, dynamic>>'
My code.
List<Map<String, dynamic>> arrayOfProductList = List<Map<String, dynamic>>();
Calling APIs
Future<Map<String, dynamic>> _callWebServiceForGetProductList() async{
String strURL = 'http:xxxxxx/productlist.php';
Map<String, String> headerDic = {"Authorization": "ff624bc580ab48a3910d4352dxxx712"};
Map<String, String> paramDic = {"page": "1", "search": "", "term_id": ""};
http.Response httpRes = await http.post(strURL, headers: headerDic, body: paramDic);
Map<String, dynamic> responseDic = json.decode(httpRes.body);
return responseDic;
}
FutureBuilder Widget and stuff
Expanded(
child: Container(
child: FutureBuilder(
future: _callWebServiceForGetProductList(),
builder: (BuildContext context, AsyncSnapshot snap) {
if(snap.hasData) {
Map<String, dynamic> dicOfData = snap.data;
arrayOfProductList = dicOfData["data"] as List<Map<String, dynamic>>;
print(arrayOfProductList);
_setupProductListView();
}
else if(snap.hasError) {
showDialog(context: context, builder:(_) => AlertDialog(
content: Text("Error ${snap.hasError.toString()}"),
title: Text("Error"),
));
}
return Center(child: CircularProgressIndicator());
}
),
),
)
I'm googling for last 2 hours but didn't get any result. Where i'm going wrong? Guide me.
The cast attempt on dicOfData["data"] as List<Map<String, dynamic>> seems to have caused the type mismatch error you received. If you can provide more details on the data the snapshot contains, this can help us identify why it causes the error.

How to properly feed data to StreamBuilder initialData parameter?

I am using a StreamBuilder widget to display some data. When the app is recently opened, I wanted to display some initial data from my json file and feed it into the initialData optional keyword parameter of StreamBuilder.
Here is how I feed it:
MyStorage m = new MyStorage(); // Using path_provider, I accessed the json file inside this class
int x;
#override
void initState(){
super.initState();
getData();
}
getData() async{
Map<String, dynamic> myMap = await m._getMap;
x = int.parse(myMap["total"]);
}
...
#override
Widget build(BuildContext context){
...
child: StreamBuilder(
stream: mystream, // coming from my BLoC class
initialData: x,
builder: (context, snapshot){
return new Text("${snapshot.data}");
}
...
The problem is it that the Text widget inside my StreamBuilder is displaying "null".
I tried to rewrite my code to this:
MyStorage m = new MyStorage();
Future<int> getData() async{
Map<String, dynamic> myMap = await m._getMap;
return int.parse(myMap["total"]);
}
...
#override
Widget build(BuildContext context){
...
child: StreamBuilder(
stream: mystream, // coming from my BLoC class
initialData: getData(),
builder: (context, snapshot){
return new Text("${snapshot.data}");
}
...
but it displayed on my Text widget as "Instance of Future:int"
I have no problem with my stream parameter in the StreamBuilder. It displays the correct value that I'm expecting from the BLoC class.
The only problem I had is the feeding of initialData from my json file.
What am I doing wrong? I would appreciate any kind of help. Thanks
[UPDATE]
After a long hours of thinking a solution, I gave up using initialData parameter since after I added int to StreamBuilder like this StreamBuilder<int>() it prompted me that it will only take a value of integer. I couldn't fed it with a type of Future or Stream so I decided not to use it. What I did was I nested a FutureBuilder inside the StreamBuilder through ConnectionState.
Here is what my code now:
MyStorage m = new MyStorage();
Future<int> getData() async{
Map<String, dynamic> myMap = await m._getMap;
return int.parse(myMap["total"]);
}
...
#override
Widget build(BuildContext context){
...
child: StreamBuilder<int>(
stream: mystream, // coming from my BLoC class
//initialData: getData(),
builder: (context, snapshot){
swith(snapshot.connectionState){
case ConnectionState.none:
return new FutureBuilder(
future: getData(),
builder: (context, snapshot){
return new Text('${snapshot.data}');
}
);
case ConnectionState.active:
case ConnectionState.waiting:
return new FutureBuilder(
future: getData(),
builder: (context, snapshot){
return new Text('${snapshot.data}');
}
);
case ConnectionState.done:
if (snapshot.hasData){
return new Text('${snapshot.data}');
}
return new FutureBuilder(
future: getData(),
builder: (context, snapshot){
return new Text('${snapshot.data}');
}
);
}
}
...
I know that this solution is very inefficient but I couldn't think of a better solution as of now.
initialData is supposed to be the data to show before your actual data is available.
place 0 as initialData.
StreamBuilder has an additional parameter stream which is responsible for fetching your stream data. here you can do stream: getData().asStream,
edit:
Also make sure to specify the type of data you are expecting in your StreamBuilder. StreamBuilder<int>()

Future Builder to make Sliver Lists

I am trying to generate a dynamic list of slivers from a GET request. But I am having trouble, it seems the response data is null. Here is my code:
import 'package:flutter/material.dart';
import 'boardSummary.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:flutter/foundation.dart';
class HomepageBody extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return HomepageBodyState();
}
}
class HomepageBodyState extends State <HomepageBody> {
#override
Widget build(BuildContext context) {
return new Expanded(
child: new Container(
color: new Color(0xFF736AB7),
child: new FutureBuilder <List<Post>>(
future: fetchPost(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) print(snapshot.error);
else
return jobscroll(context, snapshot);
//: Center(child: CircularProgressIndicator());
}
),
),
);
}
}
Future<List<Post>>fetchPost() async {
final response = await http.get('https://jsonplaceholder.typicode.com/posts?userId=1');
return compute(parsePosts, response.body);
}
List<Post> parsePosts(String responseBody){
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Post>((json)=>Post.fromJson(json)).toList();
}
class Post {
final String userId;
final String hashtag;
final String price;
final String description;
Post({this.userId, this.hashtag, this.price, this.description});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
hashtag: json['id'],
price: json['title'],
description: json['body'],
);
}
}
Widget jobscroll(BuildContext context, AsyncSnapshot snapshot) {
List data = snapshot.data;
return CustomScrollView(
scrollDirection: Axis.vertical,
shrinkWrap: false,
slivers: <Widget>[new SliverPadding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
sliver: new SliverList(
delegate: new SliverChildBuilderDelegate(
(context, index) => new BoardSummary(data[index]),
childCount: data.length,
),
),
),
],
);
}
BoardSummary is a stateless widget class that just takes creates a "card" using the properties on each "Post". It takes in a object of type "Post."
The console spit out a lot of errors but this was the last one that seemed meaningful it also appeared in my emulator:
I/flutter (18882): Another exception was thrown: NoSuchMethodError: The getter 'length' was called on null.
EDIT Here's also the first few lines from my slack trace:
E/flutter (13964): type 'int' is not a subtype of type 'String'
E/flutter (13964): #0 new Post.fromJson (file:///home/daniel/Desktop/testapp/lib/ui/homePageBody.dart:76:19)
E/flutter (13964): #1 parsePosts.<anonymous closure> (file:///home/daniel/Desktop/testapp/lib/ui/homePageBody.dart:60:40)
E/flutter (13964): #2 MappedListIterable.elementAt (dart:_internal/iterable.dart:414:29)
E/flutter (13964): #3 ListIterable.toList (dart:_internal/iterable.dart:219:19)
E/flutter (13964): #4 parsePosts (file:///home/daniel/Desktop/testapp/lib/ui/homePageBody.dart:60:56)
E/flutter (13964): #5 _IsolateConfiguration.apply (package:flutter/src/foundation/isolates.dart:88:16)
E/flutter (13964): #6 _spawn.<anonymous closure> (package:flutter/src/foundation/isolates.dart:96:30)
What should I do?
Edit casting this piece of code to string made it work, just have overflow to fix. If there is a better solution feel free to share!
return Post(
userId: json['userId'].toString(),
hashtag: json['id'].toString(),
price: json['title'].toString(),
description: json['body'].toString(),
);
FutureBuilder builds immediately even when the value is not yet available, because build() is sync and can't be delayed.
The FutureBuilder example shows how to check if the value is already available (default: ... and not snapshot.hasError):
new FutureBuilder<String>(
future: _calculation, // a Future<String> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return new Text('Press button to start');
case ConnectionState.waiting: return new Text('Awaiting result...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return new Text('Result: ${snapshot.data}');
}
},
)

Resources