initialData in FutureBuilder - Flutter - flutter

What is the purpose of initialData in FutureBuilder? The documentation says:-
The data that will be used to create the snapshots provided until a non-null future has completed
Does that mean the placeholder data that gets shown to the user when the data is awaiting?
If that was the case then why my FutureBuilder always shows the loading state and not the initialData
My FutureBuilder()
FutureBuilder(
future: _getPosts(),
initialData: _getOfflineData(),
builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
if (!snapshot.hasData ||
snapshot.data == null ||
snapshot.data.isEmpty ||
snapshot.hasError) {
// Still loading || load null data UI
return ListView(
scrollDirection: Axis.horizontal,
children: [for (var i = 0; i < 5; i++) HorizontalPostShimmer()],
);
}
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final titleExists = doesTitleExist(snapshot.data[index].title);
final title = titleExists
? snapshot.data[index].title
: _translate.translate('unknownTitle');
final imgExists =
doesImageExist(snapshot.data[index].featuredMedia);
final img = imgExists ? snapshot.data[index].featuredMedia : null;
return Container(
width: 300.0,
child: Card(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SAPost(data: snapshot.data[index]),
settings: RouteSettings(name: 'SAPost')));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
img == null
? Container(
child: SaviorImageShimmer(
height: 100,
width: 100,
))
: CachedNetworkImage(
imageUrl: img,
placeholder: (context, url) =>
SaviorImageShimmer(),
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'$title',
style: TextStyle(
fontSize: _isMobile ? 18.0 : 22.0,
),
),
),
)
],
),
),
),
),
);
},
);
},
)
And my _getPosts()
Future<List<Post>> _getPosts() {
final Map<String, dynamic> args = {
'IDs': [1983],
'perPage': 10,
'pageNum': 1,
'from': 'horizontalSlider'
};
final Future<List<Post>> posts =
context.read(fetchSpecificPostsByCategoriesProvider(args).future);
return posts;
}
And my _getOfflineData()
List<Post> _getOfflineData() {
final List<Post> cachedPosts =
Hive.box('posts').get('horizontalSlider', defaultValue: <Post>[]);
return cachedPosts;
}
Am I doing something that my FutureBuilder() always returns the ListView about loading?
Or is initialData used in another way

In case if you would like to provide some data while other data is being fetched, you can use the initialData property to provide that data.

The documentation is as clear as it can be. My guess is your initial data is empty. Try removing the following condition from the build function and check again: snapshot.data.isEmpty.

Related

Flutter - Nested Streambuilder and FutureBuilder

