zoukankan      html  css  js  c++  java
  • 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开发过程中必须对控件非常熟悉,否则稍微不注意用错了控件就会导致视图重建,很多时候定位都不好定位!

  • 相关阅读:
    JS 删除web sql 数据表
    JS 新建web sql 数据表
    JS 更新web sql 数据表的数据
    JS 删除一行web sql 数据表的数据
    JS 向web sql数据表插入数据
    JS 打开or连接web sql数据库
    JS 获取表单数据存入数组
    JS 限制小数点位数
    JS 通过id获取DOM对象--减少代码
    mvc “System.NullReferenceException”类型的异常在 App_Web_zo44wdaq.dll 中发生,但未在用户代码中进行处理 其他信息: 未将对象引用设置到对象的实例。
  • 原文地址:https://www.cnblogs.com/lindeer/p/12012472.html
Copyright © 2011-2022 走看看