How to display streambuilder and dropdown correctly in flutter - flutter

I'm a new flutter developer and a bit confused here, I want to display a dropdown menu and the streambuilder in my screen. Here's the code for the dropdown.
DropdownButton<String>(
value: selectedItem,
items: services,
.map((item) => DropdownMenuItem<String>(
value:item,
child: Text(item, style: TextStyle(fonSize: 24))
)).toList(),
onChanged: (item) => setState(() => selectedItem - item),
)
Here's the scaffold with streambuilder
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: StreamBuilder<List<SportBooking>>(
stream: readBooking(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
} else if (snapshot.hasData) {
final booking = snapshot.data!;
return ListView(
children: booking.map(buildBooking).toList(),
);
} else {
return const Center(child: CircularProgressIndicator());
}
}),
);
}
Problem is I have tried with a Column widget here like below with a simple text widget, but it throws an Assertion error
body: Column(
children: [
Text('data'),
StreamBuilder<List<SportBooking>>(
stream: readBooking(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
} else if (snapshot.hasData) {
final booking = snapshot.data!;
return ListView(
children: booking.map(buildBooking).toList(),
);
} else {
return const Center(child: CircularProgressIndicator());
}
}),
],
),
How do I display both the dropdown and the streambuilder in the scaffold?

You can wrap it inside a Column Widget and now you can add as many Widgets as you want.
You can either wrap ListView with Column Widget.
Make sure to Wrap ListView with Expanded Widget otherwise it will give exception.
return Scaffold(
backgroundColor: Colors.black,
body: Column(
children: [
DropdownButton<String>(
value: selectedItem,
items: services,
.map((item) => DropdownMenuItem<String>(
value:item,
child: Text(item, style: TextStyle(fonSize: 24))
)).toList(),
onChanged: (item) => setState(() => selectedItem - item),
),
StreamBuilder<List<SportBooking>>(
stream: readBooking(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
} else if (snapshot.hasData) {
final booking = snapshot.data!;
return ListView(
children: booking.map(buildBooking).toList(),
);
} else {
return const Center(child: CircularProgressIndicator());
}
}),
],
),
);

In the ListView add the following properties
ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,

Related

Text position problems with UserAccountsDrawerHeader in the Drawer in flutter

I am using a userAccountsDrawerHeader in the Drawer with a photo, username and email but I get a location or text positioning problem.
The text it shows is from the database using a future builder and it gives me a problem.
But when I test the properties of userAccountsDrawerHeader(accountName,accountEmail) using a Text widget with no data from the database, the text is located correctly, no problem there.
How can I solve that?
Your help would be greatly appreciated.
code and image::
UserAccountsDrawerHeader(
accountName:NombreCabeceraDrawer(),
accountEmail:CorreoCabeceraDrawer(),
),
class CorreoCabeceraDrawerextends StatefulWidget{return correocabeceradrawer ();}
class correocabeceradrawer extends State<CorreoCabeceraDrawer>{
Widget CorreoCabeceraDrawer(){
return Container(
child: Text(correousuario), ); }
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: futureServices,
builder:(context, AsyncSnapshot snapshot) {
List listainfocabeceradrawer = snapshot.data;
if(snapshot.hasData ) {
return Center(
child: Container(
child: ListView.builder(
itemCount: listainfocabeceradrawer.length,
itemBuilder: (context,i){
correousuario = listainfocabeceradrawer[i].CorreoUsuario;
return Container(
child: CorreoCabeceraDrawer(),
); }), ),);
}else if(snapshot.hasError){
return Text("${snapshot.error}");
} return Center(
child: CircularProgressIndicator()
);
}
),
);
throw UnimplementedError();
}}
enter image description here
enter image description here
enter image description here
if we think simple is this code work with your code?
UserAccountsDrawerHeader(
accountName:NombreCabeceraDrawer(),
accountEmail:CorreoCabeceraDrawer(),
),
class CorreoCabeceraDrawerextends StatefulWidget{return correocabeceradrawer ();}
class correocabeceradrawer extends State<CorreoCabeceraDrawer>{
Widget CorreoCabeceraDrawer(){
return Container(margin:EdgeInsets.only(top:40),
child: Text(correousuario), ); }
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: futureServices,
builder:(context, AsyncSnapshot snapshot) {
List listainfocabeceradrawer = snapshot.data;
if(snapshot.hasData ) {
return Center(
child: Container(
child: ListView.builder(
itemCount: listainfocabeceradrawer.length,
itemBuilder: (context,i){
correousuario = listainfocabeceradrawer[i].CorreoUsuario;
return Container(
child: CorreoCabeceraDrawer(),
); }), ),);
}else if(snapshot.hasError){
return Text("${snapshot.error}");
} return Center(
child: CircularProgressIndicator()
);
}
),
);
throw UnimplementedError();
}}

how to return a form widget in futureBuild in flutter

