ASP – 原子状态模式

ValueNotifier是Flutter反应性的简单、原生形式。此扩展旨在透明地应用函数式响应式编程(TFRP)

安装

flutter pub add asp

理解扩展。

此扩展添加了一个名为Atom的类和一个名为ValueNotifier -> Atom的转换器,以便它可以被rxObserver()函数和WidgetRxBuilder透明地观察。

Atom直接扩展自ValueListenable,因此任何实现它的对象都可以转换为Atom

AtomValueNotifier的唯一区别在于对rxObserver()RxBuilder中的观察者的自动签名函数,非常类似于MobX reactions

使用方法

要开始,请实例化一个Atom。

final counter = Atom<int>(0);

或使用.asAtom()方法转换一个已有的ValueNotifier

final counter = myValueNotifierCounter.asAtom();

重要提示:已使用扩展方法Atom()方法添加到ValueNotifier

并使用rxObserver监听更改。

RxDisposer disposer = rxObserver((){
    print(counter.value);
});

disposer();

当前作用域fn()中声明的所有值都是可观察的,并且可以生成一个在effect属性中监听的值。

RxDisposer disposer = rxObserver<String>((){
    return '${name.value} + ${lastName.value}';
}, effect: (String fullName){
  print(fullName);
});

disposer();

这是对单个反应性的透明使用,但我们也可以组合Atom Objects生成新值。此技术称为Computed

Computed:组合响应式值

要组合两个或多个Atom Objects,我们需要使用返回新组合值的getter

final num1 = Atom<int>(1);
final num2 = Atom<int>(2);

String get result => 'num1: ${num1.value} + num2: ${num2.value} = ${num1.value + num2.value}';

...

rxObserver((){
    print(result); // print´s "num1: 1 + num2: 2 = 3
});

重要提示computed必须是Getters而不是赋值。当任何一个Atom更改值时,将发生反应。

使用Getters

我们也可以在响应式值中使用getters,让我们重复上面的例子。

final _num1 = Atom<int>(1);
int get num1 => _num1.value;

final _num2 = Atom<int>(2);
int get num2 => _num2.value;

String get result => 'num1: $num1 + num2: $num2 = ${num1 + num2}';

...

rxObserver((){
    print(result); // print´s "num1: 1 + num2: 2 = 3
});

过滤器

所有Rx监听器都有一个属性过滤器filter,它是一个返回bool的函数。使用它来定义何时(或不)反映更改。

RxDisposer disposer = rxObserver<String>((){
    return '${name.value} + ${lastName.value}';
}, filter: (fullName) => fullName.isNotEmpty);

disposer();

Flutter 和 Atom

RxNotifeir 提供了有助于状态管理和向 Widget 传播的工具。

  1. 将 RxRoot Widget 添加到应用程序的根目录。

void main(){
  runApp(RxRoot(child: AppWidget()));
}
  1. 现在只需使用context.select方法,传入Atom对象。

