Flutter之狀態管理Provide

語言: CN / TW / HK

highlight: a11y-dark

前面有介紹過,資料的傳遞可以通過共享,但是隻能單向傳遞,這裡介紹一個可以雙項傳遞資料的第三方工具Provide連結

image.png

使用前的準備

首先在pubspec.yaml中配置,然後pub get,等待安裝完成

image.png

我們首先建立兩個比較簡單的控制器,測試頁面跳轉之間的資料傳遞。 ```dart import 'package:flutter/material.dart';

void main() { runApp(const MyApp()); }

class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const FirstPage(), ); } }

class FirstPage extends StatelessWidget { const FirstPage({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('first page')), body: Center( child: Text('first page count='), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.polymer), onPressed: () => Navigator.of(context) .push(MaterialPageRoute(builder: (context) => SecondPage())), ), ); } }

class SecondPage extends StatelessWidget { const SecondPage({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('second page')), body: Center( child: Text('Second page count='), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.add), onPressed: () => print('+++'), ), ); } } `` 實現要求:點選FirstPage按鈕跳轉到SecondPage`在第二個頁面修改值,然後的第一個頁面的值也能相應的改變

資料關聯模型建立

定義資料模型,通過混入 ChangeNotifier 管理監聽者(通知模式),暴露讀寫方法,資料改變的時候通知監聽者重新整理 dart // 定義資料模型,通過混入 ChangeNotifier 管理監聽者(通知模式) class CountModel with ChangeNotifier { int _count = 0; // 讀方法 int get counter => _count; // 寫方法 void increment() { _count++; notifyListeners(); // 通知監聽者重新整理 } }

資料包裝

這裡我們在根檢視這裡包裝資料,這樣子層級都可以使用。這裡使用的是ChangeNotifierProvider.value指定Value ```dart class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: CountModel(), child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const FirstPage(), )); } } ```

資料使用

首先取出模型資料Provider.of<CountModel>(context),然後就可以通過讀方法和寫方法來操作,還是比較簡便的。 dart @override Widget build(BuildContext context) { final _counter = Provider.of<CountModel>(context); return Scaffold( appBar: AppBar(title: Text('first page')), body: Center( child: Text('first page count=${_counter.counter}'), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.polymer), onPressed: () => Navigator.of(context) .push(MaterialPageRoute(builder: (context) => SecondPage())), ), ); } 這裡有一個優化的地方,在我們修改資料的時候會重新走Build方法來渲染當前的頁面,這裡會涉及到多次渲染的問題,所以我們需要控制Widget的重新整理粒度來達到優化效能的目的。

優化

不再統一取資料,而是哪裡使用的話哪裡取資料,這裡使用了一個Consumer<CountModel>,看一下consumer.dart中的註釋

在下面的示例中,當Bar資料改變的時候FooWidgetBarWidget都會重新rebuild,事實上,只有BarWidgetr需要重新整理。解決這種辦法就是需要在BarWidget外面包裝一層Consumer,這樣被包裝的Widget才會被重新整理。 dart /// Here's an example: /// ///dart /// @override /// Widget build(BuildContext context) { /// return FooWidget( /// child: BarWidget( /// bar: Provider.of(context), /// ), /// ); /// } /// /// /// In the above code, only `BarWidget` depends on the value returned by /// [Provider.of]. But when `Bar` changes, then both `BarWidget` _and_ /// `FooWidget` will rebuild. /// /// Ideally, only `BarWidget` should be rebuilt. One /// solution to achieve that is to use [Consumer]. /// /// To do so, we will wrap _only_ the widgets that depends on a provider into /// a [Consumer]: /// ///dart /// @override /// Widget build(BuildContext context) { /// return FooWidget( /// child: Consumer( /// builder: (_, bar, __) => BarWidget(bar: bar), /// ), /// ); /// } /// ```