Reputation: 130
I have an application with multiple indexes on a navigation view, when I first load the app, as expected the StreamBuilder is called two times. however, when I navigate to another index and back the build with the stream builder seems to be calling the stream builder to redownload all of the items inside of it. I am worried this is going to consume all of my firebase data if this loop is ran for too long of a period. How could I restructure to stop the StreamBuilder from being called many times when switching pages. Note, I even save the state of this index when I switch and come back.
My build method:
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection("posts/player/post")
.orderBy("time", descending: true)
.snapshots()
.take(2),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int highLightCount = snapshot.data.documents.length;
return new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection("posts/player/post")
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return new Text("There are no current posts");
return new ListView(
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: getPostItems(snapshot),
);
});
});
}
When I switch it is calling the getPostItem(snaphot) that is being called nearly nonstop, how could I prevent this from being called and consuming my data, or, prevent the StreamBuilder from changing unless something actually updates?
The rest of the important code:
getPostItems(AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.data.documents.map((doc) => getListItem(doc)).toList();
}
Widget getListItem(var doc) {
getProfUrl(doc);
getDownUrl(doc);
print("item hit");
print("user: " + doc["user"] + "- current: " + widget.auth.getUserId());
if (doc["user"] != widget.auth.getUserId()) {
print("will show");
if (doc["type"] == "image") {
return new Column(
children: <Widget>[
new ListTile(
title: new Text(doc["title"]),
subtitle: new Text(doc["description"].toString()),
leading: new Container(
width: 44.0,
height: 44.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.fill, image: NetworkImage(profUrl)),
))),
new Padding(
padding: EdgeInsets.fromLTRB(4, 4, 4, 4),
child: new Center(
child: new AspectRatio(
aspectRatio: 1 / 1,
child: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
fit: BoxFit.fill,
alignment: FractionalOffset.topCenter,
image: new NetworkImage(downUrl),
)),
),
),
),
),
],
);
} else {
VideoPlayerController _controller = VideoPlayerController.network(
'http://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4')
..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
return new Column(children: <Widget>[
new ListTile(
title: new Text(doc["title"]),
subtitle: new Text(doc["description"].toString()),
leading: new Container(
width: 44.0,
height: 44.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.fill, image: NetworkImage(profUrl)),
))),
new Padding(
padding: EdgeInsets.fromLTRB(4, 4, 4, 4),
child: new Center(
child: new AspectRatio(
aspectRatio: 500 / 500,
child: VideoPlayer(_controller),
),
),
),
]);
}
}
}
Upvotes: 1
Views: 2729
Reputation: 1962
In order to reduce the number of times that the Stream
is being created, you could move its initialisation into the initState
method of the State
class.
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
Stream<QuerySnapshot> _outerStream;
Stream<QuerySnapshot> _innerStream;
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _outerStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int highLightCount = snapshot.data.documents.length;
return StreamBuilder<QuerySnapshot>(
stream: _innerStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('There are no current posts');
return ListView(
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: getPostItems(snapshot),
);
},
);
},
);
}
@override
void initState() {
super.initState();
_outerStream = Firestore
.instance
.collection('posts/player/post')
.orderBy('time', descending: true)
.snapshots()
.take(2);
_innerStream = Firestore
.instance
.collection('posts/player/post')
.snapshots();
}
}
Alternatively, if you would like to create the Stream
once during the lifetime of the application, then you could use a StreamProvider
to provide the Stream
.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<QuerySnapshot>(
create: (context) {
return Firestore.instance.collection('posts/player/post').snapshots();
},
),
],
child: MaterialApp(
home: MyPage(),
),
);
}
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final snapshot = context.watch<QuerySnapshot>();
return Scaffold(
body: snapshot == null
? Text('There are no current posts')
: ListView(
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: getPostItems(snapshot),
),
);
}
}
Upvotes: 5