Flutter管理Flutter应用程序中的状态插件jetpack的使用
Flutter管理Flutter应用程序中的状态插件jetpack的使用
Jetpack for Flutter
Jetpack for Flutter是一组从Android Jetpack中汲取灵感的抽象和工具,旨在帮助管理Flutter应用程序中的状态。
功能
LiveData
作为状态持有者和更改通知器,还允许读取当前值。如果你完全依赖于Stream
和响应式编程,可能不需要这个特性。但如果你想通过命令式代码更新状态,这将有所帮助。
EventQueue
用于推送短暂的状态到UI并在处理后清除。对于从ViewModel
内部触发吐司/弹出窗口非常有用。
ViewModel
业务逻辑容器,它暴露状态和事件方法给UI,并与应用程序的其余部分通信。
安装
$ flutter pub add jetpack
使用
创建你的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.viewModel();
}
并且你可以使用LiveDataBuilder
消费LiveData
:
LiveDataBuilder<int>(
liveData: viewModel.counter,
builder: (BuildContext buildContext, int count) =>
Text('$count'),
)
你还可以通过调用ViewModel
上的方法传递UI事件:
FloatingActionButton(
onPressed: viewModel.increment,
//...
)
在Flutter项目中的一次性设置
创建一个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
,你的工厂可以像这样:
class MyAppViewModelFactory extends ViewModelFactory {
const MyAppViewModelFactory();
@override
T create<T extends ViewModel>() {
return GetIt.I.get<T>();
}
}
在你的App根部提供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});
@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'),
));
}
}
创建一个基础小部件Page
来包装所有页面内容与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之后,它们仍然保持不变。这些抽象由于低耦合和灵活性而具有强大的适应能力。
现有解决方案如bloc
、provider
等默认情况下仅限于发出一个状态流,并且需要额外的样板代码来“选择”UI想要反应的状态片段。
有时候,我们希望暴露多个不同但相关联的状态流,这些流以不同的频率变化/发出。直接从ViewModel
暴露它们,无需编写选择器等样板代码是非常方便的,没有任何成本。
这使我们能够组织和传播状态,以便按照其在UI中被消耗的方式进行最小化不必要的小部件重建。
你也可以使用Future
和Stream
暴露状态给UI。你的选择。
并且不需要为将UI事件传达给ViewModel
创建额外的模型。只需直接调用方法即可。
示例代码
以下是一个完整的示例,展示了如何在一个简单的计数器应用中使用Jetpack插件:
import 'package:flutter/material.dart';
import 'package:jetpack/jetpack.dart';
// 创建ViewModel
class CounterViewModel extends ViewModel {
final MutableLiveData<int> _counter = MutableLiveData(0);
LiveData<int> get counter => _counter;
void increment() {
_counter.value++;
}
}
// ViewModelFactory
class ExampleViewModelFactory extends ViewModelFactory {
const ExampleViewModelFactory();
@override
T create<T extends ViewModel>() {
if (T == CounterViewModel) {
return CounterViewModel() as T;
}
throw Exception("Unknown ViewModel type");
}
}
void main() {
const ExampleViewModelFactory viewModelFactory = ExampleViewModelFactory();
runApp(const MyApp(viewModelFactory: viewModelFactory));
}
class MyApp extends StatelessWidget {
final ViewModelFactory viewModelFactory;
const MyApp({super.key, required this.viewModelFactory});
@override
Widget build(BuildContext context) {
return ViewModelFactoryProvider(
viewModelFactory: viewModelFactory,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'Simple Scope Demo'),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: ViewModelScope(
builder: (_) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Here are two counter components in the same scope. They react to clicks on each other because they are talking to the same ViewModel'),
),
const Counter(),
const Counter(),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const MultiScopeDemo()));
},
child: const Text('NEXT'))
],
),
),
),
);
}
}
class Counter extends StatelessWidget {
const Counter({super.key});
@override
Widget build(BuildContext context) {
final CounterViewModel viewModel = context.viewModel<CounterViewModel>();
return Column(
children: [
LiveDataBuilder<int>(
liveData: viewModel.counter,
builder: (context, count) => Text('Count: $count'),
),
FloatingActionButton(
onPressed: viewModel.increment,
child: const Icon(Icons.add),
),
],
);
}
}
class MultiScopeDemo extends StatelessWidget {
const MultiScopeDemo({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Multi Scope Demo'),
),
body: Center(
child: ViewModelScope(
builder: (_) => const Text('This is a new scope.'),
),
),
);
}
}
这段代码展示了一个包含两个计数器组件的应用程序,它们在同一作用域内共享同一个ViewModel
,因此会相互响应点击事件。此外,还有一个导航按钮可以跳转到新的作用域页面。
更多关于Flutter管理Flutter应用程序中的状态插件jetpack的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html