关于flutter:盘点主流-Flutter-状态管理库2024

6次阅读

共计 10012 个字符,预计需要花费 26 分钟才能阅读完成。

盘点支流 Flutter 状态治理库 2024

视频

https://youtu.be/nLQ2nmW8FKk

https://www.bilibili.com/video/BV1U6421M7dw/

前言

原文 https://ducafecat.com/blog/flutter-state-management-libraries…

状态治理是每个利用不可短少的,本文将会盘点下支流的状态治理包。

对了 我还对插件包做了整顿,欢送移步查看 https://flutter.ducafecat.com .

状态治理作用

  1. 数据共享和同步 :在应用程序中,不同局部可能须要共享和同步数据。通过状态治理,能够轻松地在应用程序的各个局部之间共享数据,并确保数据的一致性。
  2. UI 更新 :Flutter 状态治理能够帮忙开发者管理应用程序中的 UI 状态,以便在数据变动时更新用户界面。这样能够确保应用程序的 UI 与数据的状态放弃同步。
  3. 简单状态治理 :随着应用程序变得越来越简单,管理应用程序的状态变得更加艰难。Flutter 状态管理工具能够帮忙开发者更无效地管理应用程序的状态,使代码更具可维护性和可扩展性。
  4. 性能优化 :无效的状态治理能够帮忙应用程序防止不必要的重绘和从新构建,从而进步应用程序的性能和响应速度。
  5. 代码构造 :通过良好的状态治理,开发者能够更好地组织应用程序的代码构造,使其更易于了解和保护。

考量纬度

  1. 编写代码的复杂度,是否简洁
  2. 实用规模(小型、中大型、参加人数、迭代频率)
  3. 开发团队的前端组件化教训
  4. 我的项目是否疾速开发,MVP

Provider

轻量级、易学习、内置于 Flutter 中,实用于根本状态治理。

https://pub.dev/packages/provider

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

/// This is a reimplementation of the default Flutter application using provider + [ChangeNotifier].

void main() {
  runApp(/// Providers are above [MyApp] instead of inside it, so that tests
    /// can use [MyApp] while mocking the providers
    MultiProvider(
      providers: [ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: const MyApp(),),
  );
}

/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
// ignore: prefer_mixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();}

  /// Makes `Counter` readable inside the devtools by listing all of its properties
  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);
    properties.add(IntProperty('count', count));
  }
}

class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Example'),
      ),
      body: const Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[Text('You have pushed the button this many times:'),

            /// Extracted as a separate widget for performance optimization.
            /// As a separate widget, it will rebuild independently from [MyHomePage].
            ///
            /// This is totally optional (and rarely needed).
            /// Similarly, we could also use [Consumer] or [Selector].
            Count(),],
        ),
      ),
      floatingActionButton: FloatingActionButton(key: const Key('increment_floatingActionButton'),

        /// Calls `context.read` instead of `context.watch` so that it does not rebuild
        /// when [Counter] changes.
        onPressed: () => context.read<Counter>().increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class Count extends StatelessWidget {const Count({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(/// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
      '${context.watch<Counter>().count}',
      key: const Key('counterState'),
      style: Theme.of(context).textTheme.headlineMedium,
    );
  }
}

BLoC/Cubit:

基于块模式构建,将业务逻辑与用户界面解耦,实用于简单的状态治理。

https://pub.dev/packages/flutter_bloc

  • Bloc 架构

https://bloclibrary.dev/#/zh-cn/coreconcepts?id=bloc

  • 快手上手文档

https://bloclibrary.dev/#/zh-cn/gettingstarted

  • 计数器 我的项目构造
├── lib
│   ├── app.dart
│   ├── counter
│   │   ├── counter.dart
│   │   ├── cubit
│   │   │   └── counter_cubit.dart
│   │   └── view
│   │       ├── counter_page.dart
│   │       └── counter_view.dart
│   ├── counter_observer.dart
│   └── main.dart
├── pubspec.lock
├── pubspec.yaml

这个利用中咱们应用的是性能驱动(feature-driven)的我的项目构造。

这种我的项目构造能够让咱们通过一个个独立的性能来扩大我的项目。

  • login 登录 我的项目构造

Riverpod

实用于中大型应用程序的具备类型、作用域和关注点拆散的改进版提供程序。

https://pub.dev/packages/riverpod

https://riverpod.dev/zh-hans/docs/introduction/getting_started

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'main.g.dart';

// 咱们创立了一个 "provider",它能够存储一个值(这里是 "Hello world")。// 通过应用提供者程序,这能够容许咱们模仿或者笼罩一个裸露的值。@riverpod
String helloWorld(HelloWorldRef ref) {return 'Hello world';}

void main() {
  runApp(
    // 为了使小组件能够读取提供者程序,// 咱们须要将整个应用程序包装在“ProviderScope”小部件中。// 这是咱们的提供者程序的状态将被存储的中央。ProviderScope(child: MyApp(),
    ),
  );
}

// 继承父类应用 ConsumerWidget 代替 StatelessWidget,这样能够获取到提供者程序的援用
class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {final String value = ref.watch(helloWorldProvider);

    return MaterialApp(
      home: Scaffold(appBar: AppBar(title: const Text('Example')),
        body: Center(child: Text(value),
        ),
      ),
    );
  }
}

