flutter 调用build(),但在热重新加载之前不会更新显示

11dmarpk  于 2023-05-23  发布在  Flutter
关注(0)|答案(3)|浏览(144)

我有一个异步流,每秒生成连续的整数:

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键执行热重新加载,则显示更新。
为什么?

如何复制:

  1. flutter run -d macos启动应用程序。
    1.即使每秒调用build(),显示屏仍保持全黑。

1.如果按r键执行热重新加载,则显示更新。

完整代码如下:

DartPad

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);
  }
}
xienkqul

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

  • 与框架中的其他小部件一样,这个小部件期望在传递到这里之后,孩子列表不会发生变化。更多详情请参阅SliverChildListDelegate.children上的文档。*

嵌入式链接显示

  • 此外,Flutter中的Widget是不可变的,因此直接修改子对象(如someWidget.children.add(...))或将原始列表值的引用传递给children参数将导致不正确的行为。每当修改子列表时,都应该提供一个新的列表对象。*
xxb16uws

xxb16uws2#

使用builder代替

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: 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({Key key}) : super(key: key);

  @override
  State<W> createState() => _WState();
}

class _WState extends State<W> {
  StreamController<int> streamController;
  List<String> list = []; // change the string to any data object

  @override
  void initState() {
    super.initState();

    streamController = StreamController<int>();

    Stream<int> f() async* {
      for (int i = 0; i < 100; ++i) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
      }
    }

    f().listen((d) {
      setState(() {
        list.add(d.toString());
      });
    });
  }

  @override
  void dispose() {
    streamController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('build(): $itemCount');
    return ListView.builder(
      itemCount: itemCount,
      itemBuilder: (BuildContext context, int index) {
        final listItem = list[index];
        return Card(
          key: Key('card_$index'),
          child: ListTile(
            title: Text(listItem),
          ),
        );
      },
    );
  }
}
pgpifvop

pgpifvop3#

你不应该缓存小部件。首选的方法是存储状态-在本例中是接收到的字符串列表。在构建中,然后动态地创建小部件。

class _WState extends State<W> {
  final strings = <String>[];

  @override
  void initState() {
    super.initState();

    Stream<int> f() async* {
      for (int i = 0; i < 100; ++i) {
        await Future.delayed(const Duration(seconds: 1));
        yield i;
      }
    }

    f().listen((d) {
      setState(() {
        strings.add(d.toString());
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
        children: strings
            .map<Widget>((s) => ListTile(
                  title: Text(s),
                ))
            .toList());
  }
}

相关问题