【问题标题】:Avoid ListView's unwanted refresh避免 ListView 不必要的刷新
【发布时间】:2021-11-02 00:31:39
【问题描述】:

如下动画所示,当我点击 StreamBuilder() 正在查询的列表项之一时,它会在右侧较暗的容器中显示项目数据(它始终是 Instance of ' _JsonQueryDocumentSnapshot')。但同时在每次点击时,整个列表都在刷新自己,我相信这不是很划算。

如何避免这种不必要的刷新? 也欢迎带有 GetX 状态管理依赖的答案。

class Schedule extends StatefulWidget {
  @override
  _ScheduleState createState() => _ScheduleState();
}

class _ScheduleState extends State<Schedule> {

  final FirebaseFirestore _db = FirebaseFirestore.instance;
  final DateTime _yesterday = DateTime.now().subtract(Duration(days: 1));

  var _chosenData;

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Expanded(
          child: StreamBuilder<QuerySnapshot>(
            stream: _db.collection('Schedule').where('date', isGreaterThan: _yesterday).limit(10).orderBy('date').snapshots(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.active) {
                return ListView.builder(
                  itemCount: snapshot.data!.docs.length,
                  itemBuilder: (context, index) {
                    var data = snapshot.data!.docs[index];
                    return ListTile(
                      leading: Icon(Icons.person),
                      title: Text(data['project'], style: TextStyle(fontWeight: FontWeight.bold)),
                      subtitle: Text(data['parkour']),
                      onTap: () {
                        setState(() {_chosenData = data;});
                      },
                    );
                  },
                );
              } else {
                return Center(child: CupertinoActivityIndicator());
              }
            },
          ),
        ),
        VerticalDivider(),
        Expanded(
          child: Container(
            alignment: Alignment.center,
            color: Colors.black26,
            child: Text('$_chosenData'),
          ),
        ),
      ],
    );
  }
}

【问题讨论】:

    标签: flutter google-cloud-firestore listviewitem stream-builder flutter-getx


    【解决方案1】:

    对我来说,最简单的解决方案就是使其无状态并使用 Getx 类。

    class ScheduleController extends GetxController {
      var chosenData;
    
      void updateChosenData(var data) {
        chosenData = data;
        update();
      }
    }
    

    您的 Schedule.dart 看起来像这样:

    class Schedule extends StatelessWidget {
      final FirebaseFirestore _db = FirebaseFirestore.instance;
      final DateTime _yesterday = DateTime.now().subtract(Duration(days: 1));
    
      @override
      Widget build(BuildContext context) {
        final controller = Get.put(ScheduleController());
        return Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Expanded(
              child: StreamBuilder<QuerySnapshot>(
                stream: _db
                    .collection('Schedule')
                    .where('date', isGreaterThan: _yesterday)
                    .limit(10)
                    .orderBy('date')
                    .snapshots(),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.active) {
                    return ListView.builder(
                      itemCount: snapshot.data!.docs.length,
                      itemBuilder: (context, index) {
                        var data = snapshot.data!.docs[index];
                        return ListTile(
                          leading: Icon(Icons.person),
                          title: Text(data['project'],
                              style: TextStyle(fontWeight: FontWeight.bold)),
                          subtitle: Text(data['parkour']),
                          onTap: () => controller.updateChosenData(data), // calls method from GetX class
                        );
                      },
                    );
                  } else {
                    return Center(child: CupertinoActivityIndicator());
                  }
                },
              ),
            ),
            VerticalDivider(),
            Expanded(
              child: Container(
                alignment: Alignment.center,
                color: Colors.black26,
                child: GetBuilder<ScheduleController>(
                  builder: (controller) => Text('${controller.chosenData}'), // only this rebuilds
                ),
              ),
            ),
          ],
        );
      }
    }
    

    这样listview.builder 永远不会重建,当您选择不同的ListTile 时,只会重建直接位于GetBuilder 内的文本小部件。

    【讨论】:

      【解决方案2】:

      调用setState() 会通知框架Schedule 的状态已更改,这会导致小部件以及StreamBuilder 的重建。

      您可以将流逻辑移动到小部件树的上层。因此,setState() 不会触发 StreamBuilder 的重建。

      class ParentWidget extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return StreamBuilder<QuerySnapshot>(
            stream: FirebaseFirestore.instance
                .collection('Schedule')
                .where(
                  'date',
                  isGreaterThan: DateTime.now().subtract(Duration(days: 1)),
                )
                .limit(10)
                .orderBy('date')
                .snapshots(),
            builder: (context, snapshot) {
              return Schedule(snapshot: snapshot); // Pass snapshot to Schedule
            },
          );
        }
      }
      

      另一种方法是在调用一次的initState() 中使用Stream.listen。这样,每次调用 setState() 时都不会订阅您的流。

      ...
      
      late StreamSubscription<QuerySnapshot> _subscription;
      
      @override
      void initState() {
        _subscription = _db
          .collection('Schedule')
          .where('date', isGreaterThan: _yesterday)
          .limit(10)
          .orderBy('date')
          .snapshots()
          .listen((QuerySnapshot querySnapshot) {
            setState(() {
              _querySnapshot = querySnapshot;
            });
          });
      
        super.didChangeDependencies();
      }
      
      @override
      void dispose() {
        _subscription.cancel(); // Cancel the subscription
        super.dispose();
      }
      
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-29
        • 1970-01-01
        • 1970-01-01
        • 2020-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多