Flutter了解之入门篇9-1(状态管理库)
2022-09-20 本文已影响0人
平安喜乐698
目录
1. Provider库
2. scoped_model库
3. Redux库
4. MobX库
对比
1. Provider库
// 最大的好处:UI与业务分离
Provider(
create: (_) => Model(),
child: someWidget(),
);
Provider类本身并不会在状态改变的时候自动更新子组件,因此更常用的是其子类:
1. ListenableProvider
监听实现了Listenable的的对象,并将其暴露给下级组件。
当触发一个事件后会通知有依赖的子组件重建。
2. ChangeNotifierProvider(最常用,ListenableProvider的子类)
监听实现了ChangeNotifier接口的对象,当该对象调用notifyListeners时通知全部的监听组件更新组件。
3. ValueListenableProvider
监听实现了ValueListenable接口的对象,当该对象改变时更新其下级组件。
4. StreamProvider
监听Stream对象,然后将其内容暴露给子组件。
通常是向一个组件以流的方式提供大量的内容。
使用状态数据:
1. context.read<T>()
返回T类型状态数据对象(不会监听该对象的变化,只读)。
2. context.watch<T>()
返回T类型状态数据对象(监听该对象的变化)
3. context.select<T,R>(R cb(T value))
返回T类型对象中的R类型对象(只监听状态对象的指定数据)。
只有当R类型值发生改变时,才会通知依赖该数据的组件刷新,而其他不依赖该值的组件不会刷新。
MultiProvider(专门为了解决多状态共存的情况而设计)定义如下:
MultiProvider({
Key? key,
providers: List<SingleChildWidget>,
Widget? child,
TransitionBuilder? builder
})
ChangeNofitierProvider.value方法
可以利用一个已有的对象(可以供其他组件共用)提供状态管理。
class DynamicAddWrapper extends StatelessWidget {
DynamicAddWrapper({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: DynamicShareModel.sharedDynamicModel,
child: _DynamicAdd(),
);
}
}
StreamProvider的2种形式
// create 形式
StreamProvider({
Key? key,
required Create<Stream<T>?> create,
required T initialData,
ErrorBuilder<T>? catchError,
UpdateShouldNotify<T>? updateShouldNotify,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
})
// value 形式
StreamProvider.value({
Key? key,
required Stream<T>? value,
required T initialData,
ErrorBuilder<T>? catchError,
UpdateShouldNotify<T>? updateShouldNotify,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
})
使用:
一个provider
ChangeNotifierProvider( // ChangeNotifierProvider组件(为子节点提供ChangeNotifier实例)
create: (context) => CartModel(),
child: HelloWidget(),
),
);
多个provider
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CartModel()),
ChangeNotifierProvider(create: (context) => DynamicModel()),
Provider(create: (context) => SomeOtherClass()),
],
child: HelloWidget(),
),
);
子组件中使用(越深越好:可以避免构建大量UI)
Consumer<CartModel>(
// 在 ChangeNotifier对象发生改变时会被调用
// context:当前上下文,cart:触发build函数调用的ChangeNotifier实例对象,child:用于优化
builder: (context, cart, child) => Stack (
children: [
if(child != null) child,
Text('Total price is ${cart.totalPrice}'),
],
),
child: const SomeExpensiveWidget(), // 不需要改变
);
访问
onPressed: () {
// listen为false时 状态改变时不会通知该组件进行重建。
Provider.of<CartModel>(context, listen: false).removeAll();
}
示例(context.select)
class DynamicDetailWrapper extends StatelessWidget {
final String id;
const DynamicDetailWrapper({Key? key, required this.id}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<DynamicDetailModel>(
create: (context) => DynamicDetailModel(),
child: Stack(
children: [
_DynamicDetailPage(id),
Positioned(
bottom: 0,
height: 60,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_PraiseButton(),
_FavorButton(),
],
))
],
));
}
}
// _DynamicDetailPage
Widget build(BuildContext context) {
print('_DynamicDetailPage');
DynamicEntity? currentDynamic =
context.select<DynamicDetailModel, DynamicEntity?>(
(dynamicDetail) => dynamicDetail.currentDynamic,
);
return Scaffold(
appBar: AppBar(
title: Text('动态详情'),
brightness: Brightness.dark,
),
body: currentDynamic == null
? Center(
child: Text('请稍候...'),
)
: _getDetailWidget(currentDynamic),
);
}
}
// _PraiseButton
class _PraiseButton extends StatelessWidget {
const _PraiseButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('PraiseButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
context.read<DynamicDetailModel>().updatePraiseCount();
},
child: Text(
context.select<DynamicDetailModel, String>(
(DynamicDetailModel dyanmicDetail) {
return '点赞 ' + dyanmicDetail.praiseCount.toString();
},
),
style: TextStyle(color: Colors.white),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith((states) =>
Size((MediaQuery.of(context).size.width / 2 - 1), 60))),
),
);
}
}
2. scoped_model库
class HelloModel extends Model{
int _count=0;
int get count=>_count;
void increaseCount(){
_count++;
notifyListeners();
}
}
class HelloPageWidget extends StatelessWidget{
@override
Widget build(BuildContext context) {
return ScopedModel(
model: HelloModel(),
child: Scaffold(
appBar: AppBar(
title: Text('hello'),
elevation: 1.0,
),
body: HelloWidget(),
floatingActionButton: ScopedModelDescendant<HelloModel>(
rebuildOnChange: false, // 不需要重建
builder: (context,_,model)=>FloatingActionButton(
child: Icon(Icons.add),
onPressed: model.increaseCount,
),
),
),
);
}
class HelloWidget extends StatelessWidget{
@override
Widget build(BuildContext context) {
return HellWorldWidget();
}
}
class HellWorldWidget extends StatelessWidget{
@override
Widget build(BuildContext context) {
return ScopedModelDescendant<HelloModel>(builder:(context,_,model)=>ActionChip(
label: Text('${model.count}'),
onPressed: model.increaseCount,
));
}
}
3. Redux库
Redux定义如下:
typedef Reducer<State> = State Function(State state, dynamic action)
拦截器/中间件(就是一个方法)定义如下:
void (Store<T> store, action, NextDispatcher next)
Action与中间件相关联(避免if过多)
TypedMiddleware<T, Action>(middlewareFunction)
StoreBuilder定义如下:
StoreBuilder({
Key? key,
required this.builder,
this.onInit,
this.onDispose,
this.rebuildOnChange = true,
this.onWillChange,
this.onDidChange,
this.onInitialBuild,
}) : super(key: key);
Redux通过Store存储了代表整个应用的状态对象State,每一个应用事件(用户发起或外部驱动)都被认为是一个 Action(通过Reducer进行调度)。Reducer根据Action来更新Store中的状态(Reducer接收一个Action和当前的状态并返回一个新的状态对象)。而一旦Store中的状态对象 State改变了,对应的视图View就会反映状态的变化。
中间件(Reducer前处理Action的组件),接收一个Action和当前的状态,进行处理(如:网络请求,数据库访问等)。
如果一个 Store 需要被下级组件使用,那么就要定义在上一级组件中。
每次Store改变时(即使Action中什么都没做),组件都会被重建。为了保证只有在ViewModel状态改变时重建,需要重写ViewModel的==和hashCode方法,且在StoreConnector中设置distinct属性为true(action返回的state相同时store不会发送onChange事件)。
StoreConnector 构建的 Widget 在状态发生改变的时候,并不会重建整个子组件,而是只更新依赖于 converter 转换后对象的组件。
规范了代码:使状态管理的业务模式一致,都必须具备Action、Store、Reducer
redux
Flutter中需要的全部Redux组件:Store、Reducer、Middleware
flutter_redux
Flutter专属插件包,在redux基础上进行了封装。
StoreProvider(为组件提供 Store的基础 Widget)
StoreBuilder(从 StoreProvider 中接收 Store 的组件)
StoreConnector(用于替换 StoreBuilder,将Store转换为一个ViewModel来构建组件树,Store中的State改变后重建StoreConnector)。
1. StoreProvider
基础Widget,把指定的Redux状态数据(Redux Store) 传递给需要的下级组件;
2. StoreBuilder
一个从StoreProvider获取状态的下级组件,它会将获取到的状态传递给一个返回Widget的builder方法。
3. StoreConnector
一个从最临近的StoreProvider祖先组件获取状态的下级组件,然后利用指定的converter将状态转换为 ViewModel对象后给到builder方法。任何时候,状态发出一个更改事件后,该组件会被自动重建,从而无需主动管理事件订阅。
示例
// counter_actions.dart,目前为空,后续可以增加业务代码
// 计数器增加
class CounterAddAction {}
//计数器减少
class CounterSubAction {}
//counter_state.dart
class CounterState {
int count = 0;
CounterState(this.count);
}
// counter_reducer.dart
CounterState counterReducer(CounterState state, dynamic action) {
if (action.runtimeType == CounterAddAction) {
return CounterState(state.count + 1);
}
if (action.runtimeType == CounterSubAction) {
return CounterState(state.count - 1);
}
return state;
}
=====================
使用
class CounterPage extends StatelessWidget {
CounterPage({Key? key}) : super(key: key);
final store =
Store<CounterState>(counterReducer, initialState: CounterState(0));
@override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: Scaffold(
appBar: AppBar(
title: Text('Redux Counter'),
),
body: Center(
child: StoreConnector<CounterState, String>(
converter: (store) => store.state.count.toString(),
builder: (context, count) {
return Text(
count,
style: TextStyle(color: Colors.blue, fontSize: 30),
);
},
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
store.dispatch(CounterAddAction()); // 执行Action
}),
FloatingActionButton(
backgroundColor: Colors.red,
child: Icon(
Icons.arrow_downward,
),
onPressed: () {
store.dispatch(CounterSubAction());
}),
],
),
),
);
}
}
示例(中间件)
// action文件
class RefreshAction {}
class LoadAction {}
class SuccessAction {
final List<dynamic> jsonItems;
final int currentPage;
SuccessAction(this.jsonItems, this.currentPage);
}
class FailedAction {
final String errorMessage;
FailedAction(this.errorMessage);
}
// state文件
class ContactorState {
final List<dynamic> contactors;
final isLoading;
final String? errorMessage;
final int currentPage;
ContactorState(this.contactors,
{this.isLoading = false, this.errorMessage, this.currentPage = 1});
factory ContactorState.initial() => ContactorState(List.unmodifiable([]));
}
// reducer文件
ContactorState contactorReducer(ContactorState state, dynamic action) {
if (action is RefreshAction) {
ContactorState contactorState = ContactorState(state.contactors,
isLoading: true, errorMessage: null, currentPage: 1);
return contactorState;
}
if (action is LoadAction) {
ContactorState contactorState = ContactorState(state.contactors,
isLoading: true,
errorMessage: null,
currentPage: state.currentPage + 1);
return contactorState;
}
if (action is SuccessAction) {
int currentPage = action.currentPage;
List<dynamic> contactors = state.contactors;
if (currentPage > 1) {
contactors += action.jsonItems;
} else {
contactors = action.jsonItems;
}
ContactorState contactorState = ContactorState(contactors,
isLoading: false, errorMessage: null, currentPage: currentPage);
return contactorState;
}
if (action is FailedAction) {
ContactorState contactorState = ContactorState(
state.contactors,
isLoading: false,
errorMessage: action.errorMessage,
);
return contactorState;
}
return state;
}
===========================
// 创建store时添加中间件
final Store<ContactorState> store = Store(
contactorReducer,
initialState: ContactorState.initial(),
middleware: [
fetchContactorMiddleware,
],
);
// 中间件
void fetchContactorMiddleware(
Store<ContactorState> store, action, NextDispatcher next) {
const int pageSize = 10;
if (action is RefreshAction) {
// 刷新取第一页数据
ContactorService.list(1, pageSize).then((response) {
if (response != null && response.statusCode == 200) {
store.dispatch(SuccessAction(response.data, 1));
} else {
store.dispatch(FailedAction('请求失败'));
}
}).catchError((error, trace) {
store.dispatch(FailedAction(error.toString()));
});
}
if (action is LoadAction) {
// 加载更多时页码+1
int currentPage = store.state.currentPage + 1;
ContactorService.list(currentPage, pageSize).then((response) {
if (response != null && response.statusCode == 200) {
store.dispatch(SuccessAction(response.data, currentPage));
} else {
store.dispatch(FailedAction('请求失败'));
}
}).catchError((error, trace) {
store.dispatch(FailedAction(error.toString()));
});
}
next(action);
}
//
class _ViewModel {
final List<_ContactorViewModel> contactors;
_ViewModel(this.contactors);
factory _ViewModel.create(Store<ContactorState> store) {
List<_ContactorViewModel> items = store.state.contactors
.map((dynamic item) => _ContactorViewModel.fromJson(item))
.toList();
return _ViewModel(items);
}
}
class _ContactorViewModel {
final String followedUserId;
final String nickname;
final String avatar;
final String description;
_ContactorViewModel({
required this.followedUserId,
required this.nickname,
required this.avatar,
required this.description,
});
static _ContactorViewModel fromJson(Map<String, dynamic> json) {
return _ContactorViewModel(
followedUserId: json['followedUserId'],
nickname: json['nickname'],
avatar: UploadService.uploadBaseUrl + 'image/' + json['avatar'],
description: json['description']);
}
}
//
@override
Widget build(BuildContext context) {
return StoreProvider<ContactorState>(
store: store,
child: Scaffold(
//省略 appBar
body: StoreConnector<ContactorState, _ViewModel>(
converter: (Store<ContactorState> store) => _ViewModel.create(store),
builder: (BuildContext context, _ViewModel viewModel) {
return EasyRefresh(
child: ListView.builder(
itemBuilder: (context, index) {
return ListTile(
leading:
_getRoundImage(viewModel.contactors[index].avatar, 50),
title: Text(viewModel.contactors[index].nickname),
subtitle: Text(
viewModel.contactors[index].description,
style: TextStyle(fontSize: 14.0, color: Colors.grey),
),
);
},
itemCount: viewModel.contactors.length,
),
onRefresh: () async {
store.dispatch(RefreshAction());
},
onLoad: () async {
store.dispatch(LoadAction());
},
firstRefresh: true,
);
},
),
// 省略其他代码
),
);
Fish Redux
闲鱼出品的一个基于Redux的整体应用框架,对于构建大中型应用来说很合适。
与 Redux 的区别是,Fish Redux 是一个应用框架,解决了如应用分治、通信、数据驱动、解耦等问题。
4. MobX库
基于观察者模式和响应式模式的状态管理库,将响应式数据和UI绑定(绑定是完全自动的)。
添加flutter_mobx插件后,只需要使用Observer的builder属性包裹依赖状态数据的子组件,状态发生改变后会自动刷新。
Observer(
builder: (_) => Text(
'${_counter.value}',
style: const TextStyle(fontSize: 20),
),
),
Observer类的关系图
响应式无感知刷新的具体实现:
1. 控制渲染的Element是StatelessObserverElement,该类在mount阶段通过createReaction注册了reaction。
2. StatelessObserverElement在build方法中reaction和observable进行绑定。
3. 在Observer中读取状态对象属性时,会调用到其get方法,该方法会将状态对象属性与对应的Observer组件 进行绑定。
4. 当状态对象的属性被set更改的时候,会调度到该属性绑定的reaction,执行_onInvalidate方法来进行刷新。
=========================
StatelessObserverWidget定义如下:
abstract class StatelessObserverWidget extends StatelessWidget
with ObserverWidgetMixin { // 混入ObserverWidgetMixin
/// Initializes [key], [context] and [name] for subclasses.
const StatelessObserverWidget(
{Key? key, ReactiveContext? context, String? name})
: _name = name,
_context = context,
super(key: key);
final String? _name;
final ReactiveContext? _context;
@override
String getName() => _name ?? '$this';
@override
ReactiveContext getContext() => _context ?? super.getContext();
@override
// 返回的是一个StatelessObserverElement对象(用于控制Element的刷新)。
StatelessObserverElement createElement() => StatelessObserverElement(this);
}
=========================
ObserverWidgetMixin定义如下:
mixin ObserverWidgetMixin on Widget {
String getName();
ReactiveContext getContext() => mainContext;
@visibleForTesting
// createReaction方法在ObserverElementMixin中调用
// 创建reaction,状态改变后会调用reaction
Reaction createReaction(
Function() onInvalidate, {
Function(Object, Reaction)? onError,
}) =>
ReactionImpl(
getContext(),
onInvalidate,
name: getName(),
onError: onError,
);
void log(String msg) {
debugPrint(msg);
}
}
=========================
StatelessObserverElement继承自StatelessElement仅仅是混入了ObserverElementMixin,
ObserverElementMixin定义如下:
mixin ObserverElementMixin on ComponentElement {
ReactionImpl get reaction => _reaction;
late ReactionImpl _reaction;
ObserverWidgetMixin get _widget => widget as ObserverWidgetMixin;
@override
// 重载mount方法,调用createReaction创建reaction,响应方法为invalidate
void mount(Element? parent, dynamic newSlot) {
_reaction = _widget.createReaction(invalidate, onError: (e, _) {
FlutterError.reportError(FlutterErrorDetails(
library: 'flutter_mobx',
exception: e,
stack: e is Error ? e.stackTrace : null,
));
}) as ReactionImpl;
super.mount(parent, newSlot);
}
// 状态数据改变时通过reaction来调用markNeedsBuild通知Element刷新。
// 标记为需要重建。
void invalidate() => markNeedsBuild();
@override
// 将observer对象和其依赖(Observer的builder返回的widget)进行绑定
Widget build() {
late Widget built;
reaction.track(() {
built = super.build();
});
if (!reaction.hasObservables) {
_widget.log(
'No observables detected in the build method of ${reaction.name}',
);
}
return built;
}
@override
void unmount() {
reaction.dispose();
super.unmount();
}
}
再看MobX(带有@observable)自动生成的代码:
final _$praiseCountAtom = Atom(name: 'ShareStoreBase.praiseCount');
@override
int get praiseCount {
// 最终调用的是_reportObserved方法。
_$praiseCountAtom.reportRead();
return super.praiseCount;
}
@override
set praiseCount(int value) {
// 会出发reaction回调
_$praiseCountAtom.reportWrite(value, super.praiseCount, () {
super.praiseCount = value;
});
}
=========================
_reportObserved定义如下:
// 将之前Observer绑定的依赖和对应的状态对象属性关联起来。
void _reportObserved(Atom atom) {
final derivation = _state.trackingDerivation;
if (derivation != null) {
derivation._newObservables!.add(atom);
if (!atom._isBeingObserved) {
atom
.._isBeingObserved = true
.._notifyOnBecomeObserved();
}
}
}
==========================
// reaction的run方法
void _run() {
if (_isDisposed) {
return;
}
_context.startBatch();
_isScheduled = false;
if (_context._shouldCompute(this)) {
try {
_onInvalidate(); // 触发widget的build方法
} on Object catch (e, s) {
// Note: "on Object" accounts for both Error and Exception
_errorValue = MobXCaughtException(e, stackTrace: s);
_reportException(_errorValue!);
}
}
_context.endBatch();
}
核心三要素
1. Observables
响应式状态
简化(自动生成.g.dart文件)
添加builder_runner和mobx_codegen插件
添加注解@observable、@readonly只读、@computed派生状态(依赖其他核心状态)、@action
终端使用flutter packages pub run build_runner build命令
例(简化)
========================
原代码
class Counter {
Counter() {
increment = Action(_increment);
}
Action increment;
void _increment() {
_value.value++;
}
final _value = Observable(0);
int get value => _value.value;
set value(int newValue) => _value.value = newValue;
}
========================
改为自动生成
import 'package:mobx/mobx.dart';
part 'counter.g.dart';
class Counter = CounterBase with _$Counter;
abstract class CounterBase with Store {
@observable
int value = 0;
@action
void increment() {
value++;
}
}
2. Actions
定义了如何改变 observables对象。
actions是可嵌套的,这时只有最顶层的action完成后才会发出通知。
对于异步操作,MobX 会自动处理,而无需使用runInAction来包裹
3. Reactions
观察者,一旦跟踪的observable对象发生改变后就会被通知到。
在reaction中读取observables时就已经自动跟踪该对象了,无需显式绑定。
所有方式都会返回一个ReactionDisposer方法,调用该方法可以销毁该reaction。
1. autorun 方法
String greeting = Observable('Hello World');
final dispose = autorun((_){
print(greeting.value);
});
greeting.value = 'Hello MobX';
dispose();
2. reaction方法
// 在predicate方法中监测observables对象,然后当predicate返回不同的值时会执行effect方法。且只有 predicate中的observables对象会被跟踪。
ReactionDisposer reaction<T>(T Function(Reaction) predicate, void Function(T) effect)
final dispose = reaction((_) => greeting.value, (msg) => print(msg));
3. when方法
// 当predicate方法返回true时才执行effect方法。当effect方法运行后,将会自动销毁,可以手动提前销毁。
ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)
final dispose = when((_) => greeting.value == 'Hello MobX', () => print('So so'));
4. Future 异步方法
// 和when方法类似,只是返回的结果是一个Future对象——在 predicate 方法返回 true 的时候完成。
Future<void> asyncWhen(bool Function(Reaction) predicate)
await asyncWhen(() => _completed.value == true);
快捷代码片段(VSCode的自定义代码模板功能)
模板代码(一个配置的json字符串)
例(StatefulWidget模版代码对应的json)
"Stateful Widget": {
"prefix": "statefulW",
"body": [
"class ${1:name} extends StatefulWidget {",
" ${1:name}({Key? key}) : super(key: key);\n",
" @override",
" _${1:WidgetName}State createState() => _${1:WidgetName}State();",
"}\n",
"class _${1:index}State extends State<${1:index}> {",
" @override",
" Widget build(BuildContext context) {",
" return Container(",
" child: ${2:null},",
" );",
" }",
"}"
],
"description": "Create a Stateful widget"
},
具体的语法遵循 TextMate规范
${1:name} 代表:第1个变量 指定占位符是name
之后使用${1:xxx}:引用该变量
例2(将之前那个可以自动生成的文件对应的模版代码json如下)
{
"Build Mobx Observables" : {
"prefix": "build mobx",
"scope": "dart",
"body": [
"import 'package:mobx/mobx.dart';\n",
"part '${TM_FILENAME_BASE}.g.dart';\n",
"class ${1:name}$0 = $1Base with _$$1;\n",
"abstract class $1Base with Store {",
" @observable",
" ${2:type} ${3:property} = ${4:initial};\n",
" @action",
" void ${5:actionname}() {\n",
" }",
"}",
]
}
}
TM_FILENAME 完整文件名、TM_FILENAME_BASE 不带后缀文件名
VSCode|Code->Preferences->User Snippets | New Snippets 输入快捷名
MobX 本身不支持上级组件状态可以被下级组件共享,需要借助Provider或GetIt来实现状态共享。
示例(借助Provider共享状态)
class DynamicDetailProvider extends StatelessWidget {
DynamicDetailProvider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 省略其他代码
child: Provider(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_PraiseButton(),
_FavorButton(),
],
),
create: (context) => ShareStore(),
),
}
}
class _FavorButton extends StatelessWidget {
const _FavorButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
context.read<ShareStore>().increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${context.read<ShareStore>().favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
示例(借助GetIt共享状态)
class DynamicDetailGetIt extends StatelessWidget {
DynamicDetailGetIt({Key? key}) : super(key: key) {
GetIt.I.registerSingleton<ShareStore>(ShareStore());
}
//省略 build 方法
}
class _FavorButton extends StatelessWidget {
const _FavorButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
GetIt.I.get<ShareStore>().increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${GetIt.I.get<ShareStore>().favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}