• flutter 切换tab后保留tab状态


    前言

    最近在用flutter写一个小项目,在写主页面(底部导航栏+子页面)时遇到的一个问题:当点击底部item切换到另一页面, 再返回此页面时会重走它的initState方法(我们一般在initState中发起网络请求,或者初始化的操作),导致不必要的开销

    根据Tab动态加载页面

    我们先定义两个页面PageA和PageB

    class PageA extends StatefulWidget {
      _PageAState createState() => _PageAState();
    }
    
    class _PageAState extends State<PageA> {
      @override  
      void initState() {
        super.initState();
        print("pageA init state");
      }
    
      @override  
      Widget build(BuildContext context) {
        return Container(
          color: Colors.orangeAccent,
          child: Center(
            child: Text("page A"),
          ),
        );
      }
    }
    
    class PageB extends StatefulWidget {
      _PageBState createState() => _PageBState();
    }
    
    class _PageBState extends State<PageB> {
       @override 
      void initState() {
        super.initState();
        print("pageB init state");
      }
      
       @override 
      Widget build(BuildContext context) {
        return Container(
          color: Colors.blueAccent,
          child: Center(
            child: Text("page B"),
          ),
        );
      }
    }
    

    定义Tab主页面

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatefulWidget {
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      int _tabIndex = 0;
    
      List<Widget> _tabWidget = [
        PageA(),
        PageB()
      ];
    
       @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'tab demo',
          home: Scaffold(
            bottomNavigationBar: BottomNavigationBar(
              items: _createBottomItems(),
              currentIndex: this._tabIndex,
              onTap: (index) {
                setState(() {
                  this._tabIndex = index;
                });
              },
            ),
            body: this._tabWidget.elementAt(this._tabIndex),
          ),
        );
      }
    }
    
    // 创建底部导航item
    List<BottomNavigationBarItem> _createBottomItems() {
      return [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          title: Text("首页")
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.insert_emoticon),
          title: Text("我的")
        )
      ];
    
    

    运行后发现,每次切换tab都会调用initState,这显然不符合我们的正常的需求,有下面两种解决方式

    IndexedStack

    IndexedStack可以控制子元素的显示和隐藏,并且会缓存所有的元素,不会每次都重新创建子元素

      @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'tab demo',
        home: Scaffold(
          bottomNavigationBar: BottomNavigationBar(
            items: _createBottomItems(),
            currentIndex: this._tabIndex,
            onTap: (index) {
              setState(() {
                this._tabIndex = index;
              });
            },
          ),
          body: IndexedStack(
            children: this._tabWidget,
            index: this._tabIndex,
          )
        ),
      );
    }
    

    运行后发现还是有个问题,IndexedStack在初始化的时候会初始化所有的子元素,pageA和pageB的initState会同时调用,这明显还是不符合我们的需求

    正确来说应该是切换到具体页面的时候才进行初始化,而不是一开始就加载所有的页面的数据,避免资源浪费

    PageView + AutomaticKeepAliveClientMixin

    使用PageView支持多个view切换,并且不会一次加载完所有的页面

    PageController _pageController;
      
       @override 
      void initState() {
        super.initState();
        this._pageController =PageController(initialPage: this._tabIndex, keepPage: true);
      }
    
      @override  
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'tab demo',
          home: Scaffold(
            bottomNavigationBar: BottomNavigationBar(
              items: _createBottomItems(),
              currentIndex: this._tabIndex,
              onTap: (index) {
                setState(() {
                  this._tabIndex = index;
                  _pageController.jumpToPage(index);
                });
              },
            ),
            body: PageView(
              children: this._tabWidget,
              controller: _pageController,
            ),
          ),
        );
      }
    }
    

    使用PageView可以正常切换,但是每次切换Tab的时候还是会重复调用initState,我们还需要在子页面实现AutomaticKeepAliveClientMixin

    class _PageAState extends State<PageA> with AutomaticKeepAliveClientMixin {
      @override  
      bool get wantKeepAlive => true;
      
      ...
    }
    class _PageBState extends State<PageB> with AutomaticKeepAliveClientMixin {
      @override
      bool get wantKeepAlive => true;
    
      ...
    }
    

    实现了AutomaticKeepAliveClientMixin就不会每次切换Tab都调用initState了,这也是google推荐的方式

    最后发现PageView可以左右滑动切换,这个可以通过设置physics为NeverScrollableScrollPhysics()来禁止滑动

    PageView(
      children: this._tabWidget,
      controller: _pageController,
      physics: NeverScrollableScrollPhysics(),
    )
    

    参考来源:https://blog.bombox.org/2019-02-23/flutter-tab-keep-alive/#more

  • 相关阅读:
    C# 串口调试助手源码
    C# 中 textBox 侧面滑条 属性
    C# 中 comboBox 禁止输入
    VS2015 下载 破解
    中国移动OnetNet云平台 GET指令使用
    JSON JsonArray和JsonObject学习资料
    sublime 添加 ConvertToUTF-8
    sublime 添加 颜色插件 colorcoder
    线程池
    爬虫中基本的多线程
  • 原文地址:https://www.cnblogs.com/qqcc1388/p/11678085.html
Copyright © 2020-2023  润新知