Flutter狀態(tài)管理Provider(二)

? ? ? ?Google2019I/O大會上被谷歌推薦,原本谷歌的provide被棄用,與大部分狀態(tài)管理一樣使用了InheritedWidget?;?a target="_blank">Provider3.0
上一篇Flutter狀態(tài)管理Provider(一)

ChangeNotifierProvider()

? ? ? ?它與scoped_model差不多,不同的是它使用mixin而scoped_model使用的繼承,首先狀態(tài)類mixin一個ChangeNotifier,然后通過調(diào)用notifyListeners()來通知狀態(tài)更新。
? ? ? ?和ValueListenableProvider一樣也有兩種方式,ChangeNotifierProvider()和ChangeNotifierProvider.value(),區(qū)別在于ChangeNotifierProvider()會在銷毀時自動調(diào)用ChangeNotifier中的dispose()方法釋放一些資源。
? ? ? ?下面使用ChangeNotifierProvider()寫一個需求,與微信一樣底部4個按鈕切換界面,然后跳到二級頁面點擊二級頁面按鈕控制一級頁面的切換,先上圖:


sp.2019-06-27 18_11_16.gif

下面上代碼:

import "package:flutter/material.dart";
import 'index_page.dart';
import 'package:provider/provider.dart';
import 'index_provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //MultiProvider可以添加多個狀態(tài)管理
    //包裹在MaterialApp外面,作用范圍是全局
    return MultiProvider(
      providers: [
//      兩種方式,這里使用ChangeNotifierProvider,因為可以自動調(diào)用dispose()方法,幫我釋放資源
        ChangeNotifierProvider(builder: (_) => IndexProvider()),
//        ChangeNotifierProvider.value(value: IndexProvider())
      ],
      child: MaterialApp(
        title: "Flutter Demo",
        theme: ThemeData(primarySwatch: Colors.blue),
        home: IndexPage(),
      ),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'index_provider.dart';

class IndexPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<IndexProvider>(
        //優(yōu)化:在狀態(tài)改變時viewpage子頁面不會走build方法
        child: PageView(
          physics: NeverScrollableScrollPhysics(), //禁止?jié)L動
          //獲取pageController后不監(jiān)聽改變
          controller: Provider.of<IndexProvider>(context,listen: false).pageController,
          children: [ChildPage("第一頁"), ChildPage("第二頁"), ChildPage("第三頁")],
        ),
        builder: (context, indexProvider, child) {
          return Scaffold(
            body: child,
            bottomNavigationBar: BottomNavigationBar(
                onTap: (index) {
                  indexProvider.index = index;
                },
                currentIndex: indexProvider.index,
                items: [
                  BottomNavigationBarItem(icon: Icon(Icons.android), title: Text("android")),
                  BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("home")),
                  BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("person")),
                ]),
            floatingActionButton: FloatingActionButton(onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
            }),
          );
        });
  }
}

class ChildPage extends StatefulWidget {
  final String title;

  ChildPage(this.title);

  @override
  _ChildPageState createState() => _ChildPageState();
}

class _ChildPageState extends State<ChildPage> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  void initState() {
    super.initState();
    print("${widget.title}: initState");
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    print("${widget.title}: build");
    return Scaffold(
      backgroundColor: widget.title == '第一頁'
          ? Colors.red.withOpacity(0.5)
          : widget.title == '第二頁'
          ? Colors.yellow.withOpacity(0.5)
          : Colors.green.withOpacity(0.5),
      appBar: AppBar(title: Text(widget.title)),
      body: Center(child: Text(widget.title)),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            FlatButton(onPressed: () => changePageIndex(context, 0), child: Text("切換1"), color: Colors.red),
            FlatButton(onPressed: () => changePageIndex(context, 1), child: Text("切換2"), color: Colors.yellow),
            FlatButton(onPressed: () => changePageIndex(context, 2), child: Text("切換3"), color: Colors.green),
          ],
        ),
      ),
    );
  }

  changePageIndex(context, int index) {
    Provider.of<IndexProvider>(context, listen: false).index = index;
  }
}
import 'package:flutter/material.dart' show ChangeNotifier, PageController;

class IndexProvider with ChangeNotifier {
  int _index = 0;
  PageController pageController;

  int get index => _index;

  set index(int value) {
    _index = value;
    pageController.jumpToPage(this._index);
    notifyListeners();
  }

  IndexProvider() {
    pageController = PageController(initialPage: _index);
  }

  //使用ChangeNotifierProvider會在銷毀時調(diào)用dispose()方法釋放資源
  @override
  void dispose() {
    pageController?.dispose();
    super.dispose();
  }
}

源碼
ChangeNotifierProvider.png

StreamProvider

