Flutter状态管理插件state_flow的使用
Flutter状态管理插件StateFlow的使用
StateFlow 提供了一种简单且高效的管理 Flutter 应用程序状态的方法。本指南将引导你了解 StateFlow 的核心概念及其状态管理功能的使用。
核心概念
StateFlow 的状态管理围绕以下几个关键概念:
- StateFlowController:用于创建控制器,这些控制器负责管理应用程序的逻辑和状态。
- take():一个响应式值持有者,当其值发生变化时会通知监听器。
- StateFlowApp:一个用于设置 StateFlow 环境和依赖注入的小部件。
- StateValueBuilder:一个小部件,当指定的状态发生变化时会重建自身。
设置
首先,在 pubspec.yaml
文件中添加 StateFlow 的依赖:
dependencies:
state_flow: ^0.0.8
然后,使用 StateFlowApp
包装你的应用以启用 StateFlow 功能。确保传递你想要使用的控制器到 controllers
参数中:
void main() {
runApp(StateFlowApp(
controllers: [
() => CounterController(),
],
child: MyApp(),
));
}
创建控制器
控制器是 StateFlow 状态管理的核心。它们管理应用程序的逻辑和状态,并可用于创建响应式的用户界面。
要创建一个控制器,扩展 StateFlowController
类:
声明变量
class CounterController extends StateFlowController {
final counter = take(0);
void increment() {
counter.value++;
}
}
使用 StateValueBuilder
你可以使用 StateValueBuilder
来根据状态的变化重新构建小部件:
final counterController = listen(CounterController);
StateValueBuilder(
value: counterController.counter,
builder: (value) => Text('Counter: $value'),
),
网络请求
StateFlow 提供了使用 StateFlowClient
进行网络请求的功能。
使用 StateFlowClient
首先,创建一个 StateFlowClient
实例。这将用于向服务器发送请求:
final client = StateFlowClient(baseUrl: 'https://jsonplaceholder.typicode.com');
final response = await client.sendRequest('/todos', HttpMethod.GET);
HttpMethod
你可以使用以下方法向服务器发送请求:
enum HttpMethod {
GET,
POST,
PUT,
DELETE,
}
创建网络控制器
为了创建一个网络控制器,扩展 StateFlowController
类:
class TodoController extends StateFlowController {
final todos = take<List<Todo>>([]);
final StateFlowClient _client = StateFlowClient(baseUrl: 'https://jsonplaceholder.typicode.com');
Future<List<Todo>> fetchTodos() async {
todos.setLoading();
try {
final response = await _client.sendRequest('/todos', HttpMethod.GET);
if (response.statusCode == 200) {
final List<dynamic> jsonData = jsonDecode(response.body);
final todoList = jsonData.map((json) => Todo.fromJson(json)).toList();
todos.setLoaded(todoList);
return todos.value;
} else {
todos.setError('Failed to load todos');
return [];
}
} catch (e) {
todos.setError(e);
todos.stopLoading();
rethrow;
}
}
}
构建响应式UI
你可以使用 listen
函数来监听控制器的状态。如果要构建一个在状态变化时重建的小部件,可以使用 WidgetStateValueBuilder
小部件:
final todoController = listen(TodoController);
WidgetStateValueBuilder<List<Todo>>(
state: todoController.todos,
dataBuilder: (data) {
if (data.isEmpty) {
return Text('No todos');
}
return Expanded(
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index].title),
);
},
),
);
},
errorBuilder: (error) => Text('Error: ${error.toString()}'),
loadingBuilder: () => CircularProgressIndicator(),
),
你也可以通过 StateValueBuilder
传递多个值:
StateValueBuilder(
values: [
counterController.count,
false,
'Hello',
false,
],
builder: (values) {
var [count, isActive, greeting, otherValue] = values;
return Column(
children: [
Text('Counter: $count'),
Icon(isActive ? Icons.check : Icons.close),
Text(greeting),
Text('Other value: $otherValue'),
],
);
},
),
使用动画控制器
如果你希望不使用 StatefulWidget
来使用动画控制器,可以使用 takeAnimationController
函数:
class TestApp extends StatelessWidget {
const TestApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
final animationController = takeAnimationController();
return const Placeholder();
}
}
使用 StateFlowWidget
如果你想在 initState
和 dispose
方法被调用时使用,可以使用 StateFlowWidget
:
class TestApp extends StateFlowWidget {
TestApp({super.key}) : super();
[@override](/user/override)
void onInit(context) {
print('TestApp onInit');
}
[@override](/user/override)
void onDispose() {
print('TestApp onDispose');
}
[@override](/user/override)
Widget build(BuildContext context) {
return const Placeholder();
}
}
StateFlow 导航
为了在应用中启用导航,可以在 MaterialApp
中使用 navigatorKey: StateFlow.navigatorKey
:
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'StateFlow Demo',
navigatorKey: StateFlow.navigatorKey,
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
使用 StateFlow.to
方法导航到新的屏幕:
StateFlow.to(DetailScreen());
对于返回上一个屏幕,可以使用 StateFlow.back
方法:
StateFlow.back();
还可以使用其他导航方法,如 StateFlow.off
、StateFlow.offAll
、StateFlow.maybePop
、StateFlow.currentRoute
和 StateFlow.getHistory
。
创建你的应用路由
你可以这样创建你的应用路由:
class AppRoutes {
static const String home = '/';
static const String second = '/second';
static final routes = {
home: (context) => HomeScreen(),
second: (context) => SecondScreen(),
};
}
修改 MaterialApp
如下:
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'StateFlow Demo',
navigatorKey: StateFlow.navigatorKey,
onGenerateRoute: (settings) => StateFlow.onGenerateRoute(settings),
theme: ThemeData(primarySwatch: Colors.blue),
routes: AppRoutes.routes,
);
}
}
现在你可以使用这些路由:
onPressed: () => StateFlow.to('/second')
onPressed: () => StateFlow.to(AppRoutes.second)
完整示例
下面是一个完整的示例代码:
import 'package:flutter/material.dart';
import 'package:state_flow/state_flow.dart';
import 'dart:convert';
void main() {
runApp(StateFlowApp(
controllers: [
() => TodoController(),
() => CounterController(),
],
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'StateFlow Demo',
navigatorKey: StateFlow.navigatorKey,
onGenerateRoute: (settings) => StateFlow.onGenerateRoute(settings),
theme: ThemeData(primarySwatch: Colors.blue),
routes: AppRoutes.routes,
);
}
}
class AppRoutes {
static const String home = '/';
static const String second = '/second';
static final routes = {
home: (context) => TestApp(),
second: (context) => SecondScreen(),
};
}
class HomeScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Go to Second Screen'),
onPressed: () => StateFlow.to('/second')),
ElevatedButton(
child: Text('Replace with Third Screen'),
onPressed: () => StateFlow.off('/third'),
),
ElevatedButton(
child: Text('Go to Fourth Screen and clear history'),
onPressed: () => StateFlow.offAll('/fourth'),
),
ElevatedButton(
child: Text('Go to Fifth Screen and clear history'),
onPressed: () => StateFlow.offAll('/fifth'),
),
ElevatedButton(
child: Text('Print Current Route'),
onPressed: () => print(StateFlow.currentRoute()),
),
ElevatedButton(
child: Text('Print Navigation History'),
onPressed: () => print(StateFlow.getHistory()),
),
],
),
),
);
}
}
class SecondScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Go Back'),
onPressed: () => StateFlow.back(),
),
ElevatedButton(
child: Text('Can Pop?'),
onPressed: () => print('Can pop: ${StateFlow.canPop()}'),
),
ElevatedButton(
child: Text('Maybe Pop'),
onPressed: () async {
bool didPop = await StateFlow.maybePop();
print('Did pop: $didPop');
},
),
],
),
),
);
}
}
class Hi extends StateFlowWidget {
const Hi({super.key}) : super();
[@override](/user/override)
Widget build(BuildContext context) {
return const Placeholder();
}
}
// THIRD, FOURTH, FIFTH SCREENS
class ThirdScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Third Screen')),
body: Center(child: Text('Third Screen')),
);
}
}
class FourthScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fourth Screen')),
body: Center(child: Text('Fourth Screen')),
);
}
}
class FifthScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fifth Screen')),
body: Center(child: Text('Fifth Screen')),
);
}
}
class CounterController extends StateFlowController {
late final StateValue<int> count;
late final StateValue<String> status;
int count23 = 2;
add() {
count23++;
}
[@override](/user/override)
void onInit() {
count = take(2);
print('CounterController initialized with count: ${count.value}');
status = take('Zero');
print('CounterController status initialized: ${status.value}');
}
void increment() {
count.value++;
updateStatus();
}
void decrement() {
count.value--;
updateStatus();
}
void updateStatus() {
status.value = count.value == 0 ? 'Zero' : 'Non-zero';
}
[@override](/user/override)
void onDispose() {
print('Disposing CounterController');
print('Final count: ${count.value}');
print('Final status: ${status.value}');
super.onDispose();
}
}
class TodoController extends StateFlowController {
[@override](/user/override)
void onInit() {}
final todos = take<List<Todo>>([]);
final StateFlowClient _client = StateFlowClient(baseUrl: 'https://jsonplaceholder.typicode.com');
Future<List<Todo>> fetchTodos() async {
todos.setLoading();
try {
final response = await _client.sendRequest('/todos', HttpMethod.GET);
if (response.statusCode == 200) {
final List<dynamic> jsonData = jsonDecode(response.body);
final todoList = jsonData.map((json) => Todo.fromJson(json)).toList();
todos.setLoaded(todoList);
return todos.value;
} else {
todos.setError('Failed to load todos');
return [];
}
} catch (e) {
todos.setError(e);
todos.stopLoading();
rethrow;
}
}
}
class Todo {
final int id;
final String title;
final bool completed;
Todo.fromJson(Map<String, dynamic> json)
: id = json['id'],
title = json['title'],
completed = json['completed'];
}
class TestApp extends StateFlowWidget {
const TestApp({super.key}) : super();
[@override](/user/override)
void onInit(context) {
print('TestApp onInit');
}
[@override](/user/override)
void onDispose() {
print('TestApp onDispose');
}
[@override](/user/override)
Widget build(BuildContext context) {
final todoController = listen(TodoController);
final CounterController counterController = listen(CounterController);
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
counterController.add();
print('Pressed');
},
child: Icon(Icons.refresh),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
WidgetStateValueBuilder<List<Todo>>(
state: todoController.todos,
dataBuilder: (data) {
if (data.isEmpty) {
return Text('No todos');
}
return Expanded(
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index].title),
);
},
),
);
},
errorBuilder: (error) => Text('Error: ${error.toString()}'),
loadingBuilder: () => CircularProgressIndicator(),
),
SizedBox(height: 20),
StateValueBuilder(
value: counterController.count,
builder: (count) {
return Text(count.toString());
},
),
SizedBox(height: 20),
],
),
),
);
}
}
class TestAppNew extends StatelessWidget {
const TestAppNew({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
final animationController = takeAnimationController();
return const Placeholder();
}
}
更多关于Flutter状态管理插件state_flow的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter状态管理插件state_flow的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
state_flow
是 Flutter 中一个轻量级的状态管理插件,它基于 Stream
和 StreamController
来实现状态管理。state_flow
的核心思想是通过流(Stream)来管理应用的状态,并在状态变化时通知监听者进行更新。
安装 state_flow
首先,你需要在 pubspec.yaml
中添加 state_flow
依赖:
dependencies:
flutter:
sdk: flutter
state_flow: ^0.1.0
然后运行 flutter pub get
来安装依赖。
使用 state_flow
1. 创建状态管理类
首先,你需要创建一个状态管理类,并继承自 StateFlow
。在这个类中,你可以定义需要管理的状态以及相关的业务逻辑。
import 'package:state_flow/state_flow.dart';
class CounterState extends StateFlow<int> {
CounterState() : super(0); // 初始化状态为 0
void increment() {
state = state + 1; // 更新状态
}
void decrement() {
state = state - 1; // 更新状态
}
}
2. 在 UI 中使用状态管理类
在 UI 中,你可以通过 StateFlowBuilder
来监听状态的变化,并在状态变化时更新 UI。
import 'package:flutter/material.dart';
import 'counter_state.dart'; // 导入刚才创建的状态管理类
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
);
}
}
class CounterScreen extends StatelessWidget {
final CounterState counterState = CounterState(); // 创建状态管理实例
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
),
body: Center(
child: StateFlowBuilder<int>(
flow: counterState,
builder: (context, state) {
return Text(
'Count: $state',
style: TextStyle(fontSize: 24),
);
},
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: counterState.increment,
child: Icon(Icons.add),
),
SizedBox(height: 10),
FloatingActionButton(
onPressed: counterState.decrement,
child: Icon(Icons.remove),
),
],
),
);
}
}