I have categories collection with category name and id fields. In foods collection, the category is a reference field. I need to display like this:click to see expected output
where title is coming from categories collection and foods are coming from foods collection.
I tried using nested streambuilder: streambuilder 1: fetch categories in a listview streambuilder 2: fetch foods in a list. Inside streambuilder 2, i have used a futurebuilder to decode the category data. If category name in food and category name from streambuilder 1 is same, the food will be displayed under that category.+
class RestaurantDetails extends StatefulWidget {
final String id;
RestaurantDetails({required this.id, super.key});
#override
State<RestaurantDetails> createState() => _RestaurantDetailsState();
}
class _RestaurantDetailsState extends State<RestaurantDetails> {
List<FoodCategory> categories = [];
List<Food> foods = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: StreamBuilder(
stream: getCategories(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
categories = snapshot.data!.docs
.map((item) => FoodCategory.fromMap(item))
.toList();
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: categories.length,
itemBuilder: ((context, cateindex) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
child: Container(
height: 30,
width: MediaQuery.of(context).size.width * 1,
color: Colors.white,
child: Text(
categories[cateindex].name.toString(),
style: Theme.of(context)
.textTheme
.headline4!
.copyWith(fontSize: 20.0),
),
),
),
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('foods')
.doc(widget.id)
.collection('all')
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
foods = snapshot.data!.docs
.map((item) => Food.fromMap(item))
.toList();
return ListView.builder(
itemCount: foods.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: ((context, foodindex) {
var catepath = foods[foodindex].cid!.path;
String cateDocumentName = catepath
.substring(11, catepath.length);
return Column(
children: [
FutureBuilder(
future: FirebaseFirestore.instance
.collection('categories')
.doc(cateDocumentName)
.get(),
builder: ((context,
AsyncSnapshot<
DocumentSnapshot>
snapshot) {
if (snapshot.hasData) {
Map<String, dynamic> data =
snapshot.data!.data()
as Map<String,
dynamic>;
if (data['name'] ==
categories[cateindex]
.name) {
return Padding(
padding: const EdgeInsets
.symmetric(
vertical: 10,
horizontal: 10),
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius
.circular(
20),
color:
Colors.white),
height: 100,
width: MediaQuery.of(
context)
.size
.width *
1,
child: Row(
children: [
Image.network(
foods[foodindex]
.cover
.toString(),
height: 100,
width: 100,
errorBuilder:
((context,
error,
stackTrace) {
return Image
.asset(
'assets/images/food1.jpg',
height: 100,
width: 100,
);
}),
),
UIHelper
.horizontalSpaceMedium(),
Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
UIHelper
.verticalSpaceSmall(),
Text(
foods[foodindex]
.name
.toString(),
style: Theme.of(
context)
.textTheme
.bodyText1,
),
UIHelper
.verticalSpaceSmall(),
Text(
'₹${foods[foodindex].price}',
style: Theme.of(
context)
.textTheme
.bodyText1!
.copyWith(
fontSize:
14.0),
),
UIHelper
.verticalSpaceMedium(),
],
)
],
)),
);
} else {
return const SizedBox();
}
} else {
return const CircularProgressIndicator
.adaptive();
}
}))
],
);
}));
} else {
return const CircularProgressIndicator
.adaptive();
}
},
)
],
);
}));
} else {
return const CircularProgressIndicator.adaptive();
}
}),
),
);
}
getCategories() {
return FirebaseFirestore.instance
.collection('categories')
.where('uid', isEqualTo: widget.id)
.snapshots();
}
}
categories data
click to see categories
food data
click to see food data
I get the output.see my output here but when data is large (i.e large number of foods inside a category) the app hangs. Is there anyother way we can achieve this? the data should load seamlessly regardless of data size.

error: the method [] was called on null. tried calling ()[merchantId]

I am trying to get the list of products by shop from the firestore. However, getting the following error:
I/flutter ( 7294): NoSuchMethodError: The method '[]' was called on null.
I/flutter ( 7294): Receiver: null
I/flutter ( 7294): Tried calling: []("merchantId")
I am using FutureBuilder to get shop data and StreamBuilderWrapper to get the products:
class Shop extends StatelessWidget {
Widget getProductsAndImages(BuildContext context, String outletID) {
return StreamBuilderWrapper(
shrinkWrap: true,
stream: productsRef.doc(outletID).collection('products')
.orderBy('dateTime', descending: true).snapshots(),
itemBuilder: (_, DocumentSnapshot snapshot) {
ProductModel posts = ProductModel.fromJson(snapshot.data() as Map<String, dynamic>);
return posts.productId != null
? Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child:
ProductItem(productName: posts.proName!, productPrice: posts.price!,
count: posts.count!, images: posts.images!,
category: posts.category!, productDescription: posts.productDescription!,
productId: posts.productId!),
)
: Container(
child: Center(
child: Text('No products'),
),
);
});
}
#override
Widget build(BuildContext context) {
final outletData = Provider.of<OutletData>(context);
final outletId = ModalRoute.of(context)!.settings.arguments as String;
print('outlet id--->>> $outletId');
return Scaffold(
appBar: appHeader(context),
backgroundColor: color2,
extendBodyBehindAppBar: true,
body: Stack(
children: <Widget>[
Container(
decoration: const BoxDecoration(
image: DecorationImage(image: AssetImage("assets/images/background.jpg"),
fit: BoxFit.cover,),
),
),
FutureBuilder(
future: outletData.getOutletById(outletId),
builder: (ctx, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.data == null) {
return
Center(
child: Text("Юм олдсонгүй"),
);
}
return snapshot.data.length == 0
? Center(
child: Text("Бүтээгдэхүүн байхгүй"),
)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ShopHeader(
outletName: snapshot.data()[outletName],
outletImage: snapshot.data()[outletImg],
location: snapshot.data()[outletLocation],
description: snapshot.data()[outletIntro],
phoneNum: snapshot.data()[outletPhone],
merchantId: snapshot.data()[merchantId],
outletId: outletId,
outletRating: snapshot.data()['outletRating'],
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
"Recommended products:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.black54,
),
),
),
ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (ctx, index) {
return
getProductsAndImages(context, outletId);
},
),
],
),
);
},
)
]));
}
}
my getOutletById method is in the provider class:
Future<Map<String, dynamic>> getOutletById(String outId) async {
var outletsById;
final Map<String, dynamic> idOutlet = {};
_outletMerchant.clear();
try {
outletsById =
await firestore.collection(outletsCollection).doc(outId).get();
var outletResponse = outletsById.data();
var outletMerchantData = await firestore
.collection(allUserCollection)
.doc(outletResponse[merchantId])
.get();
var merchantResponse = outletMerchantData.data();
_outletMerchant.addAll(merchantResponse!);
print(_outletMerchant);
idOutlet.addAll(outletResponse);
print(merchantResponse[merchantId]);
} catch (err) {
print(err);
}
return idOutlet;
}
print() method prints nothing here my firestore database is as follows:
You can see that there is the merchantId for the outlets collection. where is the error here?

