Flutter底部tab切換保持頁面狀態的幾種方法

  • 2019 年 10 月 4 日
  • 筆記

首先看一下如下影片:

通過影片我們可以看到,當通過底部tabBar將頁面在「首頁」和「分類」之間進行切換的時候,每一次進入頁面的時候該頁面的數據都會重新載入。那麼如何讓頁面保持原來的狀態,而不是每次都要重新載入刷新數據呢?有兩種方式。

第一種方式:採用IndexdStack

IndexdStack和Stack一樣,都是層布局控制項,可以在一個控制項上面放置另一個控制項,但唯一不同的是,IndexdStack在同一時刻只能顯示子控制項中的一個控制項,通過index屬性來設置顯示的控制項。

配置底部導航的核心程式碼如下:

import 'package:flutter/material.dart';  import 'package:flutter_jdshop/pages/tabs/CategoryPage.dart';  import 'package:flutter_jdshop/pages/tabs/HomePage.dart';  import 'package:flutter_jdshop/pages/tabs/ShoppingCartPage.dart';  import 'package:flutter_jdshop/pages/tabs/UserPage.dart';    class Tabs extends StatefulWidget {    Tabs({Key key}) : super(key: key);      _TabsState createState() => _TabsState();  }    class _TabsState extends State<Tabs> {      int _currentIndex = 0;//記錄當前選中哪個頁面      List<Widget> _pages = [      HomePage(),      CategoryPage(),      ShoppingCartPage(),      UserPage()    ];      @override    Widget build(BuildContext context) {      return Scaffold(        appBar: AppBar(title: Text("JDShop")),        body: this._pages[this._currentIndex],        bottomNavigationBar: BottomNavigationBar(          fixedColor: Colors.red,//底部導航欄按鈕選中時的顏色          type: BottomNavigationBarType.fixed,//底部導航欄的適配,當item多的時候都展示出來          currentIndex: this._currentIndex,          onTap: (index){            setState(() {              this._currentIndex = index;            });          },          items: [            BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("首頁")),            BottomNavigationBarItem(icon: Icon(Icons.category), title: Text("分類")),            BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), title: Text("購物車")),            BottomNavigationBarItem(icon: Icon(Icons.people), title: Text("我的"))          ],        ),      );    }  }

此時還是不可以保持頁面狀態的。

這裡我們將body由

body: this._pages[this._currentIndex],

替換成

body: IndexedStack(          index: this._currentIndex,          children: this._pages,        ),

這樣就能夠實現保持頁面狀態了,效果如下:

我們可以看到,此時,頁面的數據只在最開始進來的時候進行載入,然後就保持住這個頁面的狀態了,並不會每次進來都進行數據的載入刷新了。

使用IndexedStack來保持頁面狀態的優點就是配置簡單,但是它也有很大的缺點:IndexedStack中管理的子頁面在一開始就全部一次性載入出來了,不管有沒有顯示出來,然後通過index屬性來確定到底顯示哪一個頁面

第二種方式:AutomaticKeepAliveClientMixin

如果所有的頁面都需要保持頁面狀態,那麼就使用indexdStack;如果有些頁面需要保持頁面狀態,有些頁面需要進來就刷新,那麼我們就需要使用AutomaticKeepAliveMixin這個類來單獨控制某個頁面的狀態。

AutomaticKeepAliveClientMixin相比IndexdStack,配置起來要複雜一些。

AutomaticKeepAliveClientMixin結合底部BottomNavigationBar來保持頁面狀態的時候,其配置步驟如下:

import 'package:flutter/material.dart';  import 'package:flutter_jdshop/pages/tabs/CategoryPage.dart';  import 'package:flutter_jdshop/pages/tabs/HomePage.dart';  import 'package:flutter_jdshop/pages/tabs/ShoppingCartPage.dart';  import 'package:flutter_jdshop/pages/tabs/UserPage.dart';    class Tabs extends StatefulWidget {    Tabs({Key key}) : super(key: key);      _TabsState createState() => _TabsState();  }    class _TabsState extends State<Tabs> {      int _currentIndex = 0;//記錄當前選中哪個頁面      //第1步,聲明PageController    PageController _pageController;      @override    void initState() {      super.initState();      //第2步,初始化PageController      this._pageController = PageController(initialPage: this._currentIndex);    }      List<Widget> _pages = [      HomePage(),      CategoryPage(),      ShoppingCartPage(),      UserPage()    ];      @override    Widget build(BuildContext context) {      return Scaffold(        appBar: AppBar(title: Text("JDShop")),        //第3步,將body設置成PageView,並配置PageView的controller屬性        body: PageView(          controller: this._pageController,          children: this._pages,        ),        bottomNavigationBar: BottomNavigationBar(          fixedColor: Colors.red,//底部導航欄按鈕選中時的顏色          type: BottomNavigationBarType.fixed,//底部導航欄的適配,當item多的時候都展示出來          currentIndex: this._currentIndex,          onTap: (index){            setState(() {              //第4步,設置點擊底部Tab的時候的頁面跳轉              this._currentIndex = index;              this._pageController.jumpToPage(this._currentIndex);            });          },          items: [            BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("首頁")),            BottomNavigationBarItem(icon: Icon(Icons.category), title: Text("分類")),            BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), title: Text("購物車")),            BottomNavigationBarItem(icon: Icon(Icons.people), title: Text("我的"))          ],        ),      );    }  }

以上前4步都是在tabs.dart中進行配置的,此時所有的頁面還是不可保持頁面狀態的。然後第5步就是在需要保持頁面狀態的頁面裡面混入AutomaticKeepAliveClientMixin類,並將wantKeepAlive方法返回為true,如下所示:

//首頁頁面  class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin{      @override    bool get wantKeepAlive => true;        //分類頁面    class _CategoryPageState extends State<CategoryPage> with AutomaticKeepAliveClientMixin{      @override    bool get wantKeepAlive => true;

這樣,首頁頁面和分類頁面就實現了頁面狀態的保持,頁面數據只在首次進入該頁面的時候進行刷新;而其他沒有實現頁面保持的頁面在每次進入該頁面的時候,數據都會刷新

使用AutomaticKeepAliveClientMixin這個類來保持首頁和分類頁面狀態,其效果如下所示:

以上。