我有一个异步流,每秒生成连续的整数:
Stream<int> f() async* {
for (int i = 0; i < 100; ++i) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
我有一个状态List<Widget> l = [];
,并希望通过使用流每秒向它附加一个Card
。
@override
void initState() {
super.initState();
f().listen((d) {
setState(
() => this.l.add(Card(child: ListTile(title: Text(d.toString())))));
});
}
下面是build()
方法:
@override
Widget build(BuildContext context) {
print('build(): ${this.l.length}');
return ListView(children: this.l);
}
当我通过flutter run -d macos
运行程序时(即macOS桌面应用程序),显示器根本没有更新,尽管build()
被调用,this.l
每秒更新一次。控制台输出为
flutter: build(): 0
flutter: build(): 1
flutter: build(): 2
flutter: build(): 3
flutter: build(): 4
flutter: build(): 5
...
如果我按r键执行热重新加载,则显示更新。
为什么?
如何复制:
flutter run -d macos
启动应用程序。
1.即使每秒调用build()
,显示屏仍保持全黑。
1.如果按r键执行热重新加载,则显示更新。
完整代码如下:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.blueGrey,
useMaterial3: true),
home: Scaffold(body: W()),
debugShowCheckedModeBanner: false,
);
}
}
class W extends StatefulWidget {
const W({
super.key,
});
@override
State<W> createState() => _WState();
}
class _WState extends State<W> {
List<Widget> l = [];
@override
void initState() {
super.initState();
Stream<int> f() async* {
for (int i = 0; i < 100; ++i) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
f().listen((d) {
setState(
() => this.l.add(Card(child: ListTile(title: Text(d.toString())))));
});
}
@override
Widget build(BuildContext context) {
print('build(): ${this.l.length}');
return ListView(children: this.l);
}
}
3条答案
按热度按时间xienkqul1#
尝试将
ListView(children: this.l)
更改为ListView(children: List.of(this.l))
ListView.builder(itemCount: l.length, itemBuilder: (_, i) => l[i])
这是因为
ListView
小部件。ListView
喜欢静态小部件,它只接收开始参数列表l
,即使调用setState也不会更新。更严格的解释
The official document of
ListView()
constructor说嵌入式链接显示
Widget
是不可变的,因此直接修改子对象(如someWidget.children.add(...)
)或将原始列表值的引用传递给children参数将导致不正确的行为。每当修改子列表时,都应该提供一个新的列表对象。*xxb16uws2#
使用builder代替
pgpifvop3#
你不应该缓存小部件。首选的方法是存储状态-在本例中是接收到的字符串列表。在构建中,然后动态地创建小部件。