Improving performance of ListView.builder with nested FutureBuilders - flutter

The ListView.builder of my widget is really laggy and slow when scrolling, especially in debug mode.
class SongsScreen extends StatelessWidget {
const SongsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final songsProvider = Provider.of<Songs>(context, listen: false);
final songs = songsProvider.getSongs();
return Scaffold(
body: Center(
child: ListView.builder(
primary: true,
itemCount: songs.length,
itemBuilder: (context, index) {
return FutureBuilder<List<dynamic>>(
future: Future.wait([
songsProvider.getTags(songs[index]),
songsProvider.getArtwork(songs[index]),
]),
builder: (ctx, snapshot) {
if (snapshot.hasData) {
return snapshot.data![1] != null
? ListTile(
title: Text(snapshot.data![0].title ?? 'Unknown'),
subtitle: Text(snapshot.data![0].artist ?? 'Unknown'),
leading: ClipRRect(
borderRadius: BorderRadius.circular(4.0),
child: Image.memory(
snapshot.data![1],
fit: BoxFit.fill,
)),
)
: ListTile(
title: Text(songs[index]),
subtitle: const Text('Unknown'),
leading: const CircleAvatar(
child: Icon(Iconsax.music5),
),
);
} else {
return ListTile(
title: Text(songs[index]),
subtitle: const Text('Unknown'),
leading: const CircleAvatar(
child: Icon(Iconsax.music5),
),
);
}
},
);
},
),
),
);
}
}
Is there a possible way to improve the performance? My guess is that the FutureBuilders are slowing down the performance but I could be wrong.
EDIT: I've rearranged the code and now I see a small improvement. But it's still not so smooth.

You should call the future function to wait for the result, and as soon as it produces the result it calls the builder function where you build the widget.
import 'dart:io';
import 'dart:typed_data';
import 'package:audiotagger/models/tag.dart';
import 'package:flutter/material.dart';
import 'package:iconsax/iconsax.dart';
import 'package:provider/provider.dart';
import 'package:taggr/providers/songs.dart';
class SongsScreen extends StatelessWidget {
const SongsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final songs = Provider.of<Songs>(context);
return Scaffold(
body: Center(
child: FutureBuilder(
future: songs.getTags(songs.getSongs()),
builder: (ctx, AsyncSnapshot<Tag?> snapshot) {
if (snapshot.hasData) {
return Center(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index]!.title!),
subtitle: Text(snapshot.data[index]!.artist!),
leading: FutureBuilder(
future: songs.getArtwork(songs.getSongs()[index]
.path),
builder: (ctx, AsyncSnapshot<
Uint8List?> artworkSnapshot) {
if (artworkSnapshot.hasData) {
return ClipRRect(
borderRadius: BorderRadius.circular(4.0),
child: Image.memory(artworkSnapshot.data!),
);
} else {
return const CircleAvatar(
child: Icon(Iconsax.music5),
);
}
},
),
);
}
),
);
} else {
return const Text('n');
}
},
),
)
);
}
}

Related

Refresh the page data when you go to this page in the flutter

I'm trying to write a small application in which I collect data through api. I take the data, everything works. I decided to make a navigation bar to switch between pages. But when I try on the pages they are empty. In order for the data to be updated on the page, I need to click "Hot reload". I will be grateful for your help.
My main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/model/dataArea_list/JsonDataArea.dart';
import 'package:flutter_app_seals/model/object_list/JsonObject.dart';
import 'package:flutter_app_seals/model/seals_list/JsonSeals.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomeScreen());
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Журнал пломби'),
),
// body: Seals(),
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Seals List"),
trailing: Icon(Icons.arrow_back),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Seals()),
);
}
)
],
),
),
);
}
}
class Seals extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home:JsonParseSeals(),
);
}
}
My modul Seals:
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/model/seals_list/SealsListGet.dart';
import 'package:flutter_app_seals/model/seals_list/ServicesSeals.dart';
class JsonParseSeals extends StatefulWidget {
//
JsonParseSeals() : super();
#override
_JsonParseSealsState createState() => _JsonParseSealsState();
}
class _JsonParseSealsState extends State <StatefulWidget> {
//
List<SealList> _seals;
bool _loading;
#override
void initState(){
super.initState();
_loading = true;
Services.getSeals().then((seals) {
_seals =seals;
_loading = false;
}
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Список пломби'),
),
body: ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(40),
itemCount: null == _seals ? 0 :_seals.length,
itemBuilder: (_,index) => Card(
color: Colors.red[300],
margin: EdgeInsets.symmetric(vertical: 7),
child:ListTile(
title: Text(_seals[index].sealNumber,
style: TextStyle(fontSize: 30),
),
subtitle: Text(
"${_seals[index].used}" ),
leading: Icon(Icons.local_activity,
size: 40,
color: Colors.black87,
),
),
),
),
);
}
}
My code :
Code after change:
Try to wrap your screen with data in FutureBuilder (you can read more about this widget here):
class _JsonParseSealsState extends State <StatefulWidget> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<SealList>>(
future: Services.getSeals(),
builder: (context, snapshot) {
// Data is loading, you should show progress indicator to a user
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
// Data is loaded, handle it
return ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(40),
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
final item = snapshot.data[index];
return Card(
color: Colors.red[300],
margin: EdgeInsets.symmetric(vertical: 7),
child: ListTile(
title: Text(
item.sealNumber,
style: TextStyle(fontSize: 30),
),
subtitle: Text("${item.used}"),
leading: Icon(
Icons.local_activity,
size: 40,
color: Colors.black87,
),
),
);
},
),
}
);
}
}

