Flutter中的狀態持久化以及狀態管理

前文

本文所提供的的狀態管理的方法爲provide以及provider這兩種,後續如果我使用了redux或state的話,就再來給大家分享

Flutter狀態持久化

目的
在我們日常開發中,每次點擊tab頁面後,都會出現從新加載當前頁面內容的現象,這種體驗給用戶看來很不友好,所以我們這裏就需要用到了Flutter狀態持久化。

兩種狀態持久化的方法

  • IndexedStack控件
  • AutomaticKeepAliveClientMixin

IndexedStack
原理:將所有的頁面都放到IndexStack當中,通過控制index屬性,來顯示哪個頁面(前端的小夥伴可以利用z-index來理解)
弊端:每一次加載APP的時候,都要將所有的頁面加載出來,才能達到頁面保持狀態的效果

原理圖:
在這裏插入圖片描述

如何使用?
在你的bottomNavigationBar頁面中,我們的body本來是控制顯示首頁的,現在我們用IndexedStack替換一下,下圖中的pageList是我的頁面數組
在這裏插入圖片描述

AutomaticKeepAliveClientMixin
結合tab切換保持頁面狀態相比IndexedStack而言配置起來稍微有些複雜,是結合BottomNavigationBar保持頁面狀態的時候進行配置的
原理:還沒搞懂。。。。。
如何使用?

  1. 首先實例化pageController,將BottomNavigationBar的 currentIndex值傳給pageController
    在這裏插入圖片描述
  2. 在body入口 用PageView包裹住,設置好controller參數爲實例化的值,children則爲頁面數組
    這裏說明一下,如果用PageView包裹住的話,tab頁切換就會有滑動切換的效果,所以這裏我們要設置一下onPageChanged方法,這樣底部標籤欄就可以跟隨切換而高亮了,下圖中由於疏忽忘記寫了,特附贈這段代碼
body: PageView(
          controller: this._pageController,
          children: this.pageList,
          onPageChanged: (index){
            setState(() {
             this._currentIndex = index; 
            });
          },
        ),
  1. 切換頁面的時候,通過BottomNavigationBar的onTap方法跳轉頁面,調用jumpToPage(index) 傳入index值 跳轉
    在這裏插入圖片描述
  2. 子類都要通過with關鍵字混合繼承AutomaticKeepAliveClientMixin;
    同時重寫wantKeepAlive方法,返回true
    在這裏插入圖片描述

OK,到此爲止了, 狀態化管理目前就這兩種,大多數使用的是第二種,推薦第二種!

Flutter狀態管理之Provide

flutter_provide是Google項目下的狀態管理,推薦使用這個插件
關於Flutter的狀態管理,我是用前端的Vuex去理解的,是將UI層和邏輯層分開的操作。

如何使用?
首先你要做的是在pubspec.yaml引入provide插件。

provide: ^1.0.2                             #狀態管理

安裝完畢後,我們就可以使用了,首先要寫個狀態管理類,也就是邏輯層的類
,我創建的文件名爲test.dart

test.dart

import 'package:flutter/material.dart';

// 將頁面的業務邏輯 分到當前dart文件中

// ChangeNotifier 不限制類的獲取,誰都可以獲取當前類
class Counter with ChangeNotifier {
  int currentIndex = 0;

  int index = 0;

  changeIndex(index){
    currentIndex = index;
    // 可以通知聽衆。局部刷新Widget
    notifyListeners();
  }

  increment(){
    index++;
    notifyListeners();
  }
}

創建好類之後,我們就將該狀態管理引入到我們的main.dart入口文件當中,同時記得要引入provide插件

import 'package:provide/provide.dart';          // 狀態管理
import 'package:project/provide/test.dart';

引入進來後,我們來將該類以及provide實例化,這裏需要注意的是,實例化的時候是Providers(),並不是Provider(),最後在runApp()當中將provider註冊進去

void main() {
  var counter = Counter();
  var providers = Providers();
	
// 如果想添加新的狀態管理,在下面通過..添加就行
  providers..provide(Provider<Counter>.value(counter))

  runApp(ProviderNode(child:MyApp(), providers:providers));
}

好了,現在我們準備工作已經完事了,接下來我們來看看如何使用吧

