Flutter基礎--狀態管理

當咱們使用編譯器建立一個新Flutter應用的時候,咱們能夠在主界面看到兩個小部件StatelessWidget和StatefulWidget。這是兩個最多見使用最頻繁的小部件了。前端

StatelessWidget ,StatefulWidget

  1. StatelessWidget 狀態不可改變的,它內部的數值和UI都應該是常量不可改變
  2. StatefulWidget 狀態可變,咱們能夠經過點擊,或者網絡獲取數據等來動態的改變界面。

假如咱們要實現以下圖的功能,點擊加一。若是使用StatelessWidget,會發現點擊的時候count是加一了,可是界面沒有刷新。應該使用StatefulWidget,當count加一的時候經過setState(() { _count++;});方法來改變count的值,這時候就發現界面能夠刷新了。git

好比github

import 'package:flutter/material.dart';

class StateManagerDemo extends StatefulWidget {
  @override
  _StateManagerDemoState createState() => _StateManagerDemoState();
}

class _StateManagerDemoState extends State<StateManagerDemo> {
  int _count = 0;
  void countCallBack(){
    setState(() {
      _count++;
    });
    debugPrint('$_count');
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StateManagerDemo'),
        elevation: 0.0,
      ),
      body: Counter0(_count),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: countCallBack,
      ),
    );
  }
}
//------
class Counter0 extends StatelessWidget {
  final int count;
  Counter0(this.count);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Chip(
        label: Text('$count'),
      ),
    );
  }
}
//-----
class Counter extends StatelessWidget {
  final int count;
  final VoidCallback voidCallback;
  Counter(this.count,this.voidCallback);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ActionChip(
        label: Text('$count'),
        onPressed: voidCallback,
      ),
    );
  }
}
複製代碼

上面的代碼中,StateManagerDemo有兩個子部件Counter0和Counter。當咱們點擊按鈕的時候,_count的值++,而後傳遞給子部件。bash

Counter0是直接接受父部件傳過來的參數。網絡

Counter不只接收父部件傳過來的參數,還有一個回調。這樣點擊它的時候,會執行父部件中的回調方法,也能改變自身的顯示。app

InheritedWidget

上面的狀況是隻有一層,Counter小部件中使用了父部件的count這個變量,假如Counter沒有用到這個變量而是它的子類用到了這個變量,咱們還要一層一層的傳下去嗎,這有點麻煩啊,這時候可使用InheritedWidget這個類來管理。less

import 'package:flutter/material.dart';

//使用InheritedWidget來管理狀態,
class ContentProvider extends InheritedWidget {
  final int count;
  final VoidCallback countCallBack;
  final Widget child;
  const ContentProvider({
    this. count,
     this. countCallBack,
    this. child,
  }): assert(child != null),
        super(child: child);

  static ContentProvider of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(
        ContentProvider) as ContentProvider;
  }
  //是否通知繼承該小部件的小部件更新
  @override
  bool updateShouldNotify(ContentProvider old) {
    return true;
  }
}
class StateManagerDemo extends StatefulWidget {
  @override
  _StateManagerDemoState createState() => _StateManagerDemoState();
}

class _StateManagerDemoState extends State<StateManagerDemo> {
  int _count = 0;
  void countCallBack(){
    setState(() {
      _count++;
    });
    debugPrint('$_count');
  }
  @override
  Widget build(BuildContext context) {
  //ContentProvider放在最外層,指定參數count和callback
    return ContentProvider(
      count: _count,
      countCallBack: countCallBack,
      child: Scaffold(
        appBar: AppBar(
          title: Text('StateManagerDemo'),
          elevation: 0.0,
        ),
        body: Counter1(),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: countCallBack,
        ),
      ),
    );
  }
}

class Counter1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //直接使用ContentProvider中的參數
    final int count = ContentProvider.of(context).count;
    final VoidCallback voidCallback = ContentProvider.of(context).countCallBack;
    return Center(
      child: ActionChip(
        label: Text('$count'),
        onPressed: voidCallback,
      ),
    );
  }
}
複製代碼

首先定義一個數據提供者ContentProvider繼承InheritedWidget,裏面定義咱們須要的count和回調。提供一個of方法讓外界能夠拿到它的實例,方便拿到方法。ide

而後將這個ContentProvider放在主佈局的最外層,並傳入須要的參數count和callBack。這樣它的子部件中就都能訪問到這個參數了。佈局

最後在子部件Counter1中直接使用ContentProvider中的參數。ui

ScopedModel

還可使用ScopedModel來完成狀態管理

這是一個第三方的庫,該庫最初是從Fuchsia代碼庫中提取的,使用時須要先導入包,在pubspec.yaml文件中添加依賴