Flutter, using FutureBuilder within SliverFillRemaining

I am building a FutureBuilder with a Listview within a SliverFillRemaining. And I think since Sliver is already within a CustomScrollView the scroll function doesn't work properly. Once it scrolls down it doesn't scroll back up.
Below is the code.
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200.0,
floating: false,
//pinned: false,
flexibleSpace: FlexibleSpaceBar(
background: Image.network("https://i.imgur.com/p3CfZBS.png",
fit: BoxFit.cover),
),
),
SliverFillRemaining(
child: Container(
child: FutureBuilder(
future: _getData(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListView(
shrinkWrap: true,
children: [
buildlink(
imageName: snapshot.data[index].image,
page: snapshot.data[index].title)
],
);
},
);
}
},
),
),
),
],
),
);
}
}
most likely the 2nd listView is superfluous and probably you want to use physics: NeverScrollableScrollPhysics() with list view, if you use CustomScrollView. Check NestedScrollView may be it would work better in this situation.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: false,
flexibleSpace: FlexibleSpaceBar(
background: Image.network("https://i.imgur.com/p3CfZBS.png",
fit: BoxFit.cover),
),
),
SliverFillRemaining(
child: Container(
child: FutureBuilder(
future: _getData(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return buildlink(
imageName: snapshot.data[index].image,
page: snapshot.data[index].title,
);
},
);
}
},
),
),
),
],
),
);
}
Future<List<LinkData>> _getData() async {
await Future.delayed(Duration(seconds: 1));
return [
LinkData(image: "https://i.imgur.com/p3CfZBS.png", title: 'First'),
LinkData(image: "https://i.imgur.com/p3CfZBS.png", title: 'Second'),
LinkData(image: "https://i.imgur.com/p3CfZBS.png", title: 'Third'),
];
}
Widget buildlink({String imageName, String page}) {
return Card(
child: Container(
child: Text(page),
height: 400,
),
);
}
}
class LinkData {
const LinkData({
this.image,
this.title,
});
final String image;
final String title;
}

Set state from FutureBuilder in Flutter

I've been trying to set a property based on a response from a FutureBuilder but can't seem to get it working. How can I set _activityLength after the future resolves without setting during build?
FutureBuilder<QuerySnapshot>(
future: _future,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start');
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
final documents = snapshot.data.documents;
_activityLength = documents.length;
return Expanded(
child: ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 0,
),
itemCount: documents.length,
itemBuilder: (context, index) => _activityTile(
documents[index],
),
),
);
}
}
},
)
The FutureBuilde is in a Column widget in the body of the Scaffold and the value that I need to set is in the _itemsHeaderText Something like this:
body:
...
child: Column(
children: <Widget>[
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey, width: 1.0),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 15.0,
right: 15.0,
top: 10.0,
bottom: 10.0),
child: _itemsHeaderText(),
),
),
_itemsBody(),
],
),
You can copy paste run full demo code below
You can use _future.then((value) in initState() and do job in addPostFrameCallback like setState
And after future resolves, you can get value of _future, you can do further processing if need
In demo code, I get value length and display on screen
You can see working demo, data length change from 0 to 9
code snippet
#override
void initState() {
super.initState();
_future = _getUsers();
_future.then((value) {
print("data length ${value.length}");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("other job");
setState(() {
_dataLength = value.length;
});
});
});
}
working demo
output
I/flutter (18414): data length 9
I/flutter (18414): other job
full code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: CategoryTab(title: 'Flutter Demo Home Page'),
);
}
}
class CategoryTab extends StatefulWidget {
CategoryTab({Key key, this.title}) : super(key: key);
final String title;
#override
_CategoryTabState createState() => _CategoryTabState();
}
class _CategoryTabState extends State<CategoryTab> {
Future<List<CategoryList>> _future;
int _dataLength = 0;
Future<List<CategoryList>> _getUsers() async {
var data = await http
.get("https://appiconmakers.com/demoMusicPlayer/API/getallcategories");
var jsonData = json.decode(data.body);
List<CategoryList> cat = [];
for (var u in jsonData) {
CategoryList categoryList = CategoryList(
u["category_id"],
u["category_name"],
u["parent_category_id"],
u["category_status"],
u["created_date"]);
cat.add(categoryList);
}
return cat;
}
#override
void initState() {
super.initState();
_future = _getUsers();
_future.then((value) {
print("data length ${value.length}");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("other job");
setState(() {
_dataLength = value.length;
});
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Categories",
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
body: Column(
children: [
Text("data length $_dataLength"),
Expanded(
child: Container(
child: FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(child: Center(child: Text("Loading...")));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(),
title: Text(
"${snapshot.data[index].categoryName}",
// subtitle: Text(snapshot.data[index].categoryId
),
);
},
);
}
},
),
),
),
],
),
);
}
}
class CategoryList {
String categoryId;
String categoryName;
String parentCategoryId;
String categoryStatus;
String createdDate;
CategoryList(this.categoryId, this.categoryName, this.parentCategoryId,
this.categoryStatus, this.createdDate);
}
There are a few workarounds to your question.
You could hoist the futureBuilder up the widget tree, instead of setting state the state will be reset once ConnectionState.done.
You could place the future in a function that is called on init state, then the result of the future you set state on it.