final counter = Atom(0);

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final value = context.select(() => counter.value);

    return Scaffold(
      body: Center(
        child: Text(
          '${home.count}',
           style: TextStyle(fontSize: 23),
        )
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () => counter.count++,
      ),
    );
  }
}
  1. 要执行调用状态外部的内容的方法,如[Dialog]、[SnackBar]等,请使用context.callback来监听一个或多个[Atom],订阅回调。

 @override
  Widget build(BuildContext context) {
    context.callback(() => errorState.value, _showSnachbar);

    ...

Widgets: RxBuilder

还提供了一个用于以作用域方式管理状态的构建器。

Widget build(BuildContext context){
    return RxBuilder(
        builder: (_) => Text('${counter.value}'),
    );
}

重要提示context.select方法和构建器都有filter属性。

Widgets: RxCallback

此 Widget 可以替换context.callback,通过从 rxObserver 获取产品列表。

Widget build(BuildContext context){
    return RxCallback(
        effects: [
          rxObserver(() => errorState.value, effect: _showSnachbar)
        ]
        child: BodyWidget(),
    );
}

集合和异步

RxList

RxList 在列表值上提供了更深层次的可观察性。它跟踪何时添加、删除或修改了项,并通知观察者。当列表中的更改很重要时,请使用RxList。

RxMap

RxMap 在映射值上提供了更深层次的可观察性。它跟踪何时添加、删除或修改了键,并通知观察者。当映射中的更改很重要时,请使用RxMap。

RxSet

RxSet 在集合值上提供了更深层次的可观察性。它跟踪何时添加、删除或修改了值,并通知观察者。当集合中的更改很重要时,请使用RxSet。

RxFuture

RxFuture 是 Future 的响应式包装器。您可以使用它在 Future 的各种状态下(从挂起到完成或被拒绝)显示 UI。RxFuture 的 status、result 和 error 字段是可观察的,可以在 UI 中使用。您可以使用.value添加新的 Future。

final rxFuture = RxFuture.of(myFuture);
...

rxFuture.value = newFuture;

RxStream

用于跟踪状态和值更改的流。T initialValue:流的初始值。

实现原子状态。

可以使用asp实现Recoil Atoms模式。此模式由状态是具有自身反应性的对象组成。

atom

动机

开发者在理解Flutter中的状态管理方面仍然存在困难。在对Flutter社区和合作公司进行大量研究后,我们得出了这个结论。原子状态是一种对新手友好的状态管理方法,同时保持可靠的结构,考虑可扩展性和维护性。

更多详情,请阅读这篇关于该主题的Medium文章

规则

为了执行此方法,我们必须考虑一些架构限制。

  1. 所有状态都必须是原子(Atom实例)。
  2. 所有动作都必须是原子(Atom实例)。
  3. 业务规则必须在Reducer中创建,而不是在Atom中。

我们将有3个主要层,它们是:AtomsReducersViews

atom

请注意,View(即表示层)不知道Reducer(即业务规则执行层)。这两层共享原子,这些原子反过来代表状态和状态操作的调度。

Atom`s

Atom代表应用程序的响应式状态。每个Atom都有自己的反应性。

// atoms
final productsState = <Product>[].asAtom();
final productTextFilterState = Atom<String>('');

// computed
List<Product> get filteredProductsState {
     if(productTextFilterState.value.isEmpty()){
         return productsState.value;
     }

     return productsState.where(
         (p) => p.title.contains(productTextFilterState.value),
     );
}

// actions
final selectedProductState = Atom<Product?>(null);
final fetchProductsState = Atom.action();

Reducer

在此架构中,您被迫分离状态管理和业务规则,这乍一看可能显得奇怪,因为我们总是像BLoCChangeNotifier一样在同一层管理和减少状态。然而,分离状态管理和业务规则执行将帮助我们将多个状态分发到同一个 Widget,并且这些多个状态不需要通过facadeproxy预先连接。

负责做出业务决策的层将被称为Reducer

class ProductReducer extends Reducer {

    ProductReducer(){
        on(() => [fetchProductsState.action], _fetchProducts);
        on(() => [selectedProductState.value], _selectProduct);
    }

    void _fetchProducts(){
        ...
    }

    void _selectProduct(){
        ...
    }
}

Reducers可以注册监听Atom反应性的方法/函数。

View (Widget)

任何 Widget 都可以监听一个或多个 Atom 的更改,前提是它们将RxRoot Widget 作为其祖先。kcontext.select()方法通过扩展添加到BuildContext,并且可以从任何类型的 Widget、StatefulWidgetStatelessWidget调用。

...
Widget build(BuildContext context){
     final products = context.select(
                 () => filteredProductsState.value
              );
     ...
}

示例

使用Atom的Flutter项目

简单的计数器.

购物车.

功能和 Bug

请将功能请求和错误发送到issue tracker。

此 README 是根据 Stagehand 在 BSD 风格许可下提供的模板创建的。

GitHub

查看 Github