Flutter StreamBuilder 在初始化时调用了两次
Flutter StreamBuilder Called Twice When Initialized
StreamBuilder 总是调用两次吗?一次用于初始数据,然后一次用于输入流?
初始化下面的StreamBuilder可以看出build方法被调用了两次。第二次调用比第一次调用晚了 0.4 秒。
流:构建 1566239814897
流:构建 1566239815284
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:nocd/utils/bloc_provider.dart';
void main() =>
runApp(BlocProvider<MyAppBloc>(bloc: MyAppBloc(), child: MyApp()));
class MyAppBloc extends BlocBase {
String _page = window.defaultRouteName ?? "";
/// Stream for [getPage].
StreamController<String> pageController = StreamController<String>();
/// Observable navigation route value.
Stream get getPage => pageController.stream;
MyAppBloc() {}
@override
void dispose() {
pageController.close();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final MyAppBloc myAppBloc = BlocProvider.of<MyAppBloc>(context);
return StreamBuilder(
stream: myAppBloc.getPage,
initialData: "Build",
builder: (context, snapshot) {
print("Stream: " +
snapshot.data +
DateTime.now().millisecondsSinceEpoch.toString());
return Container();
},
);
}
}
为什么 StreamBuilder 被调用了两次?
StreamBuilder 在初始化时进行两次构建调用,一次用于初始数据,第二次用于流数据。
Streams 不保证它们会立即发送数据,因此需要初始数据值。将 null
传递给 initialData
会引发 InvalidArgument 异常。
StreamBuilders 将始终构建两次,即使传递的流为空。
更新:
有关为什么即使提供了 initalData
StreamBuilder 也会多次构建的详细技术解释可以在这个 Flutter 问题线程中找到:https://github.com/flutter/flutter/issues/16465
It's not possible for a broadcast stream to have an initial state. Either you were subscribed when the data was added or you missed it. In an async single-subscription stream, any listen calls added won't be invoked until either the next microtask or next event loop (can't remember, may depend), but at any rate there is no way to get the data out the stream on the current frame. - jonahwilliams
Streambuilder 将被调用 2 次,第一次用于 Initial,第二次用于流。并且数据仅在状态为 ConnectionState.active. 时更改
请参阅官方文档示例。
StreamBuilder<int>(
//stream:fire, // a Stream<int> or null
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Select lot');
case ConnectionState.waiting:
return Text('Awaiting bids...');
case ConnectionState.active:
return Text('$${snapshot.data}');
case ConnectionState.done:
return Text('$${snapshot.data} (closed)');
}
return null; // unreachable
},
);
The initial snapshot data can be controlled by specifying initialData. This should be used to ensure that the first frame has the expected value, as the builder will always be called before the stream listener has a chance to be processed.
Providing this value (presumably obtained synchronously somehow when the Stream was created) ensures that the first frame will show useful data. Otherwise, the first frame will be built with the value null, regardless of whether a value is available on the stream: since streams are asynchronous, no events from the stream can be obtained before the initial build.
如上所述,您只需将代码置于 Connection.Active 状态即可。见下文:
StreamBuilder<QuerySnapshot>(
stream: historicModel.query.snapshots(),
builder: (context, stream){
if (stream.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (stream.hasError) {
return Center(child: Text(stream.error.toString()));
} else if(stream.connectionState == ConnectionState.active){
//place your code here. It will prevent double data call.
}
StreamBuilder 总是调用两次吗?一次用于初始数据,然后一次用于输入流?
初始化下面的StreamBuilder可以看出build方法被调用了两次。第二次调用比第一次调用晚了 0.4 秒。
流:构建 1566239814897
流:构建 1566239815284
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:nocd/utils/bloc_provider.dart';
void main() =>
runApp(BlocProvider<MyAppBloc>(bloc: MyAppBloc(), child: MyApp()));
class MyAppBloc extends BlocBase {
String _page = window.defaultRouteName ?? "";
/// Stream for [getPage].
StreamController<String> pageController = StreamController<String>();
/// Observable navigation route value.
Stream get getPage => pageController.stream;
MyAppBloc() {}
@override
void dispose() {
pageController.close();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final MyAppBloc myAppBloc = BlocProvider.of<MyAppBloc>(context);
return StreamBuilder(
stream: myAppBloc.getPage,
initialData: "Build",
builder: (context, snapshot) {
print("Stream: " +
snapshot.data +
DateTime.now().millisecondsSinceEpoch.toString());
return Container();
},
);
}
}
为什么 StreamBuilder 被调用了两次?
StreamBuilder 在初始化时进行两次构建调用,一次用于初始数据,第二次用于流数据。
Streams 不保证它们会立即发送数据,因此需要初始数据值。将 null
传递给 initialData
会引发 InvalidArgument 异常。
StreamBuilders 将始终构建两次,即使传递的流为空。
更新:
有关为什么即使提供了 initalData
StreamBuilder 也会多次构建的详细技术解释可以在这个 Flutter 问题线程中找到:https://github.com/flutter/flutter/issues/16465
It's not possible for a broadcast stream to have an initial state. Either you were subscribed when the data was added or you missed it. In an async single-subscription stream, any listen calls added won't be invoked until either the next microtask or next event loop (can't remember, may depend), but at any rate there is no way to get the data out the stream on the current frame. - jonahwilliams
Streambuilder 将被调用 2 次,第一次用于 Initial,第二次用于流。并且数据仅在状态为 ConnectionState.active. 时更改 请参阅官方文档示例。
StreamBuilder<int>(
//stream:fire, // a Stream<int> or null
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Select lot');
case ConnectionState.waiting:
return Text('Awaiting bids...');
case ConnectionState.active:
return Text('$${snapshot.data}');
case ConnectionState.done:
return Text('$${snapshot.data} (closed)');
}
return null; // unreachable
},
);
The initial snapshot data can be controlled by specifying initialData. This should be used to ensure that the first frame has the expected value, as the builder will always be called before the stream listener has a chance to be processed.
Providing this value (presumably obtained synchronously somehow when the Stream was created) ensures that the first frame will show useful data. Otherwise, the first frame will be built with the value null, regardless of whether a value is available on the stream: since streams are asynchronous, no events from the stream can be obtained before the initial build.
如上所述,您只需将代码置于 Connection.Active 状态即可。见下文:
StreamBuilder<QuerySnapshot>(
stream: historicModel.query.snapshots(),
builder: (context, stream){
if (stream.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (stream.hasError) {
return Center(child: Text(stream.error.toString()));
} else if(stream.connectionState == ConnectionState.active){
//place your code here. It will prevent double data call.
}