Riverpod
一、使用目的
Riverpod是一個flutter使用的狀態管理工具。
二、官網及下載處
官網:https://riverpod.dev/
pub:https://pub.dev/packages/riverpod
三、使用方法:
(一)在使用時,一般在程式的入口main()中,加入ProviderScope(child: MyApp()),這樣於下運作的程式才能使用全域常數的功能
void main() {
runApp(ProviderScope(child: MyApp()));
}
(二)六種不同情況的引入方式及用法
(1)Provider 宣告一個在任何地方都可以使用及讀取的全域常數。
宣告的方法如下:
//宣告number為一個Provider<int>類型的全域變數,切始值為0
(1-1)需要一個全域的int常數時,宣告為Provider<int>
final Provider<int> number=Provider<int>((ProviderRef ref) {
return 0;
});
要在別的位置取得這個值,可以在可使用ref的地方,使用以下的寫法
ref.watch(number)
或將其值賦予一個變數,以便在後面使用:
final getNumber=ref.watch(number);
上面ref.watch(number)的值就是等於上面回傳的0。
(1-2)需要一個全域的String常數時,宣告為Provider<String>
final Provider<String> countryProvider = Provider<String>((ProviderRef ref) {
return 'Taiwan';
});
要在別的位置取得這個值,可以在可使用ref的地方,使用以下的寫法
ref.watch(countryProvider)
或將其值賦予一個變數,以便在後面使用:
final getNumber=ref.watch(countryProvider);
上面ref.watch(countryProvider)的值就是等於上面回傳的Taiwan。
(1-3)需要一個全域的方法,宣告為Provider<方法>
(1-3-1) 不傳入參數
final Provider<Location> locationProvider = Provider((ProviderRef ref) {
return Location();
});
(1-3-2)若要傳入一個參數,可再自己新增
final Provider<Location> locationProvider = Provider((ProviderRef ref) {
return Location(providerRef: ref);
});
為了便於透過ProviderRef 類型的ref,引入其它的Provider,因此這裡將它當作location傳入建構式中,寫法如下:
final Provider<Location> locationProvider = Provider((ProviderRef ref) {
return Location(providerRef: ref); //這裡多了一個傳入的參數
});
class Location {
final ProviderRef providerRef;
Location({required this.providerRef}); //與之搭配的建構式
String get info { //下面使用了傳入的providerRef,以便引入其它的全域Provider,做其它使用
final String country = providerRef.read(countryProvider); //taiwan
final String city = providerRef.read(cityProvider); //taipei
return '$country ,$city';
}
}
要在別的位置取得這個Provider,可以在可使用ref的地方,使用以下的寫法
ref.watch(locationProvider)
或將其值賦予一個變數,以便在後面使用:
final location=ref.watch(locationProvider);
上面ref.watch(locationProvider)的值為 Location(providerRef: ref)。
而要使用這個 Location裡的方法,或讀取它的值,可以用。
location.info <=這可以取得上面info方法的回傳值 '$country ,$city'
(2)StateProvider 宣告一個可以變得簡單變數
宣告方法如下:
final StateProvider<int> postalCodeProvider = StateProvider((ref) {
return 369;
});上面的posttalCodeProvider為StateProvider<int>類型,其值為369,與上面的Provider不同的地方,使用者可以透過簡單的方法,改變回傳的369的值。
改變的方法,可以在一個可使用ref的地方,設定一個按鈕,並寫如下的程式
ElevatedButton(
onPressed: () {
ref.read(postalCodeProvider.notifier).state++;
},
child: const Text('點我加數字'),
),
read的()中的格式是(要引入的StatePriver變數名稱.notifier),而後的.state <=這代表要執行的那個值,這裡指的是上面的369,後面的++,指每點一次就加1。因此上面的寫法,會在每一次點選的時候,將state值加1。
而要在畫面中顯示的方法與執行的方法略有不同,它是透過watch進行監聽。如下宣告的方法:
舊的寫法
final postalCode = ref.watch(postalCodeProvider.state).state;
要注意的是,這裡用到的是watch,不是read,( )裡面的格式則是(要引入的StatePriver變數名稱.state),,而後的.state <=這代表要顯示的那個值。
新版有寫的寫法較為簡潔
final postalCode = ref.watch(postalCodeProvider);
其後可能在畫面中,適當的地方顯示出這個postalCode,如Text(postalCode.toString()), 這樣就可以做到隨時監聽,在畫面中改變數字的效果。
(3)StateNotifierProvider 宣告多個可以變的變數
StateNotifierProvider和StateProvider用法很相似,StateProvider有一個state可以讀取,寫程式時可以簡單的改變它的值,而StateNotifierProvider它可以建立一個類,其中擴充自StateNotifier,相對於只能操控固定的值,StateNotifierProvider可以進行更靈活的操作。
宣告的方法如下:
final StateNotifierProvider<FamilyNumberProvider, int> familyNumberProvider =
StateNotifierProvider<FamilyNumberProvider, int>((ref) {
return FamilyNumberProvider();
});
上面程式中< 1,2> ,
前面的部份為要傳入類的名字,而後面int則為要傳回值的類型,而最後要回傳(return)的是你要宣告為全域變數的方法,這個方法名要與你建的類建構式相同。
擴充自StateNotifier的類寫法參考如下:
class FamilyNumberProvider extends StateNotifier<int> {
//super(0)中的0,預設state變數的值,除了可以是int外,也可以是String,list
//例:super([])
FamilyNumberProvider() : super(0);
//state增加的方法,想寫什麼可以自己設計
void increament() {
state = state + 1;
}
//state減少的方法,想寫什麼可以自己設計
void decreament() {
state--;
}
}上面要注意的是super(0)這個部份,這裡0,在這個class裡預設為state,所以下面你要透過state進行進一步的操作,而這個state,是等等使用ref.watch監控的對象。
如果要引入的值比較多,可以使用陣列,super([]),可以參考
官網的作法。
而若要顯示於畫面的寫法如下:
final familyNumber = ref.watch(familyNumberProvider);
注意這裡宣告的方法與StateProvider是不同的,( )中沒有加上.state
其後可能在畫面中,適當的地方顯示出這個familyNumber,如Text(familyNumber.toString()), 這樣就可以做到隨時監聽,在畫面中改變數字的效果。
(4)FutureProvider flutter中有一些異步的方法,而riverpod有提供了一個可以將異步的方法於全域方法引用的方法,以下為使用的方法示範。
(4-1)任何一個異步的方法,以下會每隔2秒傳回Hello, Riverpod字串
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Hello, Riverpod!';
}
(4-2)宣告dataProvider為全域變數,可在適當的時機,讀取上面4-1建立的異步程式,因為是異步,所以宣告時要記得加入async和await
final dataProvider = FutureProvider<String>((ref) async {
return await fetchData();
});
(4-3)顯示於畫面
Widget build(BuildContext context, WidgetRef ref) {
AsyncValue<dataProvider> dataAsyncValue = ref.watch(dataProvider); //利用ref.watch(dataProvider);
return dataAsyncValue.when(
data: (data) => Text(data), //讀取到資料後會在這裡處理,data指的是回傳的資料
loading: () => CircularProgressIndicator(), //讀取中顯示的值
error: (error, stackTrace) => Text('Error: $error'), //讀取錯誤的回傳值
);
})
(5)StreamProvider
這個用法與futureProvider相同,就是其宣告成全域變數的方法為一個Stream,而在宣告時用StreamProvider<回傳類型>((ref){ })取代FutureProvider<回傳類型>((ref) async {})
(6)ChangeNotifierProvider
ChangeNotifierProvider官方並不推薦使用,但在實際使用上,這個方法相對於較於靈活,簡單,如果你的狀態很多,採用這個方法比較容易宣告很多的變數。
宣告成全域變數的方法如下:
final counterProvider = ChangeNotifierProvider<MychangerNotifierProvider>(
(ref) => MychangerNotifierProvider());
其回傳類別的寫法,裡面設了兩個變數_value及_value1
class MychangerNotifierProvider extends ChangeNotifier {
var _value = 0;
var _value1 = 0;
int get value => _value;
int get value1 => _value1;
void increament() {
_value++;
notifyListeners();
}
void decreament() {
_value1--;
notifyListeners();
}
}要注意擴充的類別為ChangeNotifier,而在其方法裡面,在執行完後,要加上notifyListeners();更新畫面,否則畫面不會更新。
而若要顯示於畫面的寫法如下:
final counter = ref.watch(counterProvider);
其後可能在畫面中,適當的地方顯示出這個counter,如Text(counter.value)), 或是Text(counter.value1)),其中value是你於changeNotifier中宣告的值,這樣就可以做到隨時監聽,在畫面中改變數字的效果。
在顯示方面,可配合Consumer進行小範圍的更新,減少畫面的重新繪製,減少資源浪費。
宣告counter為CounterProvider。
.family
有時候,在使用ref時,會有丟入參數的需要,而.family就可以在引用時丟入一個參數。
宣告的方法如下:
//.family
final message = Provider.family((ref, int myvalue) {
return Location2(providerRef: ref, number: myvalue);
});
第二個參數,就是在引用時,要丟入的值。
回傳的loction2類的寫法如下:
class Location2 {
final ProviderRef providerRef;
int number;
Location2({required this.providerRef, required this.number});
int get info {
return number;
}
count() {
number++;
}
}
而若要顯示於畫面的寫法如下:
final message=ref.watch(messageProvider(5));
記得要在引入的Provider後加入你要給的參數。
其後可能在畫面中,適當的地方顯示出這個message,如Text(message.info.toString())。
參考網頁:
Youtube視訊,示範各個不同riverpod的用法
Flutter状态管理之Riverpod
riverpod實作,透過Appwrite複刻一個Twitter
Flutter App Development Course – Build a Twitter Clone with Appwrite and Riverpod
Flutter Riverpod 輕鬆學(二),一些進階用法!