FutureBuilder gets 31 Elements from Future, but its snapshot has 129 Elements

I have a Future<List> function that fetches data from an API and it returns 31 list elements for the month july.
However, when I plug this Future function into a futurebuilder, the list length is 129 elements. Code is below. What could happen here that doubles the elements?
My Future code:
Future<List<DataDay>> getMonthlyDataHighlightDays() async {
String url = "myUrl";
var response = await http.get(Uri.parse(url));
Map<String, dynamic> DataResponse = jsonDecode(response.body) as Map<String, dynamic>;
List<dynamic> monthlyList = DataResponse["day_predictions"];
List<DataDay> monthlyListFixed = [];
monthlyList.forEach((dayElement) {
DataDay dayData = DataDay.fromJson(dayElement);
dayData.hour_predictions.forEach((hourElement) {
if (hourElement.rainbow || hourElement.pattern.toLowerCase().contains("rain")) {
monthlyListFixed.add(DataDay.fromJson(dayElement));
}
});
});
log("MonthlyListLenght: " + monthlyList.length.toString(), name: "getMonthlyDataHighlightDays()");
return monthlyListFixed;
}
This is my Futurebuilder:
Future<List<DataDay>> monthData = getMonthlyDataHighlightDays();
Display(
title: "Highlights this Month",
children: [
FutureBuilder<List<DataDay>>(
future: monthData, // a previously-obtained Future<String> or null
builder: (BuildContext context, AsyncSnapshot<List<DataDay>> snapshot) {
Widget child;
log("Snapshotlength: " + snapshot.data!.length.toString(), name: "FutureBuilder<List<DataDay>>");
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
List<DataDay> monthlyList = snapshot.data!;
log(monthlyList.length.toString(),name: "DataPage");
child = ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: monthlyList.length,
itemBuilder: (BuildContext context, int dayHourIndex) {
DataDay dayData = monthlyList[dayHourIndex];
DateTime date = DateTime(dayData.year, dayData.month, dayData.day);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(printDateTimeToStringWithoutYear(date, settings)),
SizedBox(
height: 10,
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: dayData.hour_predictions.length,
itemBuilder: (BuildContext context, int hourIndex) {
return getDataIcon(dayData.hour_predictions[hourIndex].pattern, width: 10);
},
),
),
],
);
},
);
} else if (snapshot.hasError) {
child = Row(
children: [
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Text(snapshot.error.toString())
],
);
} else {
child = Row(children: [
SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(),
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
]);
}
return Center(
child: child,
);
})
],
)

Flutter FutureBuilder duplicates items inside a GridView

