2023年5月7日 星期日

flutter學習-riverpod

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外,也可以是Stringlist
//: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 輕鬆學,簡單處理狀態管理!

Flutter Riverpod 輕鬆學(二),一些進階用法!




沒有留言:

張貼留言