GetX

全能框架,用于状态治理、路由和依赖注入,十分实用于低到中等复杂度的应用程序。

https://pub.dev/packages/get

长处

  1. 简略易用

    • GetX 提供了简洁而直观的 API,使得状态治理和导航等性能变得非常容易实现。
    • 开发者能够通过大量的代码实现简单的性能,进步开发效率。
  2. 性能优良

    • GetX 被设计为高性能的状态治理库,具备杰出的性能体现。
    • GetX 应用响应式编程和观察者模式,能够确保只有在数据变动时才会触发 UI 更新,从而进步应用程序的性能。
  3. 依赖注入

    • GetX 内置了依赖注入性能,能够不便地管理应用程序中的依赖关系。
    • 通过 GetX 的依赖注入性能,开发者能够更好地组织和管理应用程序的代码。
  4. 路由治理

    • GetX 提供了弱小的路由治理性能,反对命名路由、动画过渡等。
    • 开发者能够轻松地管理应用程序的导航逻辑,实现页面之间的切换和传递参数。
  5. 轻量级

    • GetX 是一个轻量级的库,不会给应用程序减少过多的累赘。
    • 应用 GetX 能够防止引入过多的依赖,使应用程序放弃简洁和高效。

示例

订阅更新

void main() => runApp(MaterialApp(home: Home()));

class Home extends StatelessWidget {
  var count = 0.obs;
  @override
  Widget build(context) => Scaffold(appBar: AppBar(title: Text("counter")),
      body: Center(child: Obx(() => Text("$count")),
      ),
      floatingActionButton: FloatingActionButton(child: Icon(Icons.add),
        onPressed: () => count ++,));
}

手动更新

  @override
  Widget build(BuildContext context) {
    return GetBuilder<MatchIndexController>(init: MatchIndexController(fixtureId),
      id: "match_index",
      tag: tag, // 辨别不同控制器
      builder: (_) {
        return controller.fixturesInfo == null
            ? const ProgressIndicatorWidget().paddingTop(100)
            : _buildView();},
    );
  }
  _initData() async {await _loadData();
    update(["match_index"]);
  }

官网脚手架

https://github.com/jonataslaw/get_cli/blob/master/README-zh_CN.md

猫哥 vsc 插件

https://marketplace.visualstudio.com/items?itemName=ducafecat…

getx 实战视频 + 代码模版

https://ducafecat.com/course/flutter-woo

MobX

MobX 是一种状态治理库,它让应用程序的响应式数据与 UI 关联起来变得很简略。

https://pub.dev/packages/flutter_mobx

https://github.com/mobxjs/mobx.dart/tree/master/mobx_examples/lib/counter

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';

part 'counter.g.dart';

class Counter = CounterBase with _$Counter;

abstract class CounterBase with Store {
  @observable
  int value = 0;

  @action
  void increment() {value++;}
}

class CounterExample extends StatefulWidget {const CounterExample({Key key}) : super(key: key);

  @override
  _CounterExampleState createState() => _CounterExampleState();
}

class _CounterExampleState extends State<CounterExample> {final _counter = Counter();

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text('Counter'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text('You have pushed the button this many times:',),
              Observer(builder: (_) => Text('${_counter.value}',
                        style: const TextStyle(fontSize: 20),
                      )),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _counter.increment,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      );
}

