Flutter開始干係列-狀態管理Provider3

直接開始幹,沒有爲何~html


上一篇看了官方的默認的計數 Demo ,在 2019 Google I/O 大會中, Provider 代替 Provide 成爲官方推薦的狀態管理方式之一。git

Provider 能夠幹什麼?引用官方的一段話:github

By using widgets for state management, provider can guarantee:app

maintainability, through a forced uni-directional data-flow testability/composability, since it is always possible to mock/override a value robustness, as it is harder to forget to handle the update scenario of a model/widgetless

我的總結:提升項目可維護性,使結構更加清晰,重要的是提供單向數據流,實現更小單元的刷新,提高性能。ide

接下來就用 Provider 改造下默認的技術Demo。性能

添加 Provider 依賴

在 pubspec.yaml 中增長 provider: ^3.1.0 ,而後點擊 右上角 Packages get 就能夠了,這裏說明下:字體

^ 表示適配和當前大版本一致的版本,~ 表示適配和當前小版本一致的版本,因此這裏的 ^ 能夠不要。ui

dependencies:
  flutter:
    sdk: flutter

  ...
  
  # Provider https://github.com/rrousselGit/provider
  provider: ^3.1.0
複製代碼

建立 ChangeNotifier

import 'package:flutter/foundation.dart';

class CounterNotifier with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  increment() {
    _count++;
    notifyListeners();
  }
}
複製代碼

使用 Provider.of 實現

  • 使用 ChangeNotifierProvider.value 建立 Widget 持有 value (CounterNotifier())
  • 使用 Provider.of 獲取 value (CounterNotifier())
import 'package:flutter/material.dart';
import 'package:flutter_provider_demo/CounterNotifier.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 建立 Widget 持有 CounterNotifier 數據
    return ChangeNotifierProvider.value(
      value: CounterNotifier(),
      child: MaterialApp(
        title: 'Privoder Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: ProvidePage(title: 'Provider Demo Home Page'),
      ),
    );
  }
}

class ProvidePage extends StatelessWidget {
  final String title;

  ProvidePage({Key key, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    // 獲取 CounterNotifier 數據
    final counter = Provider.of<CounterNotifier>(context);
    
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counter.count}',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.increment();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

複製代碼

這樣就完成了使用 Provider 對默認計數 Demo 改造,使用 Provider.of 當ChangeNotifier 中調用 notifyListeners 時每次會從新調用 Widget 中的 buildthis

示例1

使用 Consumer 實現

這裏實現個稍微複雜點的,建立 Page1 顯示 current count,而後建立 Page2 實現官方默認點擊計數,看看 Provider 狀態管理效果。

構建 MyApp

  1. 這裏使用 MultiProvider 添加多個 Provider,固然也可使用註釋掉的嵌套方式,願意的話
  2. Provider.value(value: 36) 用做字體大小
  3. ChangeNotifierProvider.value(value: CounterNotifier()) 用做 count 計數
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// return Provider.value(
// value: 36,
// child: ChangeNotifierProvider.value(
// value: CounterNotifier(),
// child: MaterialApp(
// title: 'Privoder Demo',
// theme: ThemeData(
// primarySwatch: Colors.blue,
// ),
// home: Page1(),
// ),
// ),
// );
    return MultiProvider(
      providers: [
        Provider.value(value: 36),
        ChangeNotifierProvider.value(value: CounterNotifier())
      ],
      child: MaterialApp(
        title: 'Privoder Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Page1(),
      ),
    );
  }
}
複製代碼

構建 Page1

  1. 使用 Provider.of(context).toDouble() 獲取文字大小
  2. 使用 Provider.of(context) 獲取計數
  3. 使用 Colum 列表方式展現計數和跳轉頁面按鈕
  4. 當 build的時候 print('rebuild page 1');
class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //獲取文字大小
    final size = Provider.of<int>(context).toDouble();
    // 獲取計數
    final counter = Provider.of<CounterNotifier>(context);
    // 調用 build 時輸出
    print('rebuild page 1');
    return Scaffold(
      appBar: AppBar(
        title: Text('Page1'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // 顯示計數
            Text(
              'Current count: ${counter.count}',
              // 設置文字大小
              style: TextStyle(
                fontSize: size,
              ),
            ),
            SizedBox(
              height: 50,
            ),
            // 跳轉 Page2
            RaisedButton(
              onPressed: () => Navigator.of(context).push(
                MaterialPageRoute(builder: (context) => Page2()),
              ),
              child: Text('Next'),
            ),
          ],
        ),
      ),
    );
  }
}
複製代碼

構建 Page2

  1. 使用 Consumer2 獲取計數對象和文字大小
  2. 分別使用 print('rebuild page 2') 和 print('rebuild page 2 refresh count') 輸出調用相應 build 日誌
  3. 使用 Consumer 獲取計數對象,在點擊 button 時調用 CounterNotifier 的 increment 方法
class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('rebuild page 2');
    return Scaffold(
      appBar: AppBar(
        title: Text('Page2'),
      ),
      body: Center(
        child: Consumer2<CounterNotifier, int>(
            builder: (context, counter, size, _) {
          print('rebuild page 2 refresh count');

          return Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '${counter.count}',
                style: TextStyle(
                  fontSize: size.toDouble(),
                ),
              ),
            ],
          );
        }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 不須要監聽改變(listen: false 不會從新調用build)
          Provider.of<CounterNotifier>(context, listen: false).increment();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
複製代碼

示例2

2019-09-14 22:21:31.393 14373-14467/com.joker.flutter_provider_demo I/flutter: rebuild page 1 2019-09-14 22:21:31.406 14373-14467/com.joker.flutter_provider_demo I/flutter: rebuild page 2 refresh count

經過這個日誌能夠看出,使用 Consumer 能夠實現局部刷新,因此它的性能更優,應該優先選用。可是 Consumer 只有 Consumer ~ Consumer6,沒有多的是實現。若是須要則只有本身實現。

一點建議:不要吝嗇逗號,這會讓你的代碼結構看起來更爽

provider 提供了幾種不一樣類型得 XXProvider ,使用方法都大同小異

name description
Provider 最基本得 provider. 攜帶一個 value 並暴露它.
ListenableProvider Listenable 對象的特定 Provider 。ListenableProvider將監聽對象,以便在調用偵聽器時重建依賴它的 Widgets。
ChangeNotifierProvider 一個具體的 ListenableProvider 監聽 ChangeNotifier 。它會在須要時自動調用ChangeNotifier.dispose。
ValueListenableProvider 監聽 ValueListenable 並僅暴露 ValueListenable.value。
StreamProvider 監聽 Stream 並暴露最新發出的值。
FutureProvider 攜帶一個 Future,並在 Future 完成時更新依賴

以上就是 Provider 使用,最後奉上Demo地址

相關文章
相關標籤/搜索