2022年12月23日 星期五

Flutter學習 -Provider組件

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

影片:https://www.bilibili.com/video/BV1Th411J7AL/?spm_id_from=333.337.search-card.all.click&vd_source=fa9081c24751e12d42048a4d68c83d90


(1)注入方法:(八種)

(1)Provider:

Provider(
  create: (_) => MyModel(),
  child: ...
)

一般都會放在程式的最前面,這樣下面的子類都可以使用於MyModel裡面設置的變數,如下範例:

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(),
)
(6)如果在注入的地方要重複依賴,可以使用ProxyProvider:
ProxyProvider<方法一, 依賴方法一的依賴方法>(
      update: (context, 方法一, 依賴方法一的依賴方法) => 依賴方法一的依賴方法(參數: 要給的參數值),
)
(7)如果在注入的地方要重複依賴,且要即時監控更新ui,可以使用ChangeNotifierProxyProvider
ChangeNotifierProxyProvider<方法一, 依賴方法一的依賴方法>(
create: (_) => BookManagerModel(BookModel()), update: (context, 方法一, 依賴方法一的依賴方法) => BookManagerModel(bookModel),
)

(2)讀取的方法有下列幾個方法:
使 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<TR>(R cb(T value)) widget T 使 Provider.of<T>(context) watch listen: false Provider.of<T>(contextlisten: 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++;
}
}

沒有留言:

張貼留言