Flutter应用状态管理插件app_state的使用
Flutter应用状态管理插件app_state的使用
简介
app_state
是一个基于路由API的状态管理解决方案,适用于较大的应用程序。它提供了许多独特的功能,如以状态为中心的设计、编译时类型安全的push-pop操作、从URL恢复堆栈以及在应用程序重启后对话框结果仍然存在等。此外,还提供了一些架构上的优势和灵活性。
核心概念
状态优先 (States First)
与传统的路由或小部件不同,app_state
的核心是状态对象的堆栈(可以是Blocs、ChangeNotifiers或其他任何东西),这些被称为页面状态。导航意图被翻译成对这些状态的操作,然后将这些页面状态包装成小部件以形成导航器的堆栈。这简化了状态管理:无需提供商、有状态的小部件、手动Bloc创建和销毁或 BuildContext
。
编译时类型安全的Push-Pop操作 (Push-pop Type Safety at Compile Time)
所有状态对象都是类型的,并且只能弹出允许的内容。你推送的页面条目也是类型的,因此你的等待类型也得到了保证。
从URL恢复堆栈 (Stack Recovery from URL)
当输入一个URL时,可以根据定义的规则恢复页面堆栈。例如,如果你导航到 /books/123/rate
,你可能会得到一个包含三个页面的堆栈:底部是书籍列表,上面是书籍详情页,顶部是评分对话框,所有这些都可以通过返回按钮弹出。
对话框结果在应用程序重启后仍然存在 (Dialog Awaiting Survives the App Restart)
页面状态监听来自其他页面状态的事件。当最顶部的一个弹出时,其结果会传递给下面的页面状态的 didPopNext
方法。因此,你可以在不等待未来的情况下获得结果。
架构
主要的状态单元是 PageStack
,通常作为全局对象创建,或者你可以使用 get_it
提供它。
对于每个页面,有三个主要部分:
- 页面状态 (Page State): 页面生命周期的核心。
- 屏幕 (Screen): 大多数情况下是一个只接受页面状态作为单个参数的状态化小部件。
- 页面 (Page): 这是页面状态和屏幕之间的适配器。
示例代码
最小的应用程序 (The Bare Minimal App)
这是一个只有一个屏幕且没有导航的应用程序,甚至没有页面状态,因为它没有什么需要保存的。
import 'package:app_state/app_state.dart';
import 'package:flutter/material.dart';
final pageStack = PageStack(bottomPage: HomePage());
final _routerDelegate = PageStackRouterDelegate(pageStack);
void main() => runApp(MyApp());
class HomePage extends StatelessMaterialPage {
HomePage() : super(key: const ValueKey('Home'), child: HomeScreen());
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Hello World with app_state!')),
);
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerDelegate: _routerDelegate,
routeInformationParser: const PageStackRouteInformationParser(),
);
}
}
推送和弹出页面 (Pushing and Popping Pages)
推送页面 (Pushing)
要推送一个屏幕,你需要创建它的 Page
对象并将其推送到 PageStack
中:
final result = await pageStack.push(
BookDetailsPage(bookId: id),
);
弹出页面 (Programmatic Popping)
要弹出一个页面,调用其状态上的 pop()
方法,可选地带有返回值:
onPressed: () => state.pop(result);
覆盖返回按钮 (Overriding the Back Button)
在你的状态中覆盖 onBackPressed()
方法:
@override
Future<BackPressedResult> onBackPressed() {
if (!_saved) {
return Future.value(BackPressedResult.keep);
}
return Future.value(BackPressedResult.close);
}
Web架构 (Web Architecture)
为了支持URL,需要解析URL并创建相应的页面路径。
解析URL (Parsing URLs)
创建一个URL解析器,该解析器在启动时由Flutter调用,并在浏览器的回退和前进导航时调用:
class MyRouteInformationParser extends PageStackRouteInformationParser {
@override
Future<PagePath> parsePagePath(RouteInformation ri) async {
return
BookDetailsPath.tryParse(ri) ??
const BookListPath(); // 默认页面如果什么都匹配不上。
}
}
创建页面对象 (Creating the Page Objects)
创建一个全局方法来创建页面对象:
AbstractPage? createPage(
String factoryKey,
Map<String, dynamic> state,
) {
switch (factoryKey) {
case BookDetailsPage.classFactoryKey: return BookDetailsPage(bookId: state['bookId']);
case BookListPage.classFactoryKey: return BookListPage();
}
return null;
}
更新地址栏 (Updating the Address Bar)
更新地址栏内容的方式取决于页面是否有状态。
没有状态的页面 (Page Without State)
对于没有状态的页面,你可以在构造函数中硬编码一个 PagePath
:
super(
// ...
path: BookDetailsPath(bookId: bookId),
);
有状态的页面 (Page With State)
对于有状态的页面,委托给状态进行更高灵活性的更新:
@override
BookListPath get path => const BookListPath();
不影响URL的页面 (Page Without URL)
对于不影响地址栏和浏览器历史记录的次要对话框,只需不在页面路径类中引入任何路径类。
程序化更新URL (Updating the URL Programmatically)
想象一个树形浏览器,动态更新地址栏中的内容:
state.emitPathChanged();
重定向URL (Redirecting a URL)
可以通过以下两种方式之一实现:
单个类 (Option 1. Single Class)
在 BookListPath.tryParse()
中允许 /
和 /books
,并在 location
getter 中返回 /books
。
多个类 (Option 2. Multiple Classes)
可以有许多 PagePath
类,它们都返回 BookListPath
,并且它在 tryParse
方法中返回 /books
。
浏览器回退和前进按钮 (Browser Back and Forward Buttons)
回退和前进按钮在浏览器中自动工作。它们遍历浏览器的历史记录。
恢复未保存的输入 (Recovering Unsaved Input on Page Refresh and Navigation)
此包允许你在许多情况下恢复状态,而不会丢失状态。
多个选项卡具有独立的堆栈 (Multiple Tabs with Independent Stacks)
每个选项卡必须有自己的导航堆栈。Android返回按钮和Scaffold的返回按钮应该只弹出当前堆栈上的页面。选项卡切换不应影响返回按钮历史记录。
高级返回结果的方法 (Advanced Ways to Return Result)
包括编译时类型安全的Push-Pop操作,以及接收对话框结果后应用程序重启。
技术支持聊天 (Tech Support Chat)
如果你有任何问题,欢迎加入 Telegram Support Chat。
需要帮助 (Help is Wanted)
如果你喜欢这个包,请帮忙传播、报告问题或参与开发。
更多示例项目请参阅 这里。
希望这篇文档对你有所帮助!如果你有任何问题或需要进一步的帮助,请随时提问。
更多关于Flutter应用状态管理插件app_state的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter应用状态管理插件app_state的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,app_state
并不是一个广为人知的 Flutter 状态管理插件,但我可以基于 Flutter 中常见的状态管理插件(如 provider
或 riverpod
)给出一个类似的示例代码,因为这些插件在 Flutter 社区中非常流行且功能强大。为了贴近你的要求,我会假设 app_state
提供了类似的功能,并展示如何使用一个类似的状态管理插件来实现应用状态管理。
以下是一个使用 provider
插件管理应用状态的示例代码:
1. 添加依赖
首先,在你的 pubspec.yaml
文件中添加 provider
依赖:
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0 # 请确保使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 创建一个状态管理类
接下来,创建一个管理应用状态的类。例如,我们可以创建一个简单的计数器状态:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
3. 包装应用
在 main.dart
中,使用 MultiProvider
包装你的应用,并提供 Counter
实例:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart'; // 假设你的 Counter 类在这个文件中
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
4. 在UI中使用状态
现在,在你的 MyHomePage
中,你可以使用 Consumer
或 Selector
来访问和监听 Counter
状态的变化:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${Provider.of<Counter>(context).count}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<Counter>(context, listen: false).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
总结
虽然这个示例使用的是 provider
插件而不是假设的 app_state
插件,但它展示了如何在 Flutter 应用中实现状态管理。你可以根据 app_state
的具体 API 文档调整上述代码以适应你的需求。如果 app_state
插件有类似的 ChangeNotifier
和 Provider
机制,代码结构应该是非常相似的。