How to pass parameters into the future instance of Futurebuilder? - flutter

I am trying to pass parameters into the future instance but I encounter the "error instance member can't be accessed in an initializer" for _getData at future: _getData even though I have initialized it in initState.
class _DisplayListState extends State<DisplayList> {
late Future<QuerySnapshot> _getData;
Future<QuerySnapshot> getData(String value)async{
return await FirebaseFirestore.instance
.collection('x')
.where('a', isEqualTo: value)
.get();
}
#override
void initState(){
super.initState();
_getData = getData(widget.value);
}
Widget displayList = FutureBuilder(
future: _getData,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return Container();
},
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: displayList,
);
}
}
I remove the the parameters for getData thinking that I should simplify the problem first and tried future: getData() but I encoutered the same error.
The only way that I don't encounter the error is if I do this - future: FirebaseFirestore.instance.collection('x').where('a', isEqualTo: 'something').get() for FutureBuilder. But this means I cannot pass parameters and I would prefer to use a function/variable for this over the long line of code.
future: FirebaseFirestore.instance.collection('x').where('a', isEqualTo: value).get() where value=widget.value is initialized in initState will give me the same error.

You can use a method for displayList instead of a variable.
You can change this:
Widget displayList = FutureBuilder(
future: _getData,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return Container();
},
);
to this:
Widget getDisplayList() => FutureBuilder(
future: _getData,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return Container();
},
);
And in the Scaffold's body, use the method like this:
body: getDisplayList()

Related

Flutter - Code in StreamBuilder doesn't get executed

I am trying to debug the following method, somehow the none of the breakpoints get hit. The one in the catch block also doesn't get hit. I fail to understand what is happening.
_getWorkout(workoutId) async {
try {
StreamBuilder<QuerySnapshot>(
stream: await FirebaseFirestore.instance
.collection("workouts")
.doc(workoutId)
.collection("exercises")
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
//doesn't go here---
print('SNAPSHOT DATA = ' + snapshot.data.toString());
if (!snapshot.hasData) {
//doesn't go here---
return const Text("There are no exercises");
}
//doesn't go here---
return DataTable( ...
],
rows: _getExercises(snapshot),
);
});
} on Exception catch (_, e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.toString()),
duration: Duration(milliseconds: 1000),
),
);
}
}
StreamBuilder is a Widget that can convert a stream of user defined objects, to widgets.
You Should include inside Widget build as a widget
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child:StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("workouts")
.doc(workoutId)
.collection("exercises")
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
//doesn't go here---
print('SNAPSHOT DATA = ' + snapshot.data.toString());
if (!snapshot.hasData) {
//doesn't go here---
return const Text("There are no exercises");
}
//doesn't go here---
return DataTable();
}),
),
);
}
You need to return the StreamBuilder from _getWorkout, which you aren't. You are effectively returning null, which will cause flutter to not execute the StreamBuilder at all.
Always setting return types on your methods will help in avoiding oversights like that one.

The argument type 'User?' can't be assigned to the parameter type Future<Object?>?'

Hey there I am new to flutter and i am currently working on firebase with flutter I am getting the following error , The argument type 'User?' can't be assigned to the parameter type 'Future<Object?>?' ,The error occurs in the futurebuilder in future argument ,
class messages extends StatelessWidget {
const messages({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
builder: (ctx, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final chatdoc = snapshot.data!.docs;
final userdata = FirebaseAuth.instance.currentUser;
return FutureBuilder(
future: userdata,
builder: (ctx, Snapshot) => ListView.builder(
reverse: true,
itemBuilder: (ctx, index) => messagebubble(
chatdoc[index]['Text'], chatdoc[index]['userId']),
itemCount: snapshot.data!.docs.length,
));
},
stream: FirebaseFirestore.instance
.collection('chats')
.orderBy('createdAt', descending: true)
.snapshots(),
);
}
}
The FirebaseAuth.instance.currentUser returns a User? not a Future. Thus, you can access it without the FutureBuilder, just return the messageBubble widget :
return messageBubble(
chatdoc[index]['Text'],
userData?.uid)

