【问题标题】:how can i update list item without rebuilding whole list如何在不重建整个列表的情况下更新列表项
【发布时间】:2021-02-08 02:24:02
【问题描述】:

有人问我这个问题,我想让答案易于理解

如何在不重建整个列表的情况下更新列表项?

一个典型的用例(我将在下面的答案中重现)

可能是一个ListView,它接收一个Widgets 的List,可能来自一个API

【问题讨论】:

    标签: list flutter listview dart key


    【解决方案1】:

    List 中的Widgets 提供Key 将阻止这些

    不会从小部件树中删除,因此会被不必要地重建

    你可以尝试自己运行这个app in dartpad

    注意终端中的日志;

    代码贴在下面

    import 'dart:async';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      final _navigatorKey = GlobalKey<NavigatorState>();
      FakeApi _api;
    
      @override
      void initState() {
        _api = FakeApi(_navigatorKey);
        super.initState();
      }
    
      @override
      void dispose() {
        _api?.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) => MaterialApp(
            navigatorKey: _navigatorKey,
            home: MyInheritedWidget(
              api: _api,
              child: const MyHomePage(),
            ),
          );
    }
    
    class MyInheritedWidget extends InheritedWidget {
      const MyInheritedWidget({
        @required Widget child,
        @required this.api,
      }) : super(
              key: const Key('MyInheritedWidget'),
              child: child,
            );
    
      final FakeApi api;
    
      static MyInheritedWidget of(BuildContext context) =>
          context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
    
      @override
      bool updateShouldNotify(MyInheritedWidget old) => false;
    }
    
    class MyHomePage extends StatelessWidget {
      const MyHomePage() : super(key: const Key('MyHomePage'));
      @override
      Widget build(BuildContext context) => Builder(
            builder: (context) => Scaffold(
              backgroundColor: Colors.blueGrey,
              body: StreamBuilder<List<ItemWidget>>(
                stream: MyInheritedWidget.of(context).api.stream,
                initialData: [],
                builder: (context, list) => list.hasError
                    ? const Center(child: Icon(Icons.error))
                    : !list.hasData
                        ? const Center(child: CircularProgressIndicator())
                        : list.data.isEmpty
                            ? const Center(
                                child: Text(
                                'the list is empty',
                                textScaleFactor: 1.5,
                              ))
                            : ListView.builder(
                                itemCount: list.data.length,
                                itemBuilder: (context, index) => list.data[index],
                              ),
              ),
              floatingActionButton: FloatingActionButton(
                backgroundColor: Colors.white,
                child: const Icon(Icons.add, color: Colors.blueGrey),
                onPressed: MyInheritedWidget.of(context).api.add,
              ),
            ),
          );
    }
    
    class ItemWidget extends StatelessWidget {
      ItemWidget(this.text) : super(key: UniqueKey());
      final String text;
    
      @override
      Widget build(BuildContext context) {
        print('Item $text is building');
        return Center(
          child: Container(
            padding: const EdgeInsets.only(bottom: 20),
            width: MediaQuery.of(context).size.width * .5,
            child: Card(
              elevation: 10,
              child: ListTile(
                leading: GestureDetector(
                  child: const Icon(Icons.edit),
                  onTap: () => MyInheritedWidget.of(context).api.edit(key),
                ),
                trailing: GestureDetector(
                  child: const Icon(Icons.delete),
                  onTap: () => MyInheritedWidget.of(context).api.delete(key),
                ),
                title: Text(text),
              ),
            ),
          ),
        );
      }
    }
    
    class ItemDialog extends StatefulWidget {
      const ItemDialog({this.text});
      final String text;
    
      @override
      _ItemDialogState createState() => _ItemDialogState();
    }
    
    class _ItemDialogState extends State<ItemDialog> {
      TextEditingController _controller;
    
      @override
      void initState() {
        _controller = TextEditingController()..text = widget.text;
        super.initState();
      }
    
      @override
      void dispose() {
        _controller?.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) => AlertDialog(
            content: Stack(
              alignment: Alignment.center,
              children: <Widget>[
                Container(
                  width: double.infinity,
                  height: MediaQuery.of(context).size.height * .3,
                  child: Center(
                    child: TextField(
                      autofocus: true,
                      controller: _controller,
                    ),
                  ),
                ),
              ],
            ),
            actions: <Widget>[
              IconButton(
                onPressed: () => Navigator.pop(context, _controller.text ?? ''),
                icon: const Icon(Icons.save),
              ),
            ],
          );
    }
    
    class FakeApi {
      FakeApi(this.navigatorKey);
      final GlobalKey<NavigatorState> navigatorKey;
      final _list = <ItemWidget>[];
      StreamController<List<ItemWidget>> _controller;
      StreamController<List<ItemWidget>> get _c =>
          _controller ??= StreamController<List<ItemWidget>>.broadcast();
      Stream<List<ItemWidget>> get stream => _c.stream;
      void dispose() => _controller?.close();
    
      void delete(Key key) {
        _list.removeWhere((ItemWidget item) => item.key == key);
        _c.sink.add(_list);
      }
    
      void edit(Key key) async {
        final _item = _list.firstWhere((ItemWidget item) => item.key == key);
        final _index = _list.lastIndexOf(_item);
        final _text = await showDialog<String>(
          context: navigatorKey.currentState.overlay.context,
          builder: (context) => ItemDialog(
            text: _item.text,
          ),
        );
        _list.removeAt(_index);
        _list.insert(_index, ItemWidget(_text));
        _c.sink.add(_list);
      }
    
      void add() async {
        final _text = await showDialog<String>(
          context: navigatorKey.currentState.overlay.context,
          builder: (context) => ItemDialog(),
        );
        _list.add(ItemWidget(_text));
        _c.sink.add(_list);
      }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-05
      • 2017-06-22
      • 1970-01-01
      • 1970-01-01
      • 2013-01-20
      • 1970-01-01
      相关资源
      最近更新 更多