跨平台

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))),
      ),
    );
  }
}
上一篇 下一篇

猜你喜欢

热点阅读