Flutter狀態管理Provider,簡單上手


在之前的文章中介紹了 Google 官方倉庫下的一個狀態管理 Provide。乍一看這倆玩意可能很容易就被認為是同一個東西,仔細一看,這不就差了一個字嗎,有什么區別呢。

首先,你要知道的最大的一個區別就是,Provide 被 Provider 干掉了...假如你就是用了 Provide 的,你的內心應該已經開始罵了,這不是坑爹嗎 。不過幸運的是,你要從 Provide 遷移到 Provider 並不是太難。

Provider 從名字上就很容易理解,它就是用於提供數據,無論是在單個頁面還是在整個 app 都有它自己的解決方案,我們可以很方便的管理狀態。可以說,Provider 的目標就是完全替代StatefulWidget。

Provider非全局狀態管理:

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

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('搜索結果'),
      ),
      body: Provider<String>.value(  //provider
        value: 'This is from MyHomePage',
        child: MyHomePage(),
      ),
    ); 
  } 
}

//Provider.of
class MyHomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Center(
         child: Text('${Provider.of<String>(context)}'),
      );
    }
}
//Consumer同樣實現
class MyHomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Center(
        //child: Text('${Provider.of<String>(context)}'),
        child:Consumer<String>(builder:(context, data, child){
          return Text(data);
        }),
      );
    }
  }

官方示例代碼:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';


class Counters with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

class ProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(builder: (_) => Counters()),
      ],
      child: Consumer<Counters>(
        builder: (context, counter, _) {
          return MaterialApp(
            supportedLocales: const [Locale('en')],
            localizationsDelegates: [
              DefaultMaterialLocalizations.delegate,
              DefaultWidgetsLocalizations.delegate,
              _ExampleLocalizationsDelegate(counter.count),
            ],
            home: const MyHomePage(),
          );
        },
      ),
    );
  }
}

class ExampleLocalizations {
  static ExampleLocalizations of(BuildContext context) =>
      Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);

  const ExampleLocalizations(this._count);

  final int _count;

  String get title => 'Tapped $_count times';
}

class _ExampleLocalizationsDelegate
    extends LocalizationsDelegate<ExampleLocalizations> {
  const _ExampleLocalizationsDelegate(this.count);

  final int count;

  @override
  bool isSupported(Locale locale) => locale.languageCode == 'en';

  @override
  Future<ExampleLocalizations> load(Locale locale) =>
      SynchronousFuture(ExampleLocalizations(count));

  @override
  bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Title()),
      body: const Center(child: CounterLabel()),
      floatingActionButton: const IncrementCounterButton(),
    );
  }
}

class IncrementCounterButton extends StatelessWidget {
  const IncrementCounterButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: () {
        // `listen: false` is specified here because otherwise that would make
        // `IncrementCounterButton` rebuild when the counter updates.
        Provider.of<Counters>(context, listen: false).increment();
      },
      tooltip: 'Increment',
      child: const Icon(Icons.add),
    );
  }
}

class CounterLabel extends StatelessWidget {
  const CounterLabel({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<Counters>(context);
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const Text(
          'You have pushed the button this many times:',
        ),
        Text(
          '${counter.count}',
          style: Theme.of(context).textTheme.display1,
        ),
        RaisedButton(
          onPressed: (){
            Navigator.push(context, MaterialPageRoute(builder: (context)=>ProviderSecond()));
          },
          child: Text('下一頁'),
        )
      ],
    );
  }
}

