Flutter状态扩展插件state_extended的使用
Flutter状态扩展插件state_extended的使用
简介
StateX 是一个扩展了 Flutter 的 State
类功能的插件。Flutter 的 State
类是状态管理的核心组件,但 StateX 进一步增强了它的能力,引入了一个 StateXController
类来分离业务逻辑和界面逻辑,并提供了 22 个生命周期事件的处理。
StateX 不要与 GetX 混淆。虽然两者都涉及控制器,但它们的设计理念和实现方式有所不同。GetX 有 GetxController
类,而 StateX 有 StateXController
类。
功能
- StateXController: 一个独立的控制器类,用于处理业务逻辑。
- 22 个生命周期事件: 提供了更丰富的生命周期管理。
- 分离业务逻辑和界面逻辑: 通过
StateXController
将业务逻辑从State
类中分离出来。 - 外部状态管理: 控制器可以调用
State
对象的setState
方法,并访问其属性(如widget
、mounted
、context
等)。
文档
- Get started
- StateX class
- State Object Controller
- Event handling
- AppStateX class
- Error Handling
- Testing
示例代码
以下是一个简单的计数器应用示例,展示了如何使用 StateX 插件。
主应用
import 'package:flutter/material.dart';
import 'package:state_extended/state_extended.dart';
void main() => runApp(const MyApp(key: Key('MyApp')));
/// README.md example app
class MyApp extends StatefulWidget {
const MyApp({super.key, this.title = 'StateX Demo App'});
final String title;
@override
State createState() => _MyAppState();
}
class _MyAppState extends AppStateX<MyApp> {
factory _MyAppState() => _this ??= _MyAppState._();
_MyAppState._() : super(controller: AppController()) {
con = controller as AppController;
}
static _MyAppState? _this;
late AppController con;
@override
Widget build(BuildContext context) => super.build(context);
@override
Widget builder(BuildContext context) => MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(title: widget.title),
);
}
主页面
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, this.title});
final String? title;
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends StateX<MyHomePage> {
_MyHomePageState() : super(controller: HomeController(), useInherited: true) {
con = controller as HomeController;
}
late HomeController con;
@override
void initState() {
super.initState();
appState = rootState!;
final con = appState.controller;
appState = con?.state!.firstState as AppStateX;
appState = stateByType<AppStateX>()!;
}
late AppStateX appState;
@override
Widget build(BuildContext context) => super.build(context);
@override
Widget buildF(BuildContext context) => super.buildF(context);
@override
Widget builder(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(widget.title ?? ''),
actions: [con.popupMenuButton],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
style: Theme.of(context).textTheme.bodyMedium,
),
const CounterWidget(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('+'),
onPressed: () => con.onPressed(),
child: const Icon(Icons.add),
),
);
}
计数器小部件
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<StatefulWidget> createState() => _CounterState();
}
class _CounterState extends State<CounterWidget> {
@override
Widget build(BuildContext context) {
final con = HomeController();
con.dependOnInheritedWidget(context);
return Text(
con.data,
style: Theme.of(context).textTheme.headlineMedium,
);
}
}
控制器
class HomeController extends StateXController {
factory HomeController() => _this ??= HomeController._();
HomeController._()
: _model = Model(),
_letters = AlphabetLetters(),
_primes = PrimeNumbers();
static HomeController? _this;
final Model _model;
final AlphabetLetters _letters;
final PrimeNumbers _primes;
String get data {
String data;
switch (_countType) {
case CountType.prime:
data = _primes.primeNumber.toString();
break;
case CountType.alphabet:
data = _letters.current;
break;
default:
data = _model.integer.toString();
}
return data;
}
CountType _countType = CountType.integer;
void onPressed() {
switch (_countType) {
case CountType.prime:
_primes.next();
break;
case CountType.alphabet:
_letters.read();
break;
default:
_model.incrementCounter();
}
notifyClients();
}
PopupMenuButton<CountType> get popupMenuButton => PopupMenuButton<CountType>(
itemBuilder: (context) => [
PopupMenuItem(
value: CountType.integer,
child: Row(
children: [
if (_countType == CountType.integer)
const Icon(Icons.star_rounded, color: Colors.black),
const Text('Integers')
],
),
),
PopupMenuItem(
value: CountType.alphabet,
child: Row(
children: [
if (_countType == CountType.alphabet)
const Icon(Icons.star_rounded, color: Colors.black),
const Text('Alphabet')
],
),
),
PopupMenuItem(
value: CountType.prime,
child: Row(
children: [
if (_countType == CountType.prime)
const Icon(Icons.star_rounded, color: Colors.black),
const Text('Prime Numbers')
],
),
),
],
onSelected: (value) {
switch (value) {
case CountType.prime:
_countType = value;
break;
case CountType.alphabet:
_countType = value;
break;
default:
_countType = CountType.integer;
}
setState(() {});
},
offset: const Offset(0, 40),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
elevation: 14,
);
@override
void initState() {
super.initState();
var thisController = state?.controller;
thisController = state?.controllerById(thisController?.identifier);
assert(thisController == this, 'Just demonstrating the means to retrieve a Controller.');
var stateObj = stateOf<MyHomePage>();
final appState = rootState;
assert(appState is _MyAppState, "Every Controller has access to the 'first' State object.");
stateObj = appState?.stateByType<_MyHomePageState>();
final appController = appState?.controller;
final con = appState?.controllerById(appController?.identifier);
assert(appController == con, 'They should be the same object.');
}
@override
void deactivate() {
super.deactivate();
if (inDebugMode) {
print('############ Event: deactivate in HomeController');
}
}
@override
void activate() {
super.activate();
if (inDebugMode) {
print('############ Event: activate in HomeController');
}
}
@override
void dispose() {
if (inDebugMode) {
print('############ Event: dispose in HomeController');
}
super.dispose();
}
@override
void didUpdateWidget(StatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (inDebugMode) {
print('############ Event: didUpdateWidget in HomeController');
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (inDebugMode) {
print('############ Event: didChangeDependencies in HomeController');
}
}
@override
void reassemble() {
super.reassemble();
if (inDebugMode) {
print('############ Event: reassemble in HomeController');
}
}
@override
Future<bool> didPopRoute() async {
if (inDebugMode) {
print('############ Event: didPopRoute in HomeController');
}
return super.didPopRoute();
}
@override
void didChangeMetrics() {
super.didChangeMetrics();
if (inDebugMode) {
print('############ Event: didChangeMetrics in HomeController');
}
}
@override
void didChangeTextScaleFactor() {
super.didChangeTextScaleFactor();
if (inDebugMode) {
print('############ Event: didChangeTextScaleFactor in HomeController');
}
}
@override
void didChangePlatformBrightness() {
super.didChangePlatformBrightness();
if (inDebugMode) {
print('############ Event: didChangePlatformBrightness in HomeController');
}
}
@override
void didChangeLocales(List<Locale>? locales) {
super.didChangeLocales(locales);
if (inDebugMode) {
print('############ Event: didChangeLocales in HomeController');
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (inDebugMode) {
print('############ Event: didChangeAppLifecycleState in HomeController');
}
}
@override
void inactiveAppLifecycleState() {
super.inactiveAppLifecycleState();
if (inDebugMode) {
print('############ Event: inactiveLifecycleState in HomeController');
}
}
@override
void pausedAppLifecycleState() {
super.pausedAppLifecycleState();
if (inDebugMode) {
print('############ Event: pausedLifecycleState in HomeController');
}
}
@override
void detachedAppLifecycleState() {
super.detachedAppLifecycleState();
if (inDebugMode) {
print('############ Event: detachedLifecycleState in HomeController');
}
}
@override
void resumedAppLifecycleState() {
super.resumedAppLifecycleState();
if (inDebugMode) {
print('############ Event: resumedLifecycleState in HomeController');
}
}
@override
void didHaveMemoryPressure() {
super.didHaveMemoryPressure();
if (inDebugMode) {
print('############ Event: didHaveMemoryPressure in HomeController');
}
}
@override
void didChangeAccessibilityFeatures() {
super.didChangeAccessibilityFeatures();
if (inDebugMode) {
print('############ Event: didChangeAccessibilityFeatures in HomeController');
}
}
}
数据模型
enum CountType {
integer,
prime,
alphabet,
}
class Model {
int _integer = 0;
int get integer => _integer;
void incrementCounter() => ++_integer;
}
class AlphabetLetters {
int start = 'a'.codeUnitAt(0);
int end = 'z'.codeUnitAt(0);
late int letter = start;
String get current => String.fromCharCode(letter);
void read() {
letter++;
if (letter > end) {
letter = start;
}
}
}
class PrimeNumbers {
PrimeNumbers({int? start, int? end}) {
start = start ?? 1;
end = end ?? 1000;
if (start < 0) {
start = 1;
}
if (end <= start) {
end = 1000;
}
initPrimeNumbers(start, end);
}
final List<int> _numbers = [];
int _cnt = 0;
int get primeNumber => _numbers[_cnt];
void next() {
_cnt++;
if (_cnt > _numbers.length) {
_cnt = 0;
}
}
void initPrimeNumbers(int M, int N) {
a:
for (var k = M; k <= N; ++k) {
for (var i = 2; i <= k / i; ++i) {
if (k % i == 0) {
continue a;
}
}
_numbers.add(k);
}
}
}
应用控制器
class AppController extends StateXController {
factory AppController() => _this ??= AppController._();
AppController._();
static AppController? _this;
@override
Future<bool> initAsync() async {
return Future.delayed(const Duration(seconds: 10), () {
return true;
});
}
}
以上代码展示了如何使用 StateX 插件来构建一个简单的计数器应用。通过将业务逻辑分离到控制器中,可以使代码更加清晰和易于维护。希望这个示例对你有所帮助!
更多关于Flutter状态扩展插件state_extended的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter状态扩展插件state_extended的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,我可以为你提供一个关于如何在Flutter项目中使用state_extended
插件的代码示例。state_extended
是一个用于Flutter的状态管理库,它允许开发者更方便地管理应用中的状态。
首先,确保你已经在pubspec.yaml
文件中添加了state_extended
依赖:
dependencies:
flutter:
sdk: flutter
state_extended: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,我将展示一个简单的示例,演示如何使用state_extended
来管理应用的状态。
1. 创建一个StateModel
首先,创建一个继承自BaseStateModel
的StateModel。例如,我们创建一个简单的计数器模型:
import 'package:state_extended/state_extended.dart';
class CounterModel extends BaseStateModel {
int count = 0;
void increment() {
count++;
update(); // 通知视图更新
}
void decrement() {
count--;
update(); // 通知视图更新
}
}
2. 在Widget中使用StateModel
接下来,我们创建一个使用CounterModel
的Widget。为了使用state_extended
,我们需要使用StateProvider
和StateConsumer
。
import 'package:flutter/material.dart';
import 'package:state_extended/state_extended.dart';
import 'counter_model.dart'; // 导入CounterModel
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StateProvider<CounterModel>(
initialState: CounterModel(), // 初始化状态模型
child: CounterScreen(),
),
);
}
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StateConsumer<CounterModel>(
builder: (context, model, child) {
return Text(
'You have pushed the button this many times:',
style: Theme.of(context).textTheme.headline4,
);
},
child: Text(
'${model.count}',
style: Theme.of(context).textTheme.headline4,
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
StateProvider.of<CounterModel>(context).increment();
},
child: Text('Increment'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
StateProvider.of<CounterModel>(context).decrement();
},
child: Text('Decrement'),
),
],
),
),
);
}
}
解释
-
CounterModel: 这是一个简单的状态模型,包含一个
count
变量和两个方法increment
和decrement
,用于修改count
的值并通知视图更新。 -
MyApp: 这是应用的入口,它使用
StateProvider
来提供CounterModel
实例,并将其传递给CounterScreen
。 -
CounterScreen: 这是显示计数器值的屏幕。它使用
StateConsumer
来监听CounterModel
的变化,并在UI中显示当前的count
值。按钮的点击事件会调用increment
和decrement
方法,从而更新状态并刷新UI。
通过这种方式,你可以轻松地在Flutter应用中使用state_extended
插件进行状态管理。希望这个示例对你有所帮助!