It seems that the GridView.builder inside FutureBuilder duplicates each element a number of times equal to the list length.
Here is the code:
InformationScreen:
List<Reference> documentReference = [];
Widget showSavedDocument() => FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.items.length,
itemBuilder: (context, index) {
final photo = snapshot.data!.items[index].getDownloadURL();
final photoName = snapshot.data!.items[index].name;
final metaData = snapshot.data!.items[index].getMetadata();
documentReference = snapshot.data!.items;
return Column(
children: [
FutureBuilder(
future: metaData,
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if(snapshot.hasData) {
photoType = snapshot.data!.contentType!;
}
return Container();
},
),
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data;
List<Document> documents = [];
for (int i = 0; i < documentReference.length; i++) {
Document document = Document(user!.uid, image!, photoName, photoType);
documents.add(document);
}
return DocumentGrid(documents: documents,); // <------------------------------
}
return Container();
},
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if (snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if (snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
);
DocumentGrid
import 'package:flutter/material.dart';
import 'package:app_test/constant/color.dart';
import '../constant/text.dart';
import '../model/document.dart';
class DocumentGrid extends StatelessWidget {
final List<Document> documents;
const DocumentGrid({Key? key, required this.documents}) : super(key: key);
#override
Widget build(BuildContext context) {
return buildGridView();
}
//****************************************************************************
// Create GridView
//****************************************************************************
Widget buildGridView() => GridView.builder(
itemCount: documents.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
),
itemBuilder: (context, index) {
final photo = documents[index].photo;
final title = documents[index].title;
final type = documents[index].type;
return buildGridViewItem(photo, title, type);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
//****************************************************************************
// Create GridView item
//****************************************************************************
Widget buildGridViewItem(String photo, String? title, String type) => Container(
width: 50,
height: 50,
color: phoneButtonColor,
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
buildNetworkImage(photo, type),
buildBlackOpacity(title),
],
),
);
//****************************************************************************
// Create Network image
//****************************************************************************
Widget buildNetworkImage(String photo, String type) => Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
photo,
errorBuilder: (context, exception, stackTrace) {
return type == "pdf" || type != "jpg"
|| type != "jpeg" || type != "png"
? Image.asset(
fit: BoxFit.cover,
width: 100,
height: 100,
"assets/images/pdf.png",
)
: Container(
color: grey,
width: 100,
height: 100,
child: const Center(
child: Text(
errorLoadImage,
textAlign: TextAlign.center,
),
),
);
},
);
//****************************************************************************
// Create Black opacity
//****************************************************************************
Widget buildBlackOpacity(String? title) => Container(
color: Colors.black54,
padding: const EdgeInsets.symmetric(
vertical: 30,
horizontal: 20,
),
child: Column(
children: [
Expanded(
child: Center(
child: Text(
title!,
style: const TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
),
],
),
);
}
How can I solve that, thanks in advance
Problem solved
Replacing ListView by GridView
Widget showSavedDocument() => FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return GridView.builder(
itemCount: snapshot.data!.items.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
),
itemBuilder: (context, index) {
final instructorDocument = snapshot.data!.items;
final photo = instructorDocument[index].getDownloadURL();
final photoName = instructorDocument[index].name;
final metaData = instructorDocument[index].getMetadata();
return Column(
children: [
FutureBuilder(
future: metaData,
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if (snapshot.hasData) {
photoType = snapshot.data!.contentType!;
}
return Container();
},
),
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data!;
return Expanded(
child: buildGridViewItem(image, photoName, photoType),
);
}
return Container();
},
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if(snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if(snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
);

UnimplementedError error in FutureBuilder while displaying inserted data from database

I'm trying to create a Futurebuilder function to call and display all data that inserted in database unfortunately I got this error 'UnimplementedError' and im pretty stock on this any suggestion will be appreciated.
Here in my full code for implementation to display data in been trying to fix my error 'UnimplementedError' in which I'm trying to do is to display inserted in list view not in web view any suggestion will be appreciated.
body: Center(
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<ContactsDao>(
future: _calltheStream(),
builder: (BuildContext context,
AsyncSnapshot<ContactsDao> snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<List<ContactObject>>(
stream: snapshot.data.findallContactsById(),
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState ==
ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
if(widget.Contactlist.length != snapshot.data.length){
widget.Contactlist = snapshot.data;
}
if(snapshot.data.length == 0){
return Center(
child: Text('No Data Found'),
);
}
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int index) {
return Card(
child: ListTile(
leading: Checkbox(
value: widget.Contactlist[index].isSelect,
onChanged: (bool value) {
setState(() {
widget.Contactlist[index].isSelect = value;
});
},
),
trailing: GestureDetector(
onTap: () {
_selectedDetele(snapshot.data[index].id);
},
child: Icon(Icons.delete),
),
title: Text('${snapshot.data[index].task}',maxLines: 1,),
subtitle: Text('${snapshot.data[index].time}',style: TextStyle(fontSize: 10),),
));
}),
);
}
}); //DATA
} //DATA
}), // DATA
], // DATA
), // DATA
),//DATA
),
Future<ContactsDao> _calltheStream() async { //GET ALL DATA HERE
ContactDatabase contactDatabase = await widget.database;
widget._contactsdao = contactDatabase.contactsDao;
return contactDatabase.contactsDao;
}