import 'package:flutter/material.dart';
import 'package:provide/provide.dart';
import 'package:project/provide/BottomNavigation.dart';

class MessagePage extends StatefulWidget {
  @override
  _MessagePageState createState() => _MessagePageState();
}

class _MessagePageState extends State<MessagePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        width: double.infinity,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // Text('消息')
            ShowText(),
            Button()
          ],
        ),
      )
    );
  }
}

class ShowText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Provide<Counter>(
        // counter 是自定義邏輯類
        builder: (context, child, counter){
          return Text(
            '${counter.index}',
            style: Theme.of(context).textTheme.display1,
          );
        },
      )
    );
  }
}

class Button extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('增加'),
      onPressed: (){
        // 調用改變狀態方法
        Provide.value<Counter>(context).increment();
      },
    );
  }
}

代碼分析:
在我們需要保持狀態管理的地方,利用Provider控件包裹起來,在調用builder方法 將控件返回出來,裏面有三個參數,第一個是上下文,第二個是子類,第三個是狀態管理類,我們通過第三個參數就可以訪問到我們的屬性值了,改變的方法也是通過value方法來調用管理類定義好的方法,這裏的value記得聲明泛型來指明我們的狀態管理類。

Flutter狀態管理之Provider

provider是也是在谷歌項目下的狀態管理,作用和provide都是一樣的,用於管理全局的數據狀態
如何使用?
首先你要做的是在pubspec.yaml引入provider插件。

provider: ^3.2.0

創建自定義狀態管理類Count.dart

import 'package:flutter/material.dart';

class Counter with ChangeNotifier{
  int _count;     // 狀態

  // 實例化時就初始化數據
  Counter(){
    this._count = 0;
  }

  int get count=>_count;      // 獲取當前狀態(爲什麼已經有個變量了,還要再寫一個變量賦值拋出去呢: 保護變量,避免狀態管理類被污染)

  incCount(){
    this._count++;
    notifyListeners();    // 通知更新狀態 
  }
}

main.dart中使用MultiProvider控件包裹住所有內容,同時設置屬性providers進行監聽狀態管理

import 'package:flutter/material.dart';
import 'package:test_03/router/routes.dart';		// 自定義路由

import 'package:provider/provider.dart';		// 狀態管理
import 'package:test_03/provider/Count.dart';		// 自定義狀態管理

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_)=>Counter()),		// 監聽自定義的類
      ],
      child: MaterialApp(
        initialRoute: '/',
        routes: routers,
        onGenerateRoute: onGenerateRoute,
      ),  
    );
  }
}

接下來就是在各個類中使用了,由於已經在main.js中用控件包裹住一層了,所以不需要像provide一樣,還需要再次嵌套控件了,只需要在您需要使用的類中引入您的provider插件以及自定義的狀態管理類就可以了。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_03/provider/Count.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  String data = "init";
  @override
  Widget build(BuildContext context) {
	
	// <Counter> 是泛型指定自定義狀態類
    var counterProvider = Provider.of<Counter>(context);	

    return Scaffold(
      body: ListView(
        children: <Widget>[
          Text(data),
          Text('${counterProvider.count}'),
          RaisedButton(
            child: Text('點擊修改狀態管理中的數據'),
            onPressed: (){
              counterProvider.incCount();
            },
          ),
        ],
      ),
  }
}

同理如果別的頁面也需要這個狀態數據,就像這樣引入就可以共用了

provider和provide的區別

不同之處

1、作者
provide:Flutter早期沒有自己的狀態管理類,由Google的一名開發人員開發寫出的一款插件
provider:Flutter官方團隊提供的官方插件
2、使用
provide
(1)需要在main.dart中的void main(){ }主函數方法中進行初始化和註冊。
(2)各個dart類使用的時候需要在外層再次包裹一層Provide的控件
provider
(1)只需在main.js中的主控件外部包裹一層MultiProvider控件,把providers聲明好屬性就可以在內部使用了。

相同之處

在自定義狀態管理類時,都是混合使用的flutter內部提供的ChangeNotifier類,在通知時都是使用的notifyListeners()方法

OK! 徹底完結,有問題@我哦,告辭┏(^0^)┛

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章