Am Trying To calculate total price of a List of items

am making a shopping cart app where I want to calculate the total price of the products present in the cart I made a function for it and tried executing in init state but it's not working
import 'package:flutter/material.dart';
import 'package:shop/Models/Database.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
int sum = 0;
total() {
studlist.forEach((element) {
sum = sum + element.price;
});
}
#override
void initState() {
total();
super.initState();
}
final DbStudentManager dbmanager = new DbStudentManager();
Student cart;
List<Cart> cartList;
int updateIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Cart',
style: TextStyle(color: Colors.black),
),
),
body: FutureBuilder(
future: dbmanager.getStudentList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
studlist = snapshot.data;
}
return ListView.builder(
itemCount: studlist == null ? 0 : studlist.length,
itemBuilder: (BuildContext context, int index) {
Student ct = studlist[index];
return Card(
child: ListTile(
title: Text(ct.name),
subtitle: Text('${ct.price}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
dbmanager.deleteStudent(ct.id);
setState(() {
studlist.remove(index);
});
}),
),
);
});
}),
bottomNavigationBar: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Text('$sum'),
SizedBox(
width: 10,
),
Text('Check Out'),
],
),
),
],
),
);
}
}
the error I get:
The method 'forEach' was called on null.
Receiver: null
Tried calling: forEach(Closure: (Student) => Null)
Try this:
import 'package:flutter/material.dart';
import 'package:shop/Models/Database.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
int sum = 0;
#override
void initState() {
super.initState();
}
final DbStudentManager dbmanager = new DbStudentManager();
Student cart;
List<Cart> studList=[];
int updateIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Cart',
style: TextStyle(color: Colors.black),
),
),
body: FutureBuilder(
future: dbmanager.getStudentList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
studlist = snapshot.data;
studlist.forEach((element) {
setState((){
sum = sum + element.price;
});
});
}
return ListView.builder(
itemCount: studlist == null ? 0 : studlist.length,
itemBuilder: (BuildContext context, int index) {
Student ct = studlist[index];
return Card(
child: ListTile(
title: Text(ct.name),
subtitle: Text('${ct.price}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
dbmanager.deleteStudent(ct.id);
setState(() {
studlist.remove(index);
});
}),
),
);
});
}),
bottomNavigationBar: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Text('$sum'),
SizedBox(
width: 10,
),
Text('Check Out'),
],
),
),
],
),
);
}
}
Try running total() function if the data when the data is loaded, not in init. As data is initially empty, running it inside init will cause this error.
if (snapshot.hasData) {
studlist = snapshot.data;
total();
}
Full Code:
import 'package:flutter/material.dart';
import 'package:shop/Models/Database.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
int sum = 0;
total() {
studlist.forEach((element) {
sum = sum + element.price;
});
}
final DbStudentManager dbmanager = new DbStudentManager();
Student cart;
List<Cart> cartList;
int updateIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Cart',
style: TextStyle(color: Colors.black),
),
),
body: FutureBuilder(
future: dbmanager.getStudentList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
studlist = snapshot.data;
total(); //Run the total() function here
}
return ListView.builder(
itemCount: studlist == null ? 0 : studlist.length,
itemBuilder: (BuildContext context, int index) {
Student ct = studlist[index];
return Card(
child: ListTile(
title: Text(ct.name),
subtitle: Text('${ct.price}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
dbmanager.deleteStudent(ct.id);
setState(() {
studlist.remove(index);
});
}),
),
);
});
}),
bottomNavigationBar: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Text('$sum'),
SizedBox(
width: 10,
),
Text('Check Out'),
],
),
),
],
),
);
}
}
try this
If I added Quantity increased totalQty but I have one issue if I removing Item in list
did not decrease. sorry my english not good
`total() {
studlist.forEach((element) {
if(element.qty!=null){
totalQty=totalQty+element.qty;
print(totalQty);
}
});
}
`

