I am trying to create a home page that has 3 tabs: Friends, Groups and Events.
After finally managing to load events for the current user only, I realised ill have a hard time loading different data for each tab - as the StreamBuilder stream: will always be the same for all the tabs.
I need help finding a way to have a different StreamBuilder for each of my tabs.
Code below:
StreamBuilder(
stream: _streamer(),//Firestore.instance.collection("Events").snapshots(),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if (!snapshot.hasData) return const Text("Loading...");
return new SizedBox(
height: MediaQuery.of(context).size.height - 42 - MediaQuery.of(context).padding.bottom -AppBar().preferredSize.height - kToolbarHeight,
child: Column(
children: <Widget>[
Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
Container(
child: ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) =>
_buildListItem(context,
snapshot.data[index]),
separatorBuilder: (context, index) {
return Divider();
},
shrinkWrap: true,
),
),
Container(
child: ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) =>
_buildListItem(context,
snapshot.data[index]),
separatorBuilder: (context, index) {
return Divider();
},
shrinkWrap: true,
),
),
Container(
child: ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) =>
_buildListItem(context,
snapshot.data[index]),
separatorBuilder: (context, index) {
return Divider();
},
shrinkWrap: true,
),
),
],
),
)
],
));
},
)
This is how the home page looks
I assume you're loading all data required for all your tabs using that stream. What you could do is have distinct pairs of stream/StreamBuilder for each tab:
... // widgets leading up to the TabBarView
TabBarView(
controller: _controller,
children: <Widget>[
StreamBuilder(
stream: friendsStream,
builder: (BuildContext context, AsyncSnapshot<SomeData> snapshot) {
return FriendsWidget();
}
),
StreamBuilder(
stream: groupsStream,
builder: (BuildContext context, AsyncSnapshot<SomeData> snapshot) {
return GroupsWidget();
}
),
//... etc
]
)
You could also wrap ONLY the widgets that should be rebuilt with a StreamBuilder instead of wrapping the whole parent widget:
... // widgets leading up to the TabBarView
TabBarView(
controller: _controller,
children: <Widget>[
Column(
children: <Widget>[
SomeWidget(),
AnotherWidget(),
StreamBuilder(
stream: friendsStream,
builder: (BuildContext context, AsyncSnapshot<SomeData> snapshot) {
return FriendsWidget();
}
)
]
),
// ... etc
]
)
If you are compelled to load all your data from the database at once, you could use a Repository class to load and store all your data, and then, depending on your architecture, have streams built from that class.
As a side note, I highly recommend using a Bloc with BlocBuilder. Blocs are just far superior to other designs (in most cases). flutter_bloc is my favorite.
Related
Flutter newbie here!
Currently, my Scaffold has 2 listview builders and the bottom one is giving me the unbounded height (!constraints.hasBoundedHeight error) issue.
I have tried using shrinkWrap on all 3 list views, as suggested in similar questions but I get the same error.
The only thing that works is wrapping the FutureBuilder in a SizedBox. But that seems unintuitive to me, as I would want it to ideally expand as needed and be scrollable.
My rough solution is to maybe dynamically calculate the height based on the number of items the FutureBuilder needs, but again, I feel there should be a better solution.
My code snippet is attached below:
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 2,
itemBuilder: (context, index) {
return const SuggestCard(
indexKey: 'takt',
);
}),
FutureBuilder<AnimeDetails>(
future: _animeDetail,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: 2, //Number of anime from list
itemBuilder: (context, index) {
var anime = snapshot.data; //Get the data from the index
return AnimeCard(
anime: anime,
);
});
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
],
),
);
As per your comment I think below link will helpful to you.
Lists
The parent ListView handling scroll event for body and while second ListView will have fixed height, you can do it like this
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => ListView(
children: [
SizedBox(
height: constraints.maxHeight * .3,
child: ListView.builder(
itemCount: 122,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => Text("H $index"),
),
),
ListView.builder(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(), // parent controll the scroll event
itemCount: 44,
itemBuilder: (context, index) => Text("$index"),
),
],
),
));
I just added the below lines to your code. You can try the below code.
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const SuggestCard(
indexKey: 'takt',
);
}),
FutureBuilder<AnimeDetails>(
future: _animeDetail,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: 2, //Number of anime from list
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
var anime = snapshot.data; //Get the data from the index
return AnimeCard(
anime: anime,
);
});
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
],
),
);
I want to create a ToDo-App that saves the ToDo's in Firestore.
I am already able to add every new ToDo Item to Firestore and now I want those Items that were added to pop up when the application opens.
I have made the following:
body: Column(
children:<Widget>[
StreamBuilder<QuerySnapshot>(
stream:FirebaseFirestore.instance.collection("TO-DO-Collection").snapshots(),
builder: (context,snapshot){
if(!snapshot.hasData) return LinearProgressIndicator();
return Expanded(
child: _buildList(snapshot.requireData),
);
},
),
Expanded(
child:ListView(
children: _getItems(),
),
),
],
),
Here the method to build the List with all the stored ToDo's:
Widget _buildList(QuerySnapshot snapshot){
return ListView.builder(
itemCount: snapshot.docs.length,
itemBuilder: (context,index){
final doc=snapshot.docs[index];
return _buildTodoItem(doc["task"]);
},
);
}
(The function _buildTodoItem simply returns a List Tile)
This creates two ListViews (one of the first half of the screen and the other one on the other). Is there any possible way to unify both?
Wrap your column with SingleChildScrollView and put physics: NeverScrollableScrollPhysics(). This will merge the scroll of both of your lists.
body: SingleChildScrollView(
child :Column(
children:<Widget>[
StreamBuilder<QuerySnapshot>(
stream:FirebaseFirestore.instance.collection("TO-DO-Collection").snapshots(),
builder: (context,snapshot){
if(!snapshot.hasData) return LinearProgressIndicator();
return _buildList(snapshot.requireData);
},
),
ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: _getItems(),
),
],
),),
Widget _buildList(QuerySnapshot snapshot) {
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
final doc = snapshot.docs[index];
return _buildTodoItem(doc["task"]);
},
);
}
Problem: I am beginner for flutter developing. I tried to get data from firestore and display it. But scrolling listview didn't respond to one finger so I had to use more than one. How to solve this problem.
body: StreamBuilder(
stream: _firebase_auth.collection("ContactData").snapshots(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemExtent: 100.0,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data.docs[index];
return Card(
child: ListView(padding: EdgeInsets.all(8.0), children: [
Text('sds'),
Text('sds'),
Text('sds'),
Text('sds'),
Text('sds'),
]),
);
});
} else {
return Text('Loading Data.....');
}
},
),
you should wrap streamBuilder with the SingleChild scroll view or use shrinkWrap: true,physics: ScrollPhysics(),
body: StreamBuilder(
stream: _firebase_auth.collection("ContactData").snapshots(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: snapshot.data.docs.length,
itemExtent: 100.0,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data.docs[index];
return Card(
child: ListView(padding: EdgeInsets.all(8.0), children: [
Text('sds'),
Text('sds'),
Text('sds'),
Text('sds'),
Text('sds'),
]),
);
});
} else {
return Text('Loading Data.....');
}
},
),
add this line in listView.builder
physics: AlwaysScrollableScrollPhysics(),
Wrap stream builder with SingleChildScrolView.
This will make the screen a scrollable element.
Set physics of ListView.builder to NeverScrollablePhysics.
This will prevent the scrolling of the parent ListView.Builder, but it's okay because it's parent, SingleChildScrollView is
scrollable.
Set physics of ListView to Scrollable
If you want to, not sure if you want to scroll these items or just display.
I have 2 streambuilders that I want to display, both with a Listview builder inside them. I want to put both those streambuilders in a scrollabe column but it just won't work. I tried wrapping them in an Expanded or Flexible and I just can't figure it out. The problem when using the expanded widget is that even if the widget doesn't have any children, it still takes in a big part of the screen. When for example streambuilder 1 doesn't have data, it still uses a proportion of the screen.
Thanks in advance!
This is my code briefly:
SingleChildScrollView(
child: Column(
children: [
Expanded(
child: StreamBuilder(
stream: stream1,
builder: (context, snapshot) {
return ListView.builder();
},
),
),
Expanded(
child: StreamBuilder(
stream: stream2,
builder: (context, snapshot) {
return ListView.builder();
},
),
),
],
),
),
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: StreamBuilder(
stream: stream1,
builder: (context, snapshot) {
return ListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: firstList(),
),
},
),
),
Expanded(
child: StreamBuilder(
stream: stream2,
builder: (context, snapshot) {
return ListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: secondList(),
),
},
),
),
],
),
)
You can make the ListView widget never scrollable by setting physics property to physics: NeverScrollableScrollPhysics(),
and
With shrinkWrap: true, you can change this behavior so that the ListView only occupies the space it needs (it will still scroll when there more items).
its make two diferent StreamBuilder widget in a single scrollable.
I am trying to add 70.0px at the bottom of each of my Container item in my ListView.
I have been looking at a lot of answers from StackOverflow and trying them in many different ways, and adding padding inside ListView doesn't work somehow. However, I still couldn't tackle it.
Can someone please give me some advice please?
class PortfolioRow extends StatelessWidget {
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return Container(
margin: const EdgeInsets.only(bottom: 70.0),
child: StreamBuilder(
stream: Firestore.instance
.collection('portfolio')
.where('category')
.snapshots(),
builder: (context, snapshot) {
return ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemExtent: 450.0,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) => portfolioContainer(
context, snapshot.data.documents[index]));
}));
...
}
Widget portfolioContainer(
BuildContext context, DocumentSnapshot documentSnapshot) {
return Align(
child: SizedBox(
height: 540,
width: 330,
child: Container( // if I add padding bottom here, there will be a pixel overflow in my container
child: Column(children: [
Container(
...
}
ListView has a named constructor for exactly that : ListView.separated(). The separatorBuilder parameter lets you specify a widget that will be added at the bottom of each item :
ListView.separated(
itemCount: 25,
separatorBuilder: (BuildContext context, int index) => Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('item $index'),
);
},
)