ViewModel X

一个类似Android的state management包,可轻松实现MVVM模式。

注意: 在Android中,ViewModel具有在配置更改时保留状态的特殊功能。此包中的ViewModel不是为此目的设计的,因为Flutter项目不需要它。它仅用于将View Logic与UI分离。

? 示例项目的实时演示: https://shubham-gupta-16.github.io/view_model_x/

功能

  • 简化的 ? 状态管理
  • 与Android项目相似的代码模式 ?
  • 方便开发者从Android迁移到Flutter ?️
  • 允许开发者轻松地同时处理Android和Flutter项目 ?
  • 易于实现MVVM模式 ?

软件包组件

  • StateFlow, MutableStateFlow (相当于LiveData) ⛵
  • SharedFlow, MutableSharedFlow ?
  • ViewModel (将视图逻辑与UI分离,类似于Cubit)
  • ViewModelProvider
  • StateFlowBuilder
  • StateFlowConsumer
  • StateFlowListener
  • SharedFlowListener
  • MultiFlowListener
  • ChangeNotifierProvider
  • MultiProvider

用法

my_view_model.dart

class CounterViewModel extends ViewModel {
  // initialize MutableStateFlow with initial value 1
  final _counterStateFlow = MutableStateFlow<int>(1);

  StateFlow<int> get counterStateFlow => _counterStateFlow;

  // you can also define more the one StateFlow or SharedFlow inside any ViewModel

  void increment() {
    // by changing the value, listeners notified
    _counterStateFlow.value = _counterStateFlow.value + 1;
  }

  @override
  void dispose() {
    // must dispose all the StateFlows and SharedFlows
    _counterStateFlow.dispose();
  }
}

main.dart

void main() => runApp(CounterApp());

class CounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ViewModelProvider(
        create: (_) => CounterViewModel(),
        child: CounterPage(),
      ),
    );
  }
}

