StreamBuilder 在构建时被多次调用
StreamBuilder being called numerous times when in build
我有一个在导航视图上有多个索引的应用程序,当我第一次加载该应用程序时,正如预期的那样,StreamBuilder 被调用了两次。但是,当我导航到另一个索引并使用流构建器返回构建时,似乎正在调用流构建器以重新下载其中的所有项目。我担心如果此循环 运行 的时间过长,这将消耗我所有的 firebase 数据。我怎样才能重组以阻止 StreamBuilder 在切换页面时被多次调用。注意,我什至在切换回来的时候保存了这个索引的状态。
我的构建方法:
@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),
);
});
});
}
当我切换时,它正在调用几乎不间断调用的 getPostItem(snaphot),我怎样才能防止它被调用并消耗我的数据,或者,防止 StreamBuilder 发生变化,除非某些东西实际更新?
其余重要代码:
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),
),
),
),
]);
}
}
}
为了减少创建 Stream
的次数,您可以将其初始化移动到 State
class 的 initState
方法中。
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();
}
}
或者,如果您想在应用程序的生命周期内创建一次 Stream
,那么您可以使用 StreamProvider
来提供 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),
),
);
}
}
我有一个在导航视图上有多个索引的应用程序,当我第一次加载该应用程序时,正如预期的那样,StreamBuilder 被调用了两次。但是,当我导航到另一个索引并使用流构建器返回构建时,似乎正在调用流构建器以重新下载其中的所有项目。我担心如果此循环 运行 的时间过长,这将消耗我所有的 firebase 数据。我怎样才能重组以阻止 StreamBuilder 在切换页面时被多次调用。注意,我什至在切换回来的时候保存了这个索引的状态。
我的构建方法:
@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),
);
});
});
}
当我切换时,它正在调用几乎不间断调用的 getPostItem(snaphot),我怎样才能防止它被调用并消耗我的数据,或者,防止 StreamBuilder 发生变化,除非某些东西实际更新?
其余重要代码:
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),
),
),
),
]);
}
}
}
为了减少创建 Stream
的次数,您可以将其初始化移动到 State
class 的 initState
方法中。
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();
}
}
或者,如果您想在应用程序的生命周期内创建一次 Stream
,那么您可以使用 StreamProvider
来提供 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),
),
);
}
}