I have this code as am trying to code something to update data in firestore.
#override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(mid.toString()),
),
body: FutureBuilder<Member?>(
future: readMember(mid),
builder: (context, snapshot) {
if (snapshot.hasData) {
final member = snapshot.data;
/// return a form
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
if snapshot hasData I want to return a form like this
Card(
child: Row(
children: <Widget>[
TextField(
controller: controllerName,
decoration: decoration('name'),
),
const SizedBox(height: 24),
TextField(
controller: controllerAge,
decoration: decoration('Age'),
keyboardType: TextInputType.number,
),
const SizedBox(height: 24),
ElevatedButton(
child: const Text('Create'),
onPressed: () {},
),
],
));
All my attempt yield no success please I need help.
Check others state like error or if the data is null or not
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Got Error");
}
if (snapshot.data == null) {
return Text("No data");
}
if (snapshot.hasData) {
final member = snapshot.data;
return Card( ///here form
child: Row(
children: <Widget>[],
));
} else {
return const Center(child: CircularProgressIndicator());
}
},
And provide width on TextFiled to fix overflow, TextFiled and row are trying to get infinite with.
just wrap with Expanded
Expanded(child: TextField(...)),
You can find more about unbound height& width

Flutter how to user setState()

One of my routes shows current data which is stored in firestore database. I am calling the function to retrieve the data from firestore in the initState method. The page will show all the data which are retrieved from firestore. It works fine i.e, when the user navigates to this page (quotesPage) it shows the data. But while navigating, for some fraction of seconds it shows error that the local variable which stores the retrieved data is null. It happens for only that fraction of seconds after which it receives the data and shows the data. So when the user navigates to that page, I want to show a progress indicator untill it receive the data. here is my code,
Map<String, dynamic> userInfo = {};
Future<void> getQoutes() async {
var data = await FirebaseFirestore.instance.collection('user').doc(auth.currentUser!.uid).get();
setState(() {
userInfo = data.data() as Map<String, dynamic>;
});
}
#override
void initState() {
getQoutes();
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Some Quotes',
),
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
)
],
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await popUpForm();
},
),
);
I am calling the function getQuotes() from initState() which will store the data to Map variable userInfo. So how to show a progress indicator untill the variable 'userInfo' gets data ?
Can anyone help ?
This is the updated code
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Some Quotes',
),
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FutureBuilder<void>(
future: getQoutes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
if(snapshot.hasError) {
return Text('Error : ${snapshot.error}');
}
return Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
);
default:
return const CircularProgressIndicator();
}
},
)
],
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await popUpForm();
},
),
);
You should try with Future builder or stream builder and here is the example with Future builder
FutureBuilder<String>(
future: getQoutes(), // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return CircularProgressIndicator();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
);
}
},
)
For more read this article
another approach that might be worth looking at is using a addPostFrameCallback method called from your initState in which you can await the necessary condition and take appropriate action and trigger a setState.

How to extract snapshot data and save as a global variable in flutter

I don't know whether this is a dumb question or not. Pardon me if it is.
I created a streamBuilder and now I want to extract it's AsyncSnapshot and save as a global variable. Currently I only have access to that snapshot is, inside the streamBuilder widget. But I want that snapshot data to update some widgets outside that streamBuilder widget. I have added a comment to below code:
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: _crudStorage.all(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.active:
case ConnectionState.waiting:
if (snapshot.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
final tasksList = snapshot.data as List<Task>; //I want to extract this
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
tasksList.isEmpty
? const RiveBird()
: Expanded(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: tasksList.length + 1,
itemBuilder: (context, index) {
if (index == tasksList.length) {
return const SizedBox(
height: 85.0,
width: double.infinity,
);
}
final task = tasksList[index];
return Dismissible();
},
),
),
],
);
default:
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}

How to loop the PopupMenuItem in PopupMenuButton in Flutter?

I want to display values from my API in a PopupMenuItem in PopupMenuButton. I manage to display it but I want it to be dynamic. Currently, I hard-coded the index of each item because it seems that I cannot do looping inside PopupMenuButton.
`Widget _simplePopup4() => PopupMenuButton<int>(
child: Icon(Icons.arrow_drop_down, color: Colors.orangeAccent),
offset: Offset(0, 100),
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[1].title); //index 1
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[2].title); //index 2
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[3].title); //index 3
}
}
return CircularProgressIndicator();
})),
),
],
);`
//First attempt which gives error: RenderShrinkWrappingViewport does not support returning intrinsic dimensions.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.setTitle.length,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data.setTitle[index].title);
});
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
//Second attempt which gives error: Another exception was thrown: A RenderFlex overflowed by 85 pixels on the bottom.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final listTitle = <Widget>[];
for (var i = 0;
i < snapshot.data.setTitle.length;
i++) {
listTitle.add(SingleChildScrollView(
scrollDirection: Axis.vertical,
child: InkWell(
child:
Text(snapshot.data.setTitle[i].title),
)));
}
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Column(children: listTitle);
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
From the screenshot, only one item are clearly displayed which is "MR" while the other item (before item "MR") are displayed in half. Meanwhile, the rest of the item (after item "MR") being replaced with error message.
The screenshot of the second attempt error
The cause of the RenderFlex error is because the child Widget expands beyond the parent Widget. What you can do here is fetch the List of PopupMenu items prior to rendering the PopupMenuButton. With this approach, the List items is ready prior to clicking the PopupMenuButton.