counter_page.dart

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ViewModel Counter Example')),
      body: Center(
        // implement StateFlowBuilder to rebuild Text on StateFlow value changed/updated
        child: StateFlowBuilder(
            // pass your StateFlow
            stateFlow: context.vm<CounterViewModel>().counterStateFlow,
            builder: (context, value) {
              return Text("$value", style: const TextStyle(fontSize: 30));
            },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // call the increment function which is inside MyViewModel
          ViewModelProvider.of<CounterViewModel>(context).increment();
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

软件包组件

ViewModel (创建自定义ViewModel类)

创建您的自定义View-Model,它必须继承自ViewModel。在其中声明所有Flow和与View相关的逻辑。不要忘记在ViewModeldispose方法中处理所有Flow。

class CustomViewModel extends ViewModel {
  // initialize StateFlow
  final _myStateFlow = MutableStateFlow<int>(1);

  StateFlow<int> get myStateFlow => _myStateFlow;

  // view related logic here

  @override
  void dispose() {
    // must dispose all flows
    _myStateFlow.dispose();
  }
}

PostFrameCallback 与 ViewModel

这将有助于在ViewModel中轻松获取onPostFrameCallback事件。通过使用PostFrameCallback,我们可以从

WidgetsBinding.instance.addPostFrameCallback((_){
  // do stuffs here
})

to

class CustomViewModel extends ViewModel with PostFrameCallback {
  //...
  
  @override
  void onPostFrameCallback(Duration timestamp) {
    // do stuffs here
  }
}

MutableStateFlow 和 StateFlow

MutableStateFlow继承自StateFlow。它存储值并在值更改时通知监听器。它可以更改/更新值。

建议初始化私有的MutableStateFlow,并为其创建公共的StateFlow getter。

final _myStateFlow = MutableStateFlow<int>(1, notifyOnSameValue: true);
StateFlow<int> get myStateFlow => _myStateFlow;

在此,notifyOnSameValue是可选的。如果将notifyOnSameValue设置为false,每当您调用stateFlow.value = newValue(其中newValue与当前值相同)时,它都不会通知监听器。默认情况下设置为true。

更改值

_myStateFlow.value = 5; // listeners were automatically notified

更新值

_listStateFlow.update((value) {
  value.add(obj);
}); // listeners were automatically notified

MutableSharedFlow 和 SharedFlow

MutableSharedFlow继承自SharedFlow。它用于将数据发送到监听器。它可以发射值。

建议初始化私有的MutableSharedFlow,并为其创建公共的SharedFlow getter。

final _mySharedFlow = MutableSharedFlow<String>();
SharedFlow<int> get mySharedFlow => _mySharedFlow;

发射值

_myStateFlow.emit("Hello from ViewModel!"); // listeners were automatically notified

将ViewModel集成到Flutter Widget中

ViewModelProvider

ViewModelProvider用于将Widget包装在您的自定义ViewModel中。这需要create,它接受自定义ViewModelchild Widget。

ViewModelProvider(
  create: (context) => counterViewModel, // provide your custom viewModel
  child: ChildWidget(),
);

在Widget Tree中获取ViewModel实例

ViewModelProvider.of<CustomViewModel>(context)

或者

context.vm<CustomViewModel>()

Builder, Listener, and Consumer Flutter Widgets

StateFlowBuilder

StateFlowBuilder用于重建其中的Widgets。这需要stateFlow来监听,以及builder,当stateFlow的值更改/更新时会重建。

StateFlowBuilder(
  stateFlow: context.vm<CustomViewModel>().myStateFlow, // pass StateFlow
  builder: (context, value) {
    return ChildWidget(value); // rebuild the widget with updated/changed value.
  },
)

StateFlowConsumer

StateFlowConsumer用于重建其中的Widgets并调用监听器。这需要stateFlow来监听,builderlistener。每当stateFlow的值更改/更新时,builder将重建其中的Widgets,而listener将被调用。

StateFlowConsumer(
  stateFlow: ViewModelProvider.of<CustomViewModel>(context).myStateFlow, // pass SharedFlow
  listener: (context, value) {
    // do stuff here based on value
  },
  builder: (context, value) {
    return ChildWidget(value); // rebuild the widget with updated/changed value.
  },
)

StateFlowListener

StateFlowListener用于捕获stateFlow的更改/更新值事件。这需要stateFlowlistenerchild。每当stateFlow的值更改/更新时,listener将被调用。

StateFlowListener(
  stateFlow: ViewModelProvider.of<CustomViewModel>(context).myStateFlow, // pass StateFlow
  listener: (context, value) {
    // do stuff here based on value
  },
  child: ChildWidget(),
)

SharedFlowListener

SharedFlowListener用于捕获sharedFlow发射的值。这需要sharedFlowlistenerchild。每当sharedFlow发射值时,listener将被调用。

SharedFlowListener(
  sharedFlow: ViewModelProvider.of<CustomViewModel>(context).mySharedFlow, // pass SharedFlow
  listener: (context, value) {
    // do stuff here based on value
  },
  child: ChildWidget(),
)

MultiFlowListener

MultiFlowListener是一个Flutter Widget,它合并了多个SharedFlowListenerStateFlowListener Widget。MultiFlowListener提高了可读性,并消除了嵌套多个监听器的需要。通过使用MultiFlowListener,我们可以从

SharedFlowListener(
  sharedFlow: context.vm<ViewModelA>().mySharedFlow,
  listener: (context, value) {
    // do stuff here based on value
  },
  child: StateFlowListener(
    stateFlow: context.vm<ViewModelA>().myStateFlow,
    listener: (context, value) {
      // do stuff here based on value
    },
    child: SharedFlowListener(
      sharedFlow: context.vm<ViewModelB>().anySharedFlow,
      listener: (context, value) {
        // do stuff here based on value
      },
      child: ChildA(),
    )
  )
)

to

MultiFlowListener(
  providers: [
    SharedFlowListener(
      sharedFlow: context.vm<ViewModelA>().mySharedFlow,
      listener: (context, value) {
        // do stuff here based on value
      },
    ),
    StateFlowListener(
      stateFlow: context.vm<ViewModelA>().myStateFlow,
      listener: (context, value) {
        // do stuff here based on value
      },
    ),
    SharedFlowListener(
      sharedFlow: context.vm<ViewModelB>().anySharedFlow,
      listener: (context, value) {
        // do stuff here based on value
      },
    ),
  ],
  child: ChildA(),
)

附加Provider

此包还包含一些Provider组件,并进行了一些修改。它们是:

ChangeNotifierProvider

这将允许用ChangeNotifier包装Widget。

ChangeNotifierProvider(
  create: (context) => CustomChangeNotifier(),
  child: WidgetA(),
)

获取ChangeNotifier的实例或监听notifyListeners()

ChangeNotifierProvider.of<CustomChangeNotifier>(context, listen: true)

如果listentrue,Widget将在notifyListeners()时重建。也可以用更简洁的方式编写。如果想监听notifyListeners(),请使用:

context.watch<CustomChangeNotifier>()

或者如果只想获取实例,请使用:

context.read<CustomChangeNotifier>()

注意: 此处的context.watchcontext.read来自provider库的修改。在此,类型被限制为ChangeNotifier。

MultiProvider

MultiProvider是一个Widget,它合并了多个ViewModelProviderChangeNotifierProvider Widget。MultiProvider提高了可读性,并消除了嵌套多个Widget的需要。通过使用MultiProvider,我们可以从

ViewModelProvider(
  create: (context) => ViewModelA(),
  child: ViewModelProvider(
    create: (context) => ViewModelB(),
    child: ChangeNotifierProvider(
      create: (context) => ChageNotifierA(),
      child: ChildA(),
    )
  )
)

to

MultiProvider(
  providers: [
    ViewModelProvider(
      create: (context) => ViewModelA(),
    ),
    ViewModelProvider(
      create: (context) => ViewModelB(),
    ),
    ChageNotifierProvider(
      create: (context) => ChageNotifierA(),
    ),
  ],
  child: ChildA(),
)

注意: 此MultiProvider与Provider包中的不同。它只接受ViewModelProviderChangeNotifierProvider

贡献

欢迎 Pull requests。对于重大更改,请先打开一个 issue 来讨论您想进行的更改。

GitHub

查看 Github