class Title extends StatelessWidget {
  const Title({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(ExampleLocalizations.of(context).title);
  }
}

class ProviderSecond extends StatelessWidget { const ProviderSecond({Key key}) : super(key: key); @override Widget build(BuildContext context) { final counter = Provider.of<Counters>(context); return Scaffold( appBar: AppBar( leading: IconButton( //返回按鈕 onPressed: (){ Navigator.pop(context); //返回上級頁面 }, icon: Icon(Icons.arrow_back), ), title: Text('第二頁'), ), body: Text( '${counter.count}', style: Theme.of(context).textTheme.display1, ), ); } }

效果圖:

點擊+到7,然后點擊下一頁,下一頁的內容同樣是7。

Provider2.0到3.0的改變:

2.0:
ChangeNotifierProvider.value(notifier: myNotifier),

StreamProvider(builder: (_) => StreamController<int>()),

3.0:
ChangeNotifierProvider.value(value: myNotifier),

StreamProvider.controller(builder: (_) => StreamController<int>()),
 
下面基於Provider v-3.0 寫個簡單的示例: 

第一步,添加Provider依賴

provider: ^3.1.0+1

pub地址:https://pub.dev/packages/provider

第二步,創建Model

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

簡單的一個Counters對象,里面只有一個字段_count

  1. 這里需要混入ChangeNotifier
  2. 寫一個增加的方法,然后需要調用notifyListeners();這個方法是通知用到Counters對象的widget刷新用的。
  3. get方法

第三步,使用ChangeNotifierProvider

我們要監聽改變要在MyApp()外面套一層,這個是全局的,於是代碼如下 :
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './provider/counter.dart';

void main() { 
  //runApp(new MyApp());
  runApp(
     ChangeNotifierProvider<Counter>.value(//ChangeNotifierProvider調用value()方法,里面傳出value和child
      value: Counter(),//value設置了默認的Counter()
      child: MyApp(),
    )
  );
}    
當然Provider不止提供了 ChangeNotifierProvider,還有 Provider、 ListenableProvider、 ValueListenableProviderStreamProvider,
具體可以看 wiki.
如果想管理多個對象可以用 MultiProvider,如下:
void main() { 
  //runApp(new MyApp());
  runApp(
    // ChangeNotifierProvider<Counter>.value(//ChangeNotifierProvider調用value()方法,里面傳出value和child
    //   value: Counter(),//value設置了默認的Counter()
    //   child: MyApp(),
    // )
    MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: Counter()),
        //ChangeNotifierProvider(builder: (_) => Counter()),
      ],
      child: MyApp(),
    ),
  );
}

第四步,使用Provider獲取Counter的值

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../provider/counter.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
        actions: <Widget>[
          FlatButton(
            child: Text("下一頁"),
            onPressed: () =>
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return SecondPage();
                })),
          ),
        ],
      ),
      body: Center(
        child: Text("${Provider.of<Counter>(context).count}"),//用Provider.of<Counter>(context).count獲取_count的值,Provider.of<T>(context)相當於Provider去查找它管理的Counter()
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context).increment();//用Provider.of<Counter>(context).increment();調用Counter()中的increment()方法
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

 同樣第二個頁面也這樣寫,如下

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var counter = Provider.of<Counter>(context).count;
    return Scaffold(
      appBar: AppBar(
        title: Text("SecondPage"),
      ),
      body: Center(
        child: Text("${counter}"),
        //child: Text("${Provider.of<Counter>(context).count}"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context).increment();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

這樣,當每個頁面都點擊+號按鈕時,_count便會+1,同時通知並更新到使用它的地方。

完整代碼寫到一個頁面,copy后可直接運行:

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

main() {
  runApp(ChangeNotifierProvider<Counter>.value(
    value: Counter(),
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Provider",
      home: HomePage(),
    );
  }
}

class Counter with ChangeNotifier {//混入ChangeNotifier
  int _count = 0;
  get count => _count;

  void increment() {
    _count++;
    notifyListeners();//通知
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
        actions: <Widget>[
          FlatButton(
            child: Text("下一頁"),
            onPressed: () =>
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return SecondPage();
                })),
          ),
        ],
      ),
      body: Center(
        child: Text("${Provider.of<Counter>(context).count}"),//用Provider.of<Counter>(context).count獲取_count的值,Provider.of<T>(context)相當於Provider去查找它管理的Counter()
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context).increment();//用Provider.of<Counter>(context).increment();調用Counter()中的increment()方法
        },
        child: Icon(Icons.add),
      ),
    );
  }
}


class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var counter = Provider.of<Counter>(context).count;
    return Scaffold(
      appBar: AppBar(
        title: Text("SecondPage"),
      ),
      body: Center(
        child: Text("${counter}"),
        //child: Text("${Provider.of<Counter>(context).count}"),//1 
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context).increment();//2
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

效果圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM