【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: width),
        activeIcon: Image.asset(activeIcon, height: height, width: width),
        title: Text(name));
  }
}

  

最后像這種多頁面使用FloatingActionButton,用它跳轉新界面是一定要設置heroTag,要不然跳轉會黑屏報錯

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM