Provider 是一個 Flutter 的套件,它是 InheritedWidget的封裝,可以讓開發者更容易地在應用程序中共享和更新狀態,而無需手動管理這些狀態的生命週期。它可以用於建立可重用的組件,並提供可定制的解決方案,其中包括將數據與UI連接到一起,以及將組件與其他組件之間的交互納入管理。
官方網站如下:https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md
參考資料:https://www.liujunmin.com/flutter/provider/eight_provider.html
(1)注入方法:(八種)
(1)Provider:
Provider( create: (_) => MyModel(), child: ... )
一般都會放在程式的最前面,這樣下面的子類都可以使用於MyModel裡面設置的變數,如下範例:
(6)如果在注入的地方要重複依賴,可以使用ProxyProvider:
(2)讀取的方法有下列幾個方法:
(4)Consumer
Consumer可以讓您在模型更改時重建小部件的特定部分,而不會失去其他部分的狀態。
void main() {
runApp(
Provider(
//此處放的是你要管控狀態變數的類別或方法,一般放置在MyApp之前
// ,以便讓下面的子類都可以使用
create: (BuildContext context) {},
//下面child後的子類才能使用此狀態
child: const MyApp(),
),
);
}
(2)如果說你有很多要注入的東西,為避免層層嵌套,可以直接使用
MultiProvider,如下:
MultiProvider
:
MultiProvider(
providers: [
Provider<Something>(create: (_) => Something()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
Provider<AnotherThing>(create: (_) => AnotherThing()),
],
child: someWidget,
)
(3)如果要即時監聽變數是否改變,可以使用ChangeNotifierProvider:ChangeNotifierProvider<ProviderInterface>(
create: (_) => ProviderImplementation(),
child: Foo(),
),
(4)如果資料有需要延遲處理,資料流僅一次傳送,可以使用FutureProvider:
FutureProvider<int?>(
initialValue: null, //初始值
create: (context) => Future.value(42), //要狀態管理的值
child: MyApp(),
)
(5)如果資料有需要延遲處理,資料流需多次傳送,可以使用StreamProvider:StreamProvider<int?>(
initialValue: null, //初始值
create: (context) => Stream<xxx>.periodic(Duration(milliseconds:1000),(value)=>return value).take(), //要狀態管理的值
child: MyApp(),
)
ProxyProvider<方法一, 依賴方法一的依賴方法>(
update: (context, 方法一, 依賴方法一的依賴方法) => 依賴方法一的依賴方法(參數: 要給的參數值),
)
(7)如果在注入的地方要重複依賴,且要即時監控更新ui,可以使用ChangeNotifierProxyProvider:
ChangeNotifierProxyProvider<方法一, 依賴方法一的依賴方法>(
create: (_) => BookManagerModel(BookModel()),
update: (context, 方法一, 依賴方法一的依賴方法) => BookManagerModel(bookModel),
)
最簡單的讀取值的方式就是使用 BuildContext 上的擴展屬性(由 provider 注入)。
(1)如果是要取得的值需要即時被更新於畫面中,使用context.watch<T>()
context.watch<T>(),widget 能夠監視到 T類型的 Provider 發生的改變。一般用於讀取需要即時監聽,渲染於ui畫面上的變數。
也可以用於一般的靜態方法讀取,如下:
Provider.of<T>(context,listen: true)
)上面T是你要讀取的類別類型,如你規劃的類別是xxx,就填入xxx,後面接你要讀的變數,listen:false表示不接受監聽,如:
context.watch<xxx>().name ⇨ 讀取出xxx類中的name變數
(2)如果是要取得的值沒必要立即更新於畫面中,使用context.read<T>()
context.read<T>(),直接返回 T,不會監視改變。一般用於操作要執行的方法,回傳一個執行後的結果。
也可以用於一般的靜態方法讀取,如下:Provider.of<T>(context,listen: false)
)上面T是你要讀取的類別類型,如你規劃的類別是xxx,就填入xxx,後面接你要讀的變數,listen:false表示不接受監聽,如:
context.watch<xxx>().add() ⇨ 讀取出xxx類中的add方法
(3)若依賴類中的變數很多,要選擇性的讀取其中的值,可以利用.select
context.select<T,R>(R cb(T value)),允許 widget 只監視 T 上的一部分內容的改變。
你也可以使用 Provider.of<T>(context) 這一靜態方法,它的表現類似 watch,而在你為傳入 listen: false 參數時(例如 Provider.of<T>(context,listen: false)),它的表現與 read 類似。
上面T是你要讀取的類別類型,如你規劃的類別是xxx,就填入xxx,R是指你要讀取的變數類型,後面的必給值有兩個,一個是selector,另一個是builder,可以直接放入你要運作的方法。
selector:(context,類名){return 類名.你要讀取變數;}
builder:(context,類名,child){return 你要回傳的值;}
可選值有1個,child,如果有一些你不需要重新建構的方法,為了節省資源,你可以放在child中。
(4)Consumer
Consumer可以讓您在模型更改時重建小部件的特定部分,而不會失去其他部分的狀態。
使用方法如下:
Consumer<回傳的類型>(
builder: (context, 回傳的值, child){ return 你要回傳的值;}
),
範例參考:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
//注入依賴
MultiProvider(
providers: [
//使用ChangeNotifierProvider組件注入依賴,注入的類為Counter
ChangeNotifierProvider(create: (_) => Counter()),
//使用Provider組件注入依賴,注入的類為Counter1
Provider(create: (_) => Counter1()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print(context.watch<Counter>().count.toString());
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(child: Text(context.watch<Counter1>().count.toString())),
floatingActionButton: FloatingActionButton(
onPressed: () {
//使用context.read,讀取Counter類中的方法
var counter = context.read<Counter>();
counter.increment();
//使用context.read,讀取Counter1類中的方法
context.read<Counter1>().increment();
Navigator.push(
context, MaterialPageRoute(builder: (context) => test()));
},
child: Icon(Icons.add),
),
);
}
}
class test extends StatefulWidget {
const test({Key? key}) : super(key: key);
@override
State<test> createState() => _testState();
}
class _testState extends State<test> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Container(
color: Colors.cyan,
width: 100,
height: 100,
//使用context.watch,讀取Counter1類中的屬性值,
child: Text(context.watch<Counter1>().count.toString()),
),
ElevatedButton(
onPressed: () {
//使用context.read,執行Counter1類中的方法,
// 此注入的類沒有混用ChangeNotifier,需於下自行使用setState更新畫面
context.read<Counter1>().increment();
setState(() {});
},
child: Text(context.watch<Counter1>().count.toString()),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
var counter = context.read<Counter>();
counter.increment();
context.read<Counter1>().increment();
Navigator.pop(context);
},
child: Icon(Icons.add),
),
);
}
}
//創建Counter,混入ChangeNotifier, DiagnosticableTreeMixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
//創建Counter1
class Counter1 {
int _count = 0;
int get count => _count;
void increment() {
_count++;
}
}
沒有留言:
張貼留言