Flutter状态扩展插件state_extended的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter状态扩展插件state_extended的使用

简介

StateX 是一个扩展了 Flutter 的 State 类功能的插件。Flutter 的 State 类是状态管理的核心组件,但 StateX 进一步增强了它的能力,引入了一个 StateXController 类来分离业务逻辑和界面逻辑,并提供了 22 个生命周期事件的处理。

StateX 不要与 GetX 混淆。虽然两者都涉及控制器,但它们的设计理念和实现方式有所不同。GetX 有 GetxController 类,而 StateX 有 StateXController 类。

StateX

功能

  • StateXController: 一个独立的控制器类,用于处理业务逻辑。
  • 22 个生命周期事件: 提供了更丰富的生命周期管理。
  • 分离业务逻辑和界面逻辑: 通过 StateXController 将业务逻辑从 State 类中分离出来。
  • 外部状态管理: 控制器可以调用 State 对象的 setState 方法,并访问其属性(如 widgetmountedcontext 等)。

文档

示例代码

以下是一个简单的计数器应用示例,展示了如何使用 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

1 回复

更多关于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,我们需要使用StateProviderStateConsumer

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'),
            ),
          ],
        ),
      ),
    );
  }
}

解释

  1. CounterModel: 这是一个简单的状态模型,包含一个count变量和两个方法incrementdecrement,用于修改count的值并通知视图更新。

  2. MyApp: 这是应用的入口,它使用StateProvider来提供CounterModel实例,并将其传递给CounterScreen

  3. CounterScreen: 这是显示计数器值的屏幕。它使用StateConsumer来监听CounterModel的变化,并在UI中显示当前的count值。按钮的点击事件会调用incrementdecrement方法,从而更新状态并刷新UI。

通过这种方式,你可以轻松地在Flutter应用中使用state_extended插件进行状态管理。希望这个示例对你有所帮助!

回到顶部