How do I pass a single Firestore document ID to another class based on a selection using Flutter/Dart?

I am trying to design a feature using flutter that when a user selects a thumbnail image of a trip it will take the user to a page that has more details of the trip they selected. I am trying to query the Firestore db as little as possible so I was trying to get the document ID from a single query snapshot and pass it to the IndividualTripPackage class. I have tried this approach numerous ways but all of them have failed. I alsow looked at other solutions people posted on SO and I could not get them to work for my specific case. What am I doing wrong? I am new to flutter so if you have ideas about other approaches or more efficient solutions I am open to suggestions.
TripPackages Class:
class _TripPackagesState extends State<TripPackages> {
#override
Widget build(BuildContext context) {
//Some other code......
child: SingleChildScrollView(
child: StreamBuilder<QuerySnapshot>(
stream:
Firestore.instance.collection('trip_package').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> docSnapshot) {
if (!docSnapshot.hasData) return const Text('Loading...');
final int docCount = docSnapshot.data.documents.length;
return GridView.builder(
shrinkWrap: true,
primary: false,
scrollDirection: Axis.vertical,
itemCount: docCount,
itemBuilder: (_, int index) {
DocumentSnapshot document =
docSnapshot.data.documents[index];
return GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => IndividualTripPackage(
docID: docSnapshot.data.documents[index]),
),
//Some other code .....
}
}
IndividualTripPackage Class:
class IndividualTripPackage extends StatefulWidget {
DocumentSnapshot docID;
IndividualTripPackage({this.docID});
#override
_IndividualTripPackageState createState() => _IndividualTripPackageState();
}
class _IndividualTripPackageState extends State<IndividualTripPackage> {
#override
Widget build(BuildContext context) {
final String docID = widget.docID.data['docID'];
return Material(
child: SafeArea(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: viewportConstraints.maxHeight),
child: StreamBuilder(
stream: Firestore.instance.collection('trip_package').document('docID').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading data....Please wait...');
} else {
final int itemCount = snapshot.data.document('docID').data['itineraryItems'].length;
return Column(...);
}
}),
//Some more code........
}
}
Alrighty I'm hoping this answers your question!
I've been using a method like this for my app and it seems to work for me. Of course you will want to adjust the class names and other code to your liking, but hopefully this is what you're trying to do.
Trip Thumbnail
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:my_test_project/screens/detail.dart';
class Thumbnail extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ThumbnailState();
}
class _ThumbnailState extends State<Thumbnail> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(
'Thumbnail'
),
),
body: ListView(
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('trips').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
if (!snapshot.hasData) return Container(
child: Center(
child: CircularProgressIndicator()
),
);
return Column(
children: snapshot.data.documents.map((doc) {
return GestureDetector(
onTap: () {
var docId = doc.documentID;
Navigator.push(context, MaterialPageRoute(builder: (context) => Detail(docId)));
},
child: Container(
child: Image(
image: NetworkImage(
doc.data['photo']
),
),
),
);
}).toList(),
);
},
)
],
)
);
}
}
Trip Detail
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Detail extends StatefulWidget {
final docId;
Detail(this.docId);
#override
State<StatefulWidget> createState() => _DetailState(docId);
}
class _DetailState extends State<Detail> {
final docId;
_DetailState(this.docId);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Detail'
),
),
body: ListView(
children: <Widget>[
StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance.collection('trips').document(docId).snapshots(),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
if (!snapshot.hasData) return Container(
child: Center(
child: CircularProgressIndicator()
),
);
return Column(
children: <Widget>[
Container(
child: Text(
snapshot.data['title'],
style: TextStyle(
fontSize: 24.0
),
),
),
Container(
child: Image(
image: NetworkImage(
snapshot.data['photo']
),
),
),
Container(
child: Text(
snapshot.data['body']
)
)
],
);
},
)
],
),
);
}
}
Basically I'm passing along a variable to the next screen that has the document's ID to use for the DocumentSnapshot Stream.