? ? ? ?它有三種使用方式,StreamProvider、StreamProvider.value()和StreamProvider.controller(),StreamProvider和StreamProvider.value()幾乎一樣,StreamProvider.controller()有一點不一樣,先上圖:


aaaaaa.2019-06-27 18_15_11.gif

先介紹StreamProvider和StreamProvider.value()

class StreamPage extends StatefulWidget {
  @override
  _StreamPageState createState() => _StreamPageState();
}

class _StreamPageState extends State<StreamPage> {
  StreamController _streamController;
  int _count=4;

  @override
  void initState() {
    super.initState();
    _streamController = StreamController<int>();
  }

  @override
  void dispose() {
    _streamController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("StreamProvider")),
//      body: StreamProvider<int>.value(
//        value: _streamController.stream,
//        initialData: _count,
////        catchError: ,
//        child: MyText(),
//      ),
      body: StreamProvider<int>(
        builder: (_) => _streamController.stream,//builder等于value
        initialData: _count,//initialData:初始化時的值,不寫為null
        catchError: (BuildContext context, Object error) {
          //catchError:異常時調(diào)用,返回值與StreamController范型一樣
          //如果catchError不寫,當(dāng)報錯時屏幕直接爆紅出錯
          print("哈哈:${error.toString()}");
          return 10000;
        },
        child: MyText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if(_count<8) _streamController.sink.add(++_count);
          else _streamController.sink.addError("異常啦");
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

class MyText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final count = Provider.of<int>(context);
    return Center(child: Text("$count"));
  }
}

StreamProvider<T>.controller()代碼:

class StreamPage extends StatefulWidget {
  @override
  _StreamPageState createState() => _StreamPageState();
}

class _StreamPageState extends State<StreamPage> {
  StreamController _streamController;
  int _count = 4;

  @override
  void initState() {
    super.initState();
    _streamController = StreamController<int>();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("StreamProvider")),
      body: StreamProvider<int>.controller(
        builder: (_) => _streamController,
        initialData: _count, //initialData:初始化時的值,不寫為null
        catchError: (BuildContext context, Object error) {
          //catchError:異常時調(diào)用,返回值與StreamController范型一樣
          //如果catchError不寫,當(dāng)報錯時屏幕直接爆紅出錯
          print("哈哈:${error.toString()}");
          return 10000;
        },
        child: MyText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (_count < 8)
            _streamController.sink.add(++_count);
          else
            _streamController.sink.addError("異常啦");//手動拋異常
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

class MyText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final count = Provider.of<int>(context);
    return Center(child: Text("$count"));
  }
}

? ? ? ?嗯?!看上去好像沒什么不同,但你仔細看StreamProvider<T>.controller代碼里面我沒有重寫dispose()方法,原因是StreamProvider<T>.controller會幫我們在銷毀是調(diào)用StreamController的close()方法。
進入源碼可以看到


StreamProvider.controller.png

StreamControllerBuilderDelegate.png

總結(jié):

  1. Provider()與Provider.value()區(qū)別是Provider()有一個dispose參數(shù)傳遞一個方法可以幫助我們銷毀的時候釋放資源,它們都不提供狀態(tài)改變監(jiān)聽。
  2. ValueListenableProvider.value()與ValueListenableProvider()是差不多的,只支持一個狀態(tài)值。
  3. ChangeNotifierProvider.value()與ChangeNotifierProvider()區(qū)別是ChangeNotifierProvider()在銷毀的時候調(diào)用dispose()釋放資源,在需要使用多個狀態(tài)值時可以使用ChangeNotifierProvider。
  4. ListenableProvider代碼沒有寫出來,它是ChangeNotifierProvider的父類,
    ListenableProvider.value()和ChangeNotifierProvider.value()功能一樣,ListenableProvider()與ValueListenableProvider()差不多,但ListenableProvider()多了一個dispose參數(shù),需要自己傳,在銷毀的時候調(diào)用釋放資源.
  5. StreamProvider.value()和StreamProvider()基本一樣,都需要手動關(guān)閉流,而StreamProvider.controller()自動關(guān)閉流。
  6. MultiProvider()可以提供多個狀態(tài)。
  7. Provider.of<T>()用來獲取Widget樹中的狀態(tài),在使用 ValueListenableProvider、ChangeNotifierProvider和StreamProvider時Provider.of<T>()中的listen參數(shù)可以控制是否監(jiān)聽狀態(tài)改變。
  8. Consumer<T>()與Provider.of<T>()都是用來獲取Widget樹中的狀態(tài),但Consumer可以用在沒有context的地方,也可以用來優(yōu)化性能,使用child參數(shù)可以縮小重繪的范圍。
  9. 狀態(tài)管理包裹在MaterialApp()外面作用域是全局,其他作用域在本頁面或本頁的子Widget中;

如有寫的不對的地方歡迎指出!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容