Flutter:如何防止在提供者更改时重建整个列表

问题描述 投票:0回答:1

我有一个 ListView.builder,它使用一个简单提供者的消费者。 每个项目都需要异步加载一些东西,所以我正在使用 FutureBuilder。

当我向列表中添加一个项目时,整个列表被重建。这是有道理的,因为它被包裹在消费者中,但不幸的是,这意味着现有的项目需要重新运行未来,尽管这些项目实际上并没有改变。

class ListWithButton extends StatelessWidget {
  const ListWithButton({super.key});

  @override
  Widget build(BuildContext context) {
    return Consumer<MyProvider>(
      builder: (_, provider, __) => Column(
        children: [
          ElevatedButton(
            onPressed: () {
              provider.addItem();
            },
            child: const Text("Add Item"),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: provider.items.length,
              itemBuilder: (_, index) => FutureBuilder<Color>(
                future: provider.loadSomething(index),
                builder: (_, snapshot) => ListTile(
                  leading: Container(
                    width: 30,
                    height: 30,
                    color: snapshot.connectionState == ConnectionState.done
                        ? snapshot.data!
                        : Colors.red,
                  ),
                  title: Text(index.toString()),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class MyProvider extends ChangeNotifier {
  final List<int> _items = [];

  List<int> get items => _items;

  void addItem() {
    _items.add(_items.length);
    notifyListeners();
  }

  Future<Color> loadSomething(int i) async {
    await Future.delayed(Duration(seconds: 1));
    return Colors.green;
  }
}

有没有办法只重建 ListView 中需要构建的项目(即新项目)?

flutter dart listview provider consumer
1个回答
0
投票

您不一定能阻止列表在添加新子项时重建其子小部件,但您可以通过在构建方法之外初始化和存储它们来防止 Futures 被重新初始化,这在 FutureBuilder 的文档中推荐:

未来必须早先获得,例如在 State.initState、State.didUpdateWidget 或 State.didChangeDependencies 期间。在构造 FutureBuilder 时,不得在 State.build 或 StatelessWidget.build 方法调用期间创建它。如果 Future 与 FutureBuilder 同时创建,那么每次 FutureBuilder 的父级重建时,都会重新启动异步任务。

为此,您只需更改 MyProvider,如下所示:

class MyProvider extends ChangeNotifier {
  final List<int> _items = [];
  final Map<int, Future<Color>> _futureColors = {};

  List<int> get items => _items;

  void addItem() {
    _items.add(_items.length);
    notifyListeners();
  }

  Future<Color>? loadSomething(int i) {
    if (!_futureColors.containsKey(i)) {
      _futureColors[i] = Future.delayed(
        const Duration(seconds: 1),
        () => Colors.green,
      );
    }

    return _futureColors[i];
  }
}

在这里,我们只在第一次使用给定索引调用

loadSomething
时创建一个新的未来——我们将该未来存储在一个 Map 中,并且在以后调用
loadSomething
时,我们返回已经为该索引初始化的 Future。 (我使用
Map
而不是
List
的原因是无法保证该函数将接收有效的列表索引)。

还要注意

loadSomething
不再是
async
函数
——这很重要,因为我们希望未来同步返回到
FutureBuilder
。如果我们异步返回 future,您可能会注意到,当您添加新元素时,有时 List 的元素仍然闪烁红色。

© www.soinside.com 2019 - 2024. All rights reserved.