zoukankan      html  css  js  c++  java
  • 【Flutter】BottomNavigationBar切换页面被重置问题(保存状态)

    网上找了一圈说保持状态需要子页面mixin AutomaticKeepAliveClientMixin,然后重写
    @override
      bool get wantKeepAlive => true;

    但发现需要配合其他组件,不是随便mixin就有用的,尝试几种写法总结BottomNavigationBar+List+AutomaticKeepAliveClientMixin是没有用的

    1. 首先尝试BottomNavigationBar+List实现的页面切换保持状态,一般刚开始学都会这么写:
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) => MaterialApp(
            title: "demo",
            home: MainPage(),
          );
    }
    
    class MainPage extends StatefulWidget {
      @override
      StatecreateState() => MainPageState();
    }
    
    class MainPageState extends State{
      int _currentIndex;
      List_pages;
    
      @override
      void initState() {
        super.initState();
        _currentIndex = 0;
        _pages = List()..add(FirstPage("第一页"))..add(SecondPage("第二页"))..add(ThirdPage("第三页"));
      }
    
      @override
      Widget build(BuildContext context) => Scaffold(
            body: _pages[_currentIndex],
            bottomNavigationBar: BottomNavigationBar(
              items: getItems(),
              currentIndex: _currentIndex,
              onTap: onTap,
            ),
          );
    
      ListgetItems() {
        return [
          BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("Home")),
          BottomNavigationBarItem(icon: Icon(Icons.adb), title: Text("Adb")),
          BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("Person"))
        ];
      }
    
      void onTap(int index) {
        setState(() {
          _currentIndex = index;
        });
      }
    }
    

      

    子页面代码,三个界面一样:

    class FirstPage extends StatefulWidget {
      String _title;
    
      FirstPage(this._title);
    
      @override
      StatecreateState() => FirstPageState();
    }
    
    class FirstPageState extends Statewith AutomaticKeepAliveClientMixin{
      int _count = 0;
    
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget._title),
          ),
          body: Center(
            child: Text(widget._title + ":点一下加1:$_count"),
          ),
          floatingActionButton: FloatingActionButton(
              heroTag: widget._title, child: Icon(Icons.add), onPressed: add),
        );
      }
    
      void add() {
        setState(() {
          _count++;
        });
      }
    }
    

      

    结果无法实现保持页面

     
    第一个.

    2.第二种BottomNavigationBar+PageView,与android的ViewPager类似,界面小改动一下,添加一个按钮,点击跳转到一个新的界面


     

    代码如下:

    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) => MaterialApp(
            title: "demo",
            home: MainPage(),
          );
    }
    
    class MainPage extends StatefulWidget {
      @override
      StatecreateState() => MainPageState();
    }
    
    class MainPageState extends State{
      int _currentIndex;
      List_pages;
      PageController _controller;
    
      @override
      void initState() {
        super.initState();
        _currentIndex = 0;
        _pages = List() ..add(FirstPage("第一页"))  ..add(SecondPage("第二页")) ..add(ThirdPage("第三页"));
        _controller = PageController(initialPage: 0);
      }
    
      @override
      void dispose() {
        super.dispose();
        _controller.dispose();
      }
    
      @override
      Widget build(BuildContext context) => Scaffold(
            body: PageView.builder(
                physics: NeverScrollableScrollPhysics(),//viewPage禁止左右滑动
                onPageChanged: _pageChange,
                controller: _controller,
                itemCount: _pages.length,
                itemBuilder: (context, index) => _pages[index]),
            bottomNavigationBar: BottomNavigationBar(
              items: getItems(),
              currentIndex: _currentIndex,
              onTap: onTap,
            ),
          );
    
      ListgetItems() {
        return [
          BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("Home")),
          BottomNavigationBarItem(icon: Icon(Icons.adb), title: Text("Adb")),
          BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("Person"))
        ];
      }
    
      void onTap(int index) {
        _controller.jumpToPage(index);
      }
    
      void _pageChange(int index) {
        setState(() {
          if (index != _currentIndex) {
            _currentIndex = index;
          }
        });
      }
    }
    

      

    子界面:

    class FirstPage extends StatefulWidget {
      String _title;
    
      FirstPage(this._title);
    
      @override
      StatecreateState() => FirstPageState();
    }
    
    class FirstPageState extends Statewith AutomaticKeepAliveClientMixin {
      int _count = 0;
    
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget._title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children:[
                Text(widget._title + ":点一下加1:$_count"),
                MaterialButton(
                    child: Text("跳转"),
                    color: Colors.pink,
                    onPressed: () => Navigator.push(context,
                        MaterialPageRoute(builder: (context) => NewPage())))
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
              heroTag: widget._title, child: Icon(Icons.add), onPressed: add),
        );
      }
    
      void add() {
        setState(() {
          _count++;
        });
      }
    }
    

      

    需要跳转的一个界面:

    class NewPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) => Scaffold(
            appBar: AppBar(
              title: Text("新的界面"),
            ),
            body: Center(
              child: Text("我是一个新的界面"),
            ),
          );
    }
     
    2.

    猛一看效果出来了,左右切换界面没有问题,结果跳转新界面时又出现新问题,当第一页跳转新的界面再返回,再切第二、三页发现重置了,再切回第一页发现页被重置了。

    发生这种情况需要在重写Widget build(BuildContext context)时调用下父类build(context)方法,局部代码:

    @override
      Widget build(BuildContext context) {
        //在这边加上super.build(context);
        super.build(context);
        return Scaffold(
          appBar: AppBar(
            title: Text(widget._title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children:[
                Text(widget._title + ":点一下加1:$_count"),
                MaterialButton(
                    child: Text("跳转"),
                    color: Colors.pink,
                    onPressed: () => Navigator.push(context,
                        MaterialPageRoute(builder: (context) => NewPage())))
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
              heroTag: widget._title, child: Icon(Icons.add), onPressed: add),
        );
      }
     
    第三个.

    这种布局样式网上还有一种用的比较多的是BottomNavigationBar+Stack+Offstage,这边就不贴出来了

    • 最近发现最好的实现是BottomNavigationBar+TabBarView,
      BottomNavigationBar+ PageView的话如果给PageView加翻页动画话的第一页直接切换第三页,翻页动画不是很流畅,因为是第一页翻到第三页,而TabBarView切换起来就非常完美
    class IndexPage extends StatefulWidget {
      @override
      _IndexPageState createState() => _IndexPageState();
    }
    
    class _IndexPageState extends Statewith SingleTickerProviderStateMixin {
      TabController _controller;
      ListbottomTabs;
      int _currentIndex = 0;
    
      @override
      void initState() {
        super.initState();
        bottomTabs = List()
          ..add(_buildBottomNavItem("assets/images/index_phone_unselect.png",
              "assets/images/index_phone_select.png", "首页"))
          ..add(_buildBottomNavItem("assets/images/index_package_unselect.png",
              "assets/images/index_package_select.png", "包啊"))
          ..add(_buildBottomNavItem("assets/images/index_found_unselect.png",
              "assets/images/index_found_select.png", "发现"))
          ..add(_buildBottomNavItem("assets/images/index_person_unselect.png",
              "assets/images/index_person_select.png", "我的"));
        _controller = TabController(length: bottomTabs.length, vsync: this);
      }
    
      @override
      void dispose() {
        super.dispose();
        _controller.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          bottomNavigationBar: BottomNavigationBar(
              fixedColor: Color(0xFF333333),
              type: BottomNavigationBarType.fixed,
              currentIndex: _currentIndex,
              onTap: _bottomNavBarClick,
              items: bottomTabs),
          body: TabBarView(
              physics: NeverScrollableScrollPhysics(), //禁止滚动
              controller: _controller,
              children: [
                PhonePage(),
                PackagePage(),
                FoundPage(),
                PersonalPage(),
              ]),
        );
      }
    
      _bottomNavBarClick(index) {
        setState(() {
          _currentIndex = index;
        });
        _controller.animateTo(_currentIndex);
      }
    
      BottomNavigationBarItem _buildBottomNavItem(
          String icon, String activeIcon, String name) {
        double height = 28.0, width = 28.0;
        return BottomNavigationBarItem(
            icon: Image.asset(icon, height: height,  width),
            activeIcon: Image.asset(activeIcon, height: height,  width),
            title: Text(name));
      }
    }
    

      

    最后像这种多页面使用FloatingActionButton,用它跳转新界面是一定要设置heroTag,要不然跳转会黑屏报错

     
  • 相关阅读:
    Git一些简单但非常重要并常用的操作命令
    利用fastjson解析json并通过js&ajax实现页面的无跳转刷新
    利用Graphviz绘制逻辑关系依赖图
    oracle导入.dmp文件
    Linux查找当前目录5天的文件并打包
    Web安全相关(三):开放重定向(Open Redirection)
    Web安全相关(二):跨站请求伪造(CSRF/XSRF)
    Web安全相关(一):跨站脚本攻击(XSS)
    密码学应用(DES,AES, MD5, SHA1, RSA, Salt, Pkcs8)
    行为驱动开发(BDD)实践示例
  • 原文地址:https://www.cnblogs.com/sangwl/p/10873628.html
Copyright © 2011-2022 走看看