介紹一下數(shù)據(jù)與Widget之間傳遞,數(shù)據(jù)改變-->觸發(fā)Widget刷新
1.0 所需工具
- Provider
2.0 Provider
Provider 會在Widget
和Model
之間建立監(jiān)聽,以確保數(shù)據(jù)發(fā)生變化時(shí)刷新Widget
3.0 必要步驟:
-
Model
要繼承ChangeNotifier
-
Widget
要監(jiān)聽Model
數(shù)據(jù)變化
Provider 是flutter推薦使用的狀態(tài)管理工具,具體原理這里不展開分析,下面介紹一下如何快速使用,達(dá)到一個(gè)真實(shí)業(yè)務(wù)的場景.
4.0 Model創(chuàng)建
// 重要: model必須繼承ChangeNotifier
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
//數(shù)據(jù)變化后腰通過 notifyListeners() 通知所有監(jiān)聽者
notifyListeners();
}
}
4.1 監(jiān)聽Model
Model
監(jiān)聽有兩種方式
- 通過
addListener
方式 - 通過
Provider.of<T>(context)
4.1.1 通過 addListener 監(jiān)聽數(shù)據(jù)改變
當(dāng)數(shù)據(jù)改變并且調(diào)用 notifyListeners() 這里會收到回調(diào)
model.addListener(() {
//Do something
});
4.1.2 通過 Provider.of<T>(context) 方式監(jiān)聽
這里看到在build
方法中我們獲取Provider
傳入的Model
.并沒有調(diào)用addListener
.這里解釋一下.
通過查看 Provider.of
源碼中知道, Provider.of
通過 context 將本次構(gòu)建的build方法綁定,并監(jiān)聽數(shù)據(jù)改變. 所以在數(shù)據(jù)變化后將自動(dòng)調(diào)用 build 方法進(jìn)行刷新.
@override
Widget build(BuildContext context) {
final model = Provider.of<TestClass>(context);
return Container()
}
5.1 向下傳遞完整示例
下面將一個(gè)Model向下傳遞的完整實(shí)例貼出來.
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<TestClass>(
create: (context)=>TestClass(),
child: MaterialApp(
theme: ThemeData.dark(),
home: FirstScreen(),
),
);
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final model = Provider.of<TestClass>(context);
return Scaffold(
appBar: AppBar(
title: Text('Provider'),
),
body: Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
),
floatingActionButton: FloatingActionButton(
onPressed: () => model.action(),
child: Icon(Icons.add),
),
);
}
}
5.2 組件內(nèi)部通過Model控制數(shù)據(jù)刷新 完整示例
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
final TestClass source=TestClass();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Provider'),
),
body:ChangeNotifierProvider<TestClass>(
create: (context) => source,
child: Consumer<TestClass>(
child: Container(),
builder: (context, model, child) {
return Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => source.action(),
child: Icon(Icons.add),
),
);
}
}
6.0 Provider封裝
由5.2
數(shù)據(jù)內(nèi)部控制刷新示例看出,實(shí)際代碼寫起來比較麻煩.所以封裝了一下. 只需要傳入必要參數(shù)就可以.
6.1 封裝Widget
class RefreshWidget<T extends ChangeNotifier> extends StatelessWidget {
RefreshWidget(
this.builder, {
@required this.source,
this.child,
});
final T source;
final Function(BuildContext context, T value, Widget child) builder;
final Widget child;
static Builder<T>(
Function(BuildContext context, T value, Widget child) builder) {
return builder;
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
value: source,
child: Consumer<T>(
child: child,
builder: (context, model, child) {
return builder(context, model, child);
},
),
);
}
}
6.2 封裝后使用對比
封裝前
ChangeNotifierProvider<TestClass>(
create: (context) => source,
child: Consumer<TestClass>(
child: Container(),
builder: (context, model, child) {
return Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
);
},
),
),
封裝后
RefreshWidget<TestClass>(
(context, value, child) => Container(
child: Center(
child: Text(
"${value.count}",
style: TextStyle(fontSize: 50),
),
),
),
source: source),
封裝后我們只需要關(guān)心下面三點(diǎn),對與新手比較友好,不用糾結(jié)太多
- builder構(gòu)建方法(自動(dòng)補(bǔ)全)
- source數(shù)據(jù)model
- <T> model 類型
直接使用builder方法沒有方法提示, 這點(diǎn)很不友好.
封裝后將builder放到首位, 會將builder方法直接補(bǔ)全, 這才是我最終的目的, 可以有效減少control+c/v的次數(shù)
代碼很少直接貼完整Demo出來
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
notifyListeners();
}
}
class MyWidget extends StatelessWidget {
var _value = TestClass();
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
child:
Text('Hello, World! Click here~~~', style: Theme.of(context).textTheme.headline4),
onTap: _value.action,
),
RefreshWidget<TestClass>(
RefreshWidget.b((context, value, child) => Container(
child: Center(
child: Text(
"${value.count}",
style: TextStyle(fontSize: 50),
),
),
)),
source: _value)
]);
}
}
class RefreshWidget<T extends ChangeNotifier> extends StatelessWidget {
RefreshWidget(
this.builder, {
@required this.source,
this.child,
});
final T source;
final Function(BuildContext context, T value, Widget child) builder;
final Widget child;
static b<T>(
Function(BuildContext context, T value, Widget child) builder) {
return builder;
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
value: source,
child: Consumer<T>(
child: child,
builder: (context, model, child) {
return builder(context, model, child);
},
),
);
}
}