Jetpack for Flutter

一套受Android Jetpack启发的抽象和实用工具,用于帮助管理Flutter应用程序的状态。

特点

LiveData

状态持有者和更改通知者,它还允许读取当前值。

如果您完全使用Stream和响应式编程,您可能不需要这个。但如果您想编写命令式代码来更新状态,这应该会有所帮助。

EventQueue

用于将短暂状态推送到UI并在处理后清除。适用于在ViewModel中触发吐司/弹出窗口。

ViewModel

业务逻辑容器,它向UI公开状态、事件方法并与应用程序的其余部分通信。

用法

创建您的ViewModel并使用LiveData公开状态。

import 'package:jetpack/jetpack.dart';

class CounterViewModel extends ViewModel {
  final MutableLiveData<int> _counter = MutableLiveData(0);

  LiveData<int> get counter => _counter;

  void increment() {
    _counter.value++;
  }
}

您可以像下面描述的那样,使用BuildContext在任何地方访问您的CounterViewModel

@override
Widget build(BuildContext context) {
  final CounterViewModel viewModel = context.viewModelProvider.get();
}

您可以使用LiveDataBuilder来消耗LiveData

LiveDataBuilder<int>(
  liveData: viewModel.counter,
  builder: (BuildContext buildContext, int count) =>
  Text('$count'),
  )
)

您可以通过直接调用ViewModel上的方法,将UI事件传递给ViewModel

FloatingActionButton(
  onPressed: viewModel.increment,
  //...
)

入门

此库尚未发布。在此之前,请考虑复制viewmodel.dartlivedata.dart

为您的应用创建一个ViewModelFactory

class MyAppViewModelFactory extends ViewModelFactory {
  const MyAppViewModelFactory();

  @override
  T create<T extends ViewModel>() {
    if (T == HomeViewModel) {
      return HomeViewModel() as T;
    }
    throw Exception("Unknown ViewModel type");
  }
}

待定:为像get_it这样的依赖注入框架的用户添加说明。

在您的应用程序的根目录提供您的ViewModelFactory

void main() {
  const MyAppViewModelFactory viewModelFactory = MyAppViewModelFactory();
  runApp(const MyApp(viewModelFactory: viewModelFactory));
}

class MyApp extends StatelessWidget {
  final ViewModelFactory viewModelFactory;
  const MyApp({super.key, required this.viewModelFactory});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ViewModelFactoryProvider(
        viewModelFactory: viewModelFactory,
        child: MaterialApp(
          title: 'Flutter App',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            ),
          home: const HomePage(title: 'Home Page'),
          ),
        );
  }
}
创建一个基础WidgetPage,用`ViewModelScope`包装所有页面内容。

abstract class Page extends StatelessWidget {
  const Page({super.key});

  Widget buildContent(BuildContext context);

  @override
  Widget build(BuildContext context) {
    return ViewModelScope(builder: buildContent);
  }
}

如果您已经有一个所有页面的基类,那么可以使用ViewModelScope像上面那样包装内容。

为什么是另一个状态管理库?

这些是在Android生态系统中经过5年以上验证的模式。即使采用了全新的UI框架——Jetpack Compose,它们仍然保持完好。这些抽象由于**低耦合**和**灵活性**而能够经受住变化。

Flutter中现有的解决方案,如blocprovider等,默认将逻辑持有者限制为仅发出一个状态流,并需要额外的样板代码来“选择”UI想要响应的状态片段。

class MyLogicHolder: LogicHolder<StateModel>

有时,我们希望公开多个相关的状态流,但它们以不同的频率变化/发出。直接从ViewModel公开它们,而无需编写选择器等样板代码,非常方便且没有额外开销。

class MyViewModel: ViewModel {
  final MutableLiveData<int> _counter = MutableLiveData(0);
  final MutableLiveData<boolean> _isModified = MutableLiveData(false);

  LiveData<int> get counter => _counter;
  LiveData<int> get isModified => _isModified;

  void increment() {
    _counter.value++;
    _isModified.value = true;
  }
}

这使我们能够按照UI的消费方式来组织和传播状态,并尽量减少不必要的Widget重建。

您也可以使用FutureStream将状态公开给UI。由您**选择**。

class ProductViewModel: ViewModel {
  //

  Future<ProductDetails> productDetails = await fetchProductDetails();
  Stream<bool> isAddedToCart = cartRepository.isAddedToCart(_productId);
}

并使用FutureBuilderStreamBuilder来监听和更新UI。

无需创建额外的模型来将UI事件传递给ViewModel。只需直接调用方法即可。

ElevatedButton(
  onPressed: viewModel.increment
  //...
)

GitHub

查看 Github