In my Flutter app, there is a custom drawer that is accessed through a button. In this drawer, there is a widget (InfoCard) that displays some of the user's information. However, in this widget, there is a FutureBuilder, so every time the drawer is displayed it shows a CircularProgressIndicator until it gets all the pieces of information from the Firestore server. My question is: can I avoid the FutureBuilder so that, when the app is fully loaded, it gets all the data needed for the widget?
Another problem related to this is that I need to wait until the Firestore database is loaded before returning the InfoCard widget (I don't know why because all the data is already loaded when the app il launched). So the thing is: I have to avoid calling the database or I call it somehow before building the widget so that I don't need to use the FutureBuilder.
Here is the widget drawer:
class SideDrawer extends ConsumerStatefulWidget {
SideDrawer({
super.key,
required this.setIndex,
});
Function(int index) setIndex;
@override
ConsumerState<SideDrawer> createState() => _SideDrawerState();
}
class _SideDrawerState extends ConsumerState<SideDrawer> {
@override
Widget build(BuildContext context) {
final userP = ref.watch(userStateNotifier);
return Scaffold(
body: Container(
width: MediaQuery.sizeOf(context).width * 3 / 4,
height: double.infinity,
color: Theme.of(context).colorScheme.secondary,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
FutureBuilder<Widget>(
future: Datas().infocard(userP),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Text('Errore: ${snapshot.error}');
}
return snapshot.data!;
} else {
return CircularProgressIndicator();
}
},
),
ColDrawer(setIndex: (index) {
widget.setIndex(index);
},),
],
),
),
),
);
}
}
the infoCard method:
Future<Widget> infocard(AppUser userP) async {
var db = FirebaseFirestore.instance;
QuerySnapshot qn =
await db.collection('users').where('id', isEqualTo: userP.id).get();
return InfoCard(
username: userP.username,
age: userP.age,
weight: userP.measurements
.firstWhere((measure) => measure.title == 'Weight')
.datas
.last
.values
.first,
);
}
If I don't wait for the database to be loaded or if I substitute the FutureBuilder with the InfoCard widget, it throws this error ONLY for the userP.measurements... line:
StateError (Bad state: No element)
Lastly, why do I need to call the database even though all the data is already loaded when the app is launched on the Firestore database?
First the code is incomplete, you did not include sqflite purpose, definition and usage. And regarding your prblm of Future Builder loading every time is because you called infoCard() inside build fn. So everytime widget build it calls the fn and execute the query everytime, so what you can do is either:
Declare the Future outside build fn and define it inside the init fn. like this
now pass this infoWidget to FutureBuilder.
As the future is outside the scope of build fn, so it will not be called again and again.