dependencies:
   scoped_model: 1.0.1
複製代碼

pub.dev/packages?q=… 這裏能夠看到最新版本。使用這個庫的時候咱們在StatelessWidget中也能夠改變UI

該庫主要分爲三個部分

  1. Model class 擴展此類以建立本身的模型,例如SearchModel或UserModel。咱們能夠監聽數據的變化!
  2. ScopedModel 小部件 把Model包裝到ScopedModel中,它內部的全部部件都能拿到model中的數據
  3. ScopedModelDescendant 只要Model發生改變 它就會改變

好比用ScopedModel實現前面的功能

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';

class CountModel extends Model{
   int _count = 0;
   int get count =>_count;

   void countIncrease(){
     _count ++;
     //通知改變
     notifyListeners();
   }
}

class StateModelDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModel<CountModel>(
      model: CountModel() ,
      child: Scaffold(
        appBar: AppBar(
          title: Text('StateModelDemo'),
        ),
        body: Counter2(),
        floatingActionButton: ScopedModelDescendant<CountModel>(
          rebuildOnChange: false,
          builder: (context, child, model) => FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: (){
              model.countIncrease();
            },
          ),
        ),
      ),
    );
  }
}

class Counter2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<CountModel>(
      builder: (context, child, model) => Center(
        child: ActionChip(
          label: Text('${model.count}'),
          onPressed: (){
            model.countIncrease();
          },
        ),
      ),
    );
  }
}

複製代碼

Mobx

Mobx在前端中用的多,且很好用,因此Flutter也引入了,對於咱們Android開發者來講跟學前面幾個的成本都同樣哈哈。

查看版本

pub.dev/packages?q=…

github

github.com/mobxjs/mobx…

首先須要去pubspec.yaml文件中引入依賴

dependencies:
  mobx: ^0.3.8
  flutter_mobx: ^0.3.3
dev_dependencies:
  mobx_codegen: ^0.3.9
  build_runner: ^1.7.0
複製代碼

而後開始使用mobx完成以前的功能

首先建立一個store類

import 'package:mobx/mobx.dart';
//包含生成的文件
part 'state_manager_demo.g.dart';

class Counter = _Counter3 with _$Counter; abstract class _Counter3 with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}
複製代碼

建立完成以後,咱們會發現part後面的內容和_$Counter都會報錯。說是找不到,須要咱們生成。來到AndroidStudio的terminal窗口執行下面命令來生成文件

flutter packages pub run build_runner build
複製代碼

這時候就能夠看到咱們本身的文件state_manager_demo.dart文件旁邊生成了一個新文件state_manager_demo.g.dart,並且不在報錯了。

若是想要修改後.g.dart文件也能自動修改執行

pub run build_runner watch
複製代碼

下面去界面中使用它

class MobxDemo extends StatefulWidget {
  @override
  _MobxDemoState createState() => _MobxDemoState();
}

class _MobxDemoState extends State<MobxDemo> {
  final Counter counter = Counter();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StateModelDemo'),
      ),

      body: Center(
        child: Observer(
            builder: (_)=>ActionChip(
              label: Text('${counter.value}'),
              onPressed: (){
                counter.increment();
              },
            ),
        ),
      ),

      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed:(){
          counter.increment();
        },
      ),
    );
  }
}
複製代碼

很簡單繼承StatefulWidget,須要監聽的小部件使用Observer包裹起來。而後建立一個成員變量final Counter counter = Counter();內部就能夠直接使用Counter中的變量和方法了。運行效果跟前面的同樣

Provider

谷歌親兒子19年推出的 pub.dev/packages/pr…

github.com/rrousselGit…

添加依賴

provider: 3.1.0+1
複製代碼
  1. 定義一個Notifier
class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}
複製代碼
  1. 在根App中使用 providers中能夠添加多個ChangeNotifier
void main() => runApp(
    MultiProvider(child: MyApp(),providers: [
      ChangeNotifierProvider(builder: (_) => Counter()),
    ],)
);
複製代碼
  1. 開始使用
class ProviderDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);
    return  Scaffold(
      appBar: AppBar(
        title: Text('StateModelDemo'),
      ),
      body: ActionChip(
        label: Text('${counter._count}'),
        onPressed: (){
          Provider.of<Counter>(context, listen: false).increment();
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: (){
          Provider.of<Counter>(context, listen: false).increment();
        },
      ),
    );
  }
}
複製代碼

OK 完成

綜合來看這幾種狀態管理的方式 ,我感受Provider是最好用的一個,並且是Google親兒子,後面發展應該也不錯。

相關文章
相關標籤/搜索