• flutter: 视图常驻


    开发中遇到一个问题,譬如有如下视图:

    class _PageState extends State<_HomePage> {
      final _controller = PageController(initialPage: 0, keepPage: true);
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
          body: PageView(
            controller: _controller,
            physics: NeverScrollableScrollPhysics(),
            children: <Widget>[
              Container(color: Colors.redAccent,),
              Container(color: Colors.blueAccent,),
            ],
          ),
        );
      }
    }
    

    PageView相当于Android的ViewPager, 调用PageController.jumpToPage(index)没有问题,如期滑动到指定页面;但是将PageView的children改成如下形式:

            children: <Widget>[
              Builder(builder: (ctx) {
                print('jumptTo111');
                return Container(color: Colors.redAccent,);
              },),
              Container(color: Colors.blueAccent,),
            ],
    

    发现每次滑动到第一个页面都会打印jumptTo111, 这里存在一个问题: Builder的builder方法是在build方法中调用的,意味着Widget.build(BuildContext)被调用了,因而Container(color: Colors.redAccent,)重建了(注意Builder对象并没有重建)。把Builder换成别的控件也一样,关键是都会走这个控件对象的build方法。

    这并不是我们期望的!PageView一般都是比较大的根节点页面的容器,如果每次滑动都要重建页面那必然引起性能问题!像这种情况我们必然是不希望重建页面的。这真是有点坑,因为明明已经给PageController设置了keepPage: true属性,竟然不好使?!不知道 这算不是算是控件的bug,反正在flutter的v1.8.0上总是重建的。后来才搞明白keepPage这个属性表示的意思是视图被重建后记住滑动到的是第几页,和我们的意图一点关系没有。。。

    后来发现ListView也有类似的问题:它接收一个IndexedWidgetBuilder itemBuilder参数来创建列表项的视图。如果有1-100的列表项,从1滑到100没问题,再从100滑到1还是重建了列表项视图。某些情况下创建好的视图对象只要数据没有变更, 我们当然是不希望再去创建视图,ListView是所有界面开发最重要的视图控件之一, 如果要使它的列表项视图常驻这可怎么解。。。

    不管原理怎样,现在需要的效果是使视图常驻,立马找到了KeepAliveAutomaticKeepAlive控件,然而让人费解的是这两个控件必须声明祖先节点SliverWithKeepAliveWidget,否则就崩溃。好在找到了这篇文章,核心意思是必须通过StatefulWidget的状态对象混入AutomaticKeepAliveClientMixin 这样能够达到视图常驻的目的,形如

    class _PageState extends State<_HomePage> with AutomaticKeepAliveClientMixin<_HomePage> {
      @override
      bool get wantKeepAlive => true;
      
      @override
      Widget build(BuildContext context) {
        super.build(context);
        return Scaffold(...
        );
      }
    }
    

    但我们平常开发肯定不希望再耦合这坨逻辑 所以最好封装成能直接拿来用的控件,因为KeepAlive已经被用了就起名AliveKeeper吧, 于是有

    import 'package:flutter/widgets.dart';
    
    class AliveKeeper extends StatefulWidget {
      final Widget child;
    
      const AliveKeeper({Key key, @required this.child}) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => _AliveState();
    }
    
    class _AliveState extends State<AliveKeeper>
        with AutomaticKeepAliveClientMixin<AliveKeeper> {
    
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(BuildContext context) {
        super.build(context);
    
        return widget.child;
      }
    }
    

    对应改成:

            children: <Widget>[
              AliveKeeper(child: Builder(builder: (ctx) {
                print('jumptTo111');
                return Container(color: Colors.redAccent,);
              },),),
              Container(color: Colors.blueAccent,),
            ],
    

    builder方法果然不再被调用,而这样一个小巧而实用的控件正是我们需要的~

    这种做法肯定不能适用所有情况,所以一定要分清哪种情况下需要让视图对象常驻。另外一个让人忧心的是flutter开发过程中必须对控件非常熟悉,否则稍微不注意用错了控件就会导致视图重建,很多时候定位都不好定位!

  • 相关阅读:
    memcache和memcached区别
    C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论
    高手问答精选:Go 语言 —— 云计算时代的 C 语言(类似于一个FAQ)
    Delphi XE5 Android 调用手机震动(通过JObject测试是否支持震动)
    Delphi Android 将Google ZXing 整合(调用Jar文件)
    Delphi Android ActivityManager(提供了接口, 利用它可以方便的对Memory, Processes, Task, Service 等进行管)
    Azure 云 Web 应用程序
    C#由变量捕获引起对闭包
    React.js学习
    Web API
  • 原文地址:https://www.cnblogs.com/lindeer/p/12012472.html
Copyright © 2020-2023  润新知