flutter之provider使用與簡單封裝

Provider是目前Google推薦的狀態管理庫,國內鏡像的地址,現在的最新版本是4.1.1,但是我的sdk版本是1.16,不支持這個最新的,所以改版本用的4.0.0

基本使用

我先是以最簡單的主題更改來做基本的更改

第一步,添加Provider依賴,pubspec.yaml

dependencies:
  provider: ^4.0.0

第二步,創建Model

import 'package:flutter/material.dart';

class ThemeModel with ChangeNotifier {
  ThemeData themeData = light();

  bool lighted = true;

  changeTheme() {
    if (lighted) {
      themeData = dark();
    } else {
      themeData = light();
    }
    lighted = !lighted;

    notifyListeners();
  }

  static ThemeData light() {
    return ThemeData(
      textTheme: TextTheme(
          subtitle1: TextStyle(
        color: Colors.black,
        fontSize: 16,
      )),
      backgroundColor: Colors.white,
      brightness: Brightness.light,
      primaryColor: Color(0xff248bfe),
      appBarTheme: AppBarTheme(
        elevation: 0,
        textTheme: TextTheme(
            subtitle1: TextStyle(
          color: Colors.white,
          fontSize: 16,
        )),
      ),
    );
  }

  ThemeData dark() {
    return ThemeData(
      textTheme: TextTheme(
          subtitle1: TextStyle(
        color: Colors.white,
        fontSize: 16,
      )),
      backgroundColor: Colors.black,
      brightness: Brightness.dark,
      primaryColor: Color(0xff000000),
      appBarTheme: AppBarTheme(
        elevation: 0,
        textTheme: TextTheme(
            subtitle1: TextStyle(
          color: Colors.white,
          fontSize: 16,
        )),
      ),
    );
  }
}

一個白天模式一個夜間模式,數據需要混入ChangeNotifier,數據更改後需要調用notifyListeners();來發出通知。

第三步,使用ChangeNotifierProvider

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context1) {
    return ChangeNotifierProvider<ThemeModel>(
      create: (_)=>new ThemeModel(),
      child: //這裏必須用Builder,因爲Provider.of<ThemeViewModel>(context,listen: true)要拿這裏傳入的context
        Builder(
          builder: (context)=>      
            MaterialApp(
              debugShowCheckedModeBanner: false,
              title: 'Flutter Demo',
              theme:  Provider.of<ThemeModel>(context,listen: true).themeData,//通過Provider.of取值
              routes: <String,WidgetBuilder>{
                "main/mainpage":(BuildContext context1)=>new MainPage(), //爲什麼這裏可以拿到context1,provider就不行呢???因爲該context中並沒有Provider
              },
              home: SplashPage(),
            )
        ),
    );

  }
}

  • ChangeNotifierProvider提供數據,如果用Provider以後取的的model就感受不到變化了
  • child需要傳入Builder,因爲外層build方法的context把當前model沒有包含進去,所以需要用build來取含有此modelcontext.
  • 通過Provider.of取到對應的model

第四步,通過點擊按鈕,獲取model並更改模式

InkWell(
          onTap: (){
            Provider.of<ThemeModel>(context,listen:false).changeTheme();
          },
          child: Icon(Icons.cake),
        )

簡單的封裝

用provider對數據更改來刷新界面時,我們需要有效的控制數據的範圍,如果全部放在最上層肯定會影響性能及數據的安全性。所以我們需要對provider寫一個通用的組件,方便我們使用。

ProviderWidget封裝

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

class ProviderWidget<T extends ChangeNotifier> extends StatefulWidget{
  final T model;
  final Widget Function(BuildContext context, T value, Widget child) builder;
  final Function(T) onReady;

  ProviderWidget({this.model,this.onReady,this.builder});

  @override
  _ProviderWidgetState<T> createState() => _ProviderWidgetState<T>();    
}
    
class _ProviderWidgetState<T extends ChangeNotifier> extends State<ProviderWidget<T>> {
  @override
  void initState() {
    super.initState();
    if(widget.onReady!=null){
      widget.onReady(widget.model);
    }
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      create: (_) => widget.model,
      child: Consumer<T>(//因爲child
        builder: widget.builder,
      ),
    );
  }
}
  • 泛型T是我們model的類型
  • builder是生成childbuilder, 不直接傳入child還是因爲之前說過的原因,構造對象的context需要是含有當前modelcontext才能取到此model,不然後報錯。
  • 爲了能取到context,我們此處用到了Consumer,合理的用Consumer可以減少刷新範圍,這個類的具體說明請參考使用Provider前你應瞭解Consumer

使用示例

model類

class TestModel with ChangeNotifier {
  int clickNum=0;

  void add() {
    clickNum++;
    notifyListeners();
  }
}

用widget的地方直接使用

ProviderWidget<TestModel>(
        model:TestModel(), 
        onReady:(model){
          model.toString();
        }, 
        builder:(context, model, child){
          return Column(
            children: <Widget>[
              Text("${model.clickNum}"),
              RaisedButton(child:Text("add"),onPressed: (){
                  model.add();
              })
            ],
          );
        },
      )

本文完整源碼請移步github

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