监督变动

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = reaction((_) => greeting.value, (msg) => print(msg));

greeting.value = 'Hello MobX'; // Cause a change

// Done with the reaction()
dispose();


// Prints:
// Hello MobX

Redux

具备集中式存储、操作和减速器的可预测状态治理,实用于须要弱小可预测性和工具的应用程序。

https://pub.dev/packages/flutter_redux

import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';

// One simple action: Increment
enum Actions {Increment}

// The reducer, which takes the previous count and increments it in response
// to an Increment action.
int counterReducer(int state, dynamic action) {return action == Actions.Increment ? state + 1 : state;}

void main() {
  // Create your store as a final variable in the main function or inside a
  // State object. This works better with Hot Reload than creating it directly
  // in the `build` function.
  final store = Store<int>(counterReducer, initialState: 0);

  runApp(FlutterReduxApp(
    title: 'Flutter Redux Demo',
    store: store,
  ));
}

class FlutterReduxApp extends StatelessWidget {
  final Store<int> store;
  final String title;

  FlutterReduxApp({Key key, this.store, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // The StoreProvider should wrap your MaterialApp or WidgetsApp. This will
    // ensure all routes have access to the store.
    return StoreProvider<int>(
      // Pass the store to the StoreProvider. Any ancestor `StoreConnector`
      // Widgets will find and use this value as the `Store`.
      store: store,
      child: MaterialApp(theme: ThemeData.dark(),
        title: title,
        home: Scaffold(appBar: AppBar(title: Text(title)),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // Connect the Store to a Text Widget that renders the current
                // count.
                //
                // We'll wrap the Text Widget in a `StoreConnector` Widget. The
                // `StoreConnector` will find the `Store` from the nearest
                // `StoreProvider` ancestor, convert it into a String of the
                // latest count, and pass that String  to the `builder` function
                // as the `count`.
                //
                // Every time the button is tapped, an action is dispatched and
                // run through the reducer. After the reducer updates the state,
                // the Widget will be automatically rebuilt with the latest
                // count. No need to manually manage subscriptions or Streams!
                StoreConnector<int, String>(converter: (store) => store.state.toString(),
                  builder: (context, count) {
                    return Text(
                      'The button has been pushed this many times: $count',
                      style: Theme.of(context).textTheme.display1,
                    );
                  },
                )
              ],
            ),
          ),
          // Connect the Store to a FloatingActionButton. In this case, we'll
          // use the Store to build a callback that will dispatch an Increment
          // Action.
          //
          // Then, we'll pass this callback to the button's `onPressed` handler.
          floatingActionButton: StoreConnector<int, VoidCallback>(converter: (store) {
              // Return a `VoidCallback`, which is a fancy name for a function
              // with no parameters and no return value. 
              // It only dispatches an Increment action.
              return () => store.dispatch(Actions.Increment);
            },
            builder: (context, callback) {
              return FloatingActionButton(
                // Attach the `callback` to the `onPressed` attribute
                onPressed: callback,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              );
            },
          ),
        ),
      ),
    );
  }
}

Fish-Redux

一个针对 Flutter 优化的 Redux 版本,缩小样板代码,同时放弃架构概念。

https://pub.dev/packages/fish_redux

GetIt

这是一个依赖管理工具,当然也能用来作为简略的状态治理,比方全局、部分的数据保护。

https://pub.dev/packages/get_it

我专门做一个视频能够移步看下。

视频 https://www.bilibili.com/video/BV1EG411X7zz

文档 https://ducafecat.com/blog/use-get_it-in-getx

小结

每个库都有其长处和毛病,实用于不同的应用程序大小和复杂性。对于根本应用程序,举荐应用 Provider 和 GetX,而对于较大的应用程序,能够从 BLoC/Cubit、Riverpod 或 MobX 中受害。在抉择库时,请思考您的应用程序需要、团队偏好和架构格调。

总之,弱小的状态治理对于利用的稳定性和增长至关重要。Flutter 提供了一系列的库来满足不同的需要,确保稳定性、可测试性和可维护性。无论你是初创公司还是曾经建设起来的企业,抉择适合的状态治理库对于胜利至关重要。

感激浏览本文

如果有什么倡议,请在评论中让我晓得。我很乐意改良。


© 猫哥
ducafecat.com

end

正文完
 0