FutureBuilder inside ListView.builder not working - flutter

Want to display items using FutureBuilder inside a ListView.builder. However, theres no display. help pls
body: ListView.builder(
itemExtent: 25.0,
itemCount: _posts.length,
itemBuilder: (BuildContext context, int index){
Post post = _posts[index];
return FutureBuilder(
future: DatabaseService.getUserWithId(post.authorId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
height: 200.0,
margin: EdgeInsets.all(10.0),
color: Colors.red,
);
},
);
},
),

Related

Flutter combine futuerbuilder and streambuilder

i need some guidance how to combine a futurebuilder with a streambuilder. For example, the futurebuilder will load the comments which are stored in my Mysql database, while the streambuilder will fetch new comments and add them to the listview. The stream and future works perfectly, but i have no idea how i will combine these two things with eachother.
FutureBuilder(
future: _fetchComments(39),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return Container(
height: 150,
child: StreamBuilder(
stream: commentProvider.channel.stream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot2) {
return ListView.builder(
key: PageStorageKey("CommentsScroll"),
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
final comment = snapshot.data[index];
return CommentTile(
key: Key(DateTime.now().toString()),
photoUrl: comment["photo_url"],
displayName: comment["display_name"],
created: comment["created"],
text: comment["text"],
);
},
);
},
),
);
},
),

Having a different StreamBuilder for every tab in TabBarView

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.

Flutter - Evenly spaced padding at the bottom of each ListView item

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'),
);
},
)

Better way to pass selected index in flutter

I have data from firestore
I need to display the name in a single page and the name of content in another page and so episodes
Is there a better way than this
tvshow page
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
tvSelected = index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return SeasonsPage(selectedTv: tvSelected);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[index].name),
),
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
seasons page:
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedTv].content.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
selectedSeason = index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return EpisodesPage(
selectedTv: selectedTv,
selectedSeason: selectedSeason,
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(
snapshot.data[selectedTv].content[index].name),
),
);
},
)
: Center(child: CircularProgressIndicator()),
),
episodes page:
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedTv].content[selectedSeason]
.episodes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[selectedTv]
.content[selectedSeason].episodes[index]),
);
},
)
: Center(child: CircularProgressIndicator()),
),
look at when I pass a route page
.............................
.............................
...........................
Firstly you you create a new provider class that will hold the indices of the currently selected tv,episode and season as shown bellow:
class CurrentIndexProvider with ChangeNotifier {
int _selectedTv;
int _selectedSeason;
int _selectedEpisode;
set selectedTv(int newIndex) {
this._selectedTv = newIndex;
notifyListeners();
}
set selectedSeason(int newIndex) {
this._selectedSeason = newIndex;
notifyListeners();
}
set selectedEpisode(int newIndex) {
this._selectedEpisode = newIndex;
notifyListeners();
}
int get selectedTv => this._selectedTv;
int get selectedSeason => this._selectedSeason;
int get selectedEpisode => this._selectedEpisode;
}
Then your tv shows page becomes:
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
// tvSelected = index; -->we dont need this anylonger
//we set the current show number to the index of the current
//listview item when tapped
selectedItems.selectedSeason=index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return SeasonsPage(
//we don't need to pass anything in the constructor again
// selectedTv: tvSelected
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[index].name),
),
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
your seasons page becomes
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[ selectedItems.selectedTv].content.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
//selectedSeason = index; --> we dont need this any longer
selectedItems.selectedSeason=index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return EpisodesPage(
//we don't need any parameter in the constructor now
// selectedTv: selectedTv,
// selectedSeason: selectedSeason,
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(
snapshot.data[selectedItems.selectedTv].content[index].name),
),
);
},
)
: Center(child: CircularProgressIndicator()),
),
And finally the episodes page becomes
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedItems.selectedTv ].content[selectedItems.selectedSeason]
.episodes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[selectedItems.selectedTv]
.content[selectedItems.selectedSeason].episodes[index]),
);
},
)
: Center(child: CircularProgressIndicator()),
),
With this you can have access to the currently selected tv show or seasons or even episodes anywhere within you code by using provider. hope this helped

How to Loop through a ListTile from ListView.builder in flutter

I am trying to display a list tile in flutter based on list of data from cloud firestore. I want the leading icon to change for each tile when the tile is tapped. My problem is whenever I tap any tile, the whole list changes. I want only the tapped tile to change. Here is my code:
StreamBuilder(
stream: Firestore.instance
.collection('Recharge_Card')
.snapshots(),
//print an integer every 2secs, 10 times
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading..");
}
return SizedBox(
height: _height / 1.9,
child: ListView.builder(
// itemExtent: 80.0,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot myCards =
snapshot.data.documents[index];
return Card(
elevation: 20.0,
child: ListTile(
onTap: () {
setState(() {
x = Text('Tapped');
});
},
leading: x,
title: Text(myCards['CardPin']),
trailing: Text(myCards['Value']),
),
);
},
),
);`
},
),
Add key to your card and provide the unique value, in your case it is your index
Card(
key: ValueKey(index),
//....
)
Thats happens because the x is the same for all cards, you need separate this guy, my suggestion is put the x inside DocumentSnapshot, so u can change only tapped card, something like this:
StreamBuilder(
stream: Firestore.instance
.collection('Recharge_Card')
.snapshots(),
//print an integer every 2secs, 10 times
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading..");
}
return SizedBox(
height: _height / 1.9,
child: ListView.builder(
// itemExtent: 80.0,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot myCards =
snapshot.data.documents[index];
return Card(
elevation: 20.0,
child: ListTile(
onTap: () {
setState(() {
myCards['x'] = Text('Tapped');
});
},
leading: myCards['x'],
title: Text(myCards['CardPin']),
trailing: Text(myCards['Value']),
),
);
},
),
);`
},
),