Handling multiple futures in Build

This is my build:
#override
Widget build(BuildContext context) {
return FutureBuilder<Position>(
future: _init,
builder: (context, snapshot) {
...
final Position position = snapshot.data!;
return FlutterMap(
...
layers: [
...
MarkerLayerOptions(
markers: markers, //this is the future list
),
],
);
});
}
Now, markers is a Future and I build it with this methods:
late Future<List<Marker>> markers;
#override
void initState() {
...
markers = getMarkers();
}
Future<List<Marker>> getMarkers() async {
List<Marker> markerTemp = [];
for (var friend in friendsList) {
DocumentSnapshot document = await locationRef.doc(friend).get();
if (document.exists)
markerTemp.add(Marker(...));
}
return markerTemp;
}
So when I run my application I get an error saying that markers is not initialized. How can I have my list ready when called in the build method?
I tried things like nested FutureBuilder or using Future.wait([item1,item2]) but since I'm newbie to this language I'm having troubles implementing it the right way probably
Try using FutureBuilder in some way similar to this:
return FutureBuilder<List<Marker>>(
future: markers,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
if (snapshot.hasError)
return Text("${snapshot.error}");
// You can access the list here, use this newly created list
List<Markers> markerList = snapshot.data as List<Marker>;
return FutureBuilder<Position>(...)
});
I believe FutureBuilder will solve your problem. try this:
#override
Widget build(BuildContext context) {
return FutureBuilder(
initialData: null,
future: Future.wait(getMarkers()),
builder: (context, snapshot) {
...
},
);
}
it's that simple. if something goes wrong just make sure your getMarkers() actually return a list of Futures and you'll be alright

Accessing a variable from a Factory

I'm new to Flutter and Dart.
I have a Stateful class with the _user variable.
I'd like to use this variable in a query I'm making to Firestore.
Neither "this" nor "_user" are available inside _usersStream (I believe this is a factory).
How can I access _user?
class _UserTermsState extends State<UserTerms> {
late User _user;
bool _isSigningOut = false;
final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance
.collection('somecol')
.where('uid', isEqualTo: this._user.uid)
.orderBy('createdAt', descending: true)
.snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return Scaffold( and so on...
You can't access instance member '_user' in an initializer.
Try this:
class _UserTermsState extends State<UserTerms> {
late User _user;
bool _isSigningOut = false;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('somecol')
// make sure _user is defined before build is called.
.where('uid', isEqualTo: _user.uid)
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

Is there any way to use FutureBuilder(future) inside the initState on this situation?

Below FutureBuilder using on the ListView. findRequirement base on item of ListView. This FutureBuilder on inside Build method. But office document said "The future must have been obtained earlier, e.g. during State.initState, State.didUpdateConfig, or State.didChangeDependencies."
Problem is every time scrolling calling to the findRequirement api and Now listView is slow/lag.
ListView.builder(
...
itemCount:value.length,
itemBuilder: (BuildContext context, int index) {
...
FutureBuilder<List<RequirementModel>>(
future: db.findRequirement(
userId, value[index].subCategoryId),
builder: (context, snapshot) {
if (snapshot.hasData) {
//widget
} else
return Container();
})
you can do it like this:
// inside state class
class _MyAppState extends State<MyApp> {
Future _getRequirments;
#override
void initState() {
super.initState();
_getRequirments = db.findRequirement(
userId, value[index].subCategoryId);
}
// change your FutureBuilder to this
FutureBuilder<List<RequirementModel>>(
future: _getRequirments,
builder: (context, snapshot) {
if (snapshot.hasData) {
//widget
} else
return Container();
this way it won't keep on calling your method again and again because you only get the future once which is inside initState() and FutureBuilder future: has that state and doesn't call the db.findRequirement(userId, value[index].subCategoryId);
for each build.