Super


功能

  • 响应式状态管理
  • 有效的依赖注入
  • 小部件控制器的生命周期管理
  • 用于构建响应式 UI 组件的小部件构建器
  • 直观的测试(无需设置/拆卸),专用的测试库 super_test

入门

将 Super 添加到您的 pubspec.yaml 文件中

dependencies:
  flutter_super:

将 Super 包导入到您的项目中

import 'package:flutter_super/flutter_super.dart';

用法

计数器应用

计数器应用测试

让我们分解一下计数器应用示例。

main.dart

main.dart 文件作为应用程序的入口点。它通过将根小部件包装在 SuperApp 中来设置项目所需的框架,从而启用 Super 框架。

void main() {
  runApp(
    // Adding SuperApp enables the framework for the project
    const SuperApp(child: MyApp()), // Step 1
  );
}

MyApp 是应用程序的根小部件。它是一个无状态小部件,将其子项返回为 MaterialAppMaterialApp 将应用程序的主视图设置为 HomeView

// Define the root widget of the application
class MyApp extends StatelessWidget {
  const MyApp({super.key});

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

HomeController 是一个扩展 SuperController 的控制器类。它为应用程序中的计数器功能管理状态和逻辑。它声明一个 RxInt 对象 _count 来表示计数器的值,并提供一个 getter 方法 count 来访问当前计数器的值。它还定义了一个 increment 方法来将计数器的值增加 1。重写了 onDisable 方法,以便在控制器禁用时处理 _count 对象。

/// The SuperController mixin class allows you to define the 
/// lifecycle of your controller classes based on a [SuperWidget].
class HomeController extends SuperController { // Step 2
  // Declare Rx object as `final` for Immutability
  final _count = 0.rx; // RxInt(0);

  int get count => _count.value;

  void increment() { // Step 3
    _count.value++;
  }

  @override
  void onDisable() {
    _count.dispose(); // Dispose Rx object.
    super.onDisable();
  }
}

HomeView 是一个显示计数器并提供递增按钮的小部件。它扩展 SuperWidget<HomeController> 来初始化 HomeController 作为其控制器。它重写了 initController 方法来创建 HomeController 的实例。

class HomeView extends SuperWidget<HomeController> { // Step 4
  const HomeView({super.key});

  @override // Initialize the Widget Controller
  HomeController initController() => HomeController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter example')),
      body: Center(
        // SuperBuilder is a widget that listens to Rx objects used in
        // its builder method and rebuilds only when the state changes.
        child: SuperBuilder( // Step 5
          builder: (context) {
            // controller is the instance getter for the Controller of
            // the widget
            return Text(
              '${controller.count}',
              style: Theme.of(context).textTheme.displayLarge,
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Increment the count state by calling the increment() method
        onPressed: () => controller.increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

通过将逻辑分离到 HomeController 中,将 UI 分离到 HomeView 中,应用程序实现了业务逻辑层和表示层之间的清晰分离。HomeView 小部件负责根据 HomeController 提供的状态渲染 UI,而 HomeController 则负责计数器功能的底层逻辑和状态管理。

Super 框架 API

SuperApp

一个表示 Super 框架根的有状态小部件。[child] 参数是必需的,代表应用程序的主内容。

[mocks] 参数是用于在测试期间模拟依赖项的对象的可选列表。mocks 属性提供了一种在测试期间将模拟对象注入应用程序依赖项图的方法。这些模拟对象可以替换真实的依赖项,例如数据库连接或网络客户端,从而实现可控且可预测的测试场景。

重要提示: 使用 mocks 属性时,请确保提供已实例化的模拟对象,而不仅仅是它们的类型。例如,使用 [MockAuthRepo()] 而不是 [MockAuthRepo],以确保使用模拟对象。添加类型而不实例化模拟对象将导致模拟对象不被使用。

[testMode] 设置为 true 时,Super 框架将在测试模式下激活。测试模式可用于为 Super 框架启用额外的测试功能或特定行为。默认情况下,测试模式设置为 false

[autoDispose] 参数是一个可选的布尔值,用于确定 Super 框架是否应在不再需要时自动处理控制器、依赖项和其他资源。默认情况下,[autoDispose] 设置为 true,启用自动处理。如果要手动处理应用程序中资源的释放,请将 [autoDispose] 设置为 false

用法示例

SuperApp(
  mocks: [
  MockAuthRepo(),
  MockDatabase(),
  ],
  testMode: true,
  autoDispose: true,
  child: const MyApp(),
);

SuperController

一个 mixin 类,为应用程序中使用的控制器提供生命周期。

SuperController mixin 类允许您定义控制器类的生命周期。它提供了在小部件生命周期的特定点调用的方法,允许您初始化资源、处理事件以及在控制器不再需要时清理资源。由于它与小部件本身绑定,因此在控制器存活后,可以从控制器访问小部件的 BuildContext。

用法示例

class SampleController extends SuperController {
  final _count = 0.rx; // RxInt(0);
  final _loading = false.rx; // RxBool(false);

  int get count => _count.value;
  bool get loading => _loading.value;

  @override
  void onAlive() {
    context.showTextSnackBar('Controller Alive');
  }

  void increment() {
    _count.value++;
  }

  void toggleLoading() {
    _loading.value = !_loading.value;
  }

  @override
  void onDisable() {
    _count.dispose(); // Dispose Rx object.
    _loading.dispose();
    super.onDisable();
  }
}

在上面的示例中,SampleController 扩展了 SuperController 并定义了一个由 Rx 对象管理的 count 变量。increment() 方法用于递增 count 值。onDisable() 方法被重写,以便在控制器禁用时处理 Rx 对象。如上面的 SampleController 所示,一个控制器可能包含其对应小部件所需的多个状态,但是,为了保持控制器干净和专注,如果存在具有多个事件的状态,建议为该状态定义一个 RxNotifier

重要提示: 建议将 Rx 对象定义为私有的,并且只提供一个 getter 来访问状态。这有助于防止状态在控制器外部被更改,确保状态仅通过控制器内的定义方法(例如示例中的 increment())进行修改。

SuperModel

一个为类提供值相等性检查的类。扩展此类的类应实现 props getter,该 getter 返回应用于相等性检查的类属性列表。

用法示例

class UserModel with SuperModel {
  UserModel(this.id, this.name);

  final int id;
  final String name;

  @override
  List<Object> get props => [id, name]; // Important
}

final _user = UserModel(1, 'Paul').rx;
final user2 = UserModel(1, 'Paul');

_user.value == user2; // true
_user.value = user2; // Will not trigger a rebuild

Super 框架中的小部件

SuperWidget

一个 [StatelessWidget],它为处理 [SuperController] 的小部件提供基本功能。

此小部件为构建需要控制器来管理其状态和生命周期的小部件提供了基础。通过扩展 [SuperWidget] 并提供 [initController()] 的具体实现,您可以轻松地将控制器与小部件关联起来。它还利用 controller getter,该 getter 提供对小部件关联控制器的访问。

用法示例

class MyWidget extends SuperWidget<MyController> {
  @override
  MyController initController() => MyController();

  // Widget implementation...
}

重要提示: 建议每个小部件使用一个控制器,以确保适当的封装和关注点分离。每个小部件都应该有自己的专用控制器来管理其状态和生命周期。这种方法通过保持每个小部件及其关联控制器的职责分开来促进干净和模块化的代码。

如果您有一个不需要状态管理或与控制器交互的小部件,最好使用普通的 [StatelessWidget]。在没有状态的小部件中使用控制器可能会增加不必要的复杂性和开销。

SuperBuilder

[SuperBuilder] 小部件允许您响应 [Rx] 对象的更改来重建 UI 的一部分。它需要一个 [builder] 回调函数,该函数接收一个 [BuildContext] 并返回要构建的小部件。

每当 [Rx] 对象更改其值时,都会调用 [builder] 回调,从而触发小部件的重建。可以在 [builder] 回调中访问 [Rx] 对象,允许您将其值包含在 UI 中。

用法示例

SuperBuilder(
  builder: (context) {
    // return widget here based on Rx state
  }
)

重要提示: 您需要在 builder 方法中使用 [Rx] 对象的值,否则会导致错误。

注意: 如果您希望在 builder 方法之外指定 Rx 对象,即您选择使 Rx 对象非私有,那么请使用 SuperConsumer 小部件。

[buildWhen] 参数是一个可选条件,用于确定当 [Rx] 对象更改时是否应调用 [builder]。如果 [buildWhen] 求值为 false,则不会调用 [builder],也不会重建子小部件。

用法示例

SuperBuilder(
  buildWhen: () => controller.count > 3,
  builder: (context) {
    // return widget here based on Rx state
  }
)

SuperConsumer

[SuperConsumer] 是一个 StatefulWidget,它侦听 [Rx] 对象的更改,并在 [Rx] 对象的状态更改时重建其子小部件。

[SuperConsumer] 小部件需要一个 [builder] 函数,该函数在 [Rx] 对象更改时被调用。[builder] 函数接收当前的 [BuildContext] 和 [Rx] 对象的最新状态,并返回要构建的小部件树。

用法示例

final counter = CounterNotifier();

// ...

SuperConsumer<int>(
  rx: counter,
  builder: (context, state) {
    return Text('Count: $state');
  },
)

在上面的示例中,创建了一个 [SuperConsumer] 小部件,并为其提供了一个名为 counter 的 CounterNotifier 对象。每当 counter 的状态更改时,builder 函数都会使用最新状态被调用,并返回一个显示计数器的 [Text] 小部件。

SuperListener

[SuperListener] 小部件侦听提供的 rx 对象的变化,并在 rx 对象更改其值时调用 [listener] 回调。

当 rx 对象更改其值时,将调用 [listener] 回调一次。

[child] 参数是此小部件渲染的可选子小部件。

[listenWhen] 参数是一个可选条件,用于确定当 rx 对象更改时是否应调用 [listener]。如果 [listenWhen] 求值为 true,则将调用 [listener];否则,将跳过它。

用法示例

SuperListener<int>(
  listen: () => controller.count;
  listenWhen: (count) => count > 5,
  listener: (context) {
    // Handle the state change here
  }, // Will only call the listener if count is greater than 5
  child: Text('Counter'),
)

重要提示: 您需要在 listen 参数中使用 [Rx] 对象的值,否则会导致错误。

AsyncBuilder

一个根据异步计算的状态来构建自身的状态小部件。

[builder] 参数是一个必需的回调函数,该函数返回要构建的小部件。

[future] 参数表示一个异步计算,该计算将在完成时触发重建。

[stream] 参数表示一个异步数据流,在有新数据可用时将触发重建。

[loading] 参数表示一个在异步计算进行时显示的部件。

[error] 参数表示一个部件构建器,在异步计算中发生错误时构建错误部件。

[initialData] 参数表示在非空 [future][stream] 完成之前用于创建快照的初始数据。

用法示例

AsyncBuilder(
    builder: (data) => ,
    error: (error, stackTrace) => ,
    loading: ,
),

重要提示: 应该一次使用一个 future 或 stream,同时使用两者都会导致错误。

Rx 类型

RxT

一个用于保存类型为 T 的值的响应式容器。

RxT 类是 Rx 类的特化,表示响应式值。它允许您存储和更新类型为 T 的值,并在值更改时自动通知其侦听器。

用法示例

final _counter = RxT<int>(0); // same as RxInt(0) or 0.rx 

void incrementCounter() {
  _counter.value++;
}

RxT 子类型

  • RxInt
  • RxString
  • RxBool
  • RxDouble

它最适合用于本地状态,即在单个控制器中使用的状态。

注意: 使用 RxT 类时,为防止内存泄漏,请务必在对象不再需要时调用其 dispose() 方法。这可以通过控制器上的 onDisable 方法来完成。

RxNotifier

一个抽象基类,用于创建管理类型为 T 的状态的响应式通知器。

RxNotifier 类为创建响应式通知器提供了基础,这些通知器封装了一块不可变的状态并在状态更改时通知其侦听器。RxNotifier 的子类必须重写 watch 方法以提供初始状态并实现更新状态的逻辑。

用法示例

class CounterNotifier extends RxNotifier<int> {
  @override
  int watch() {
    return 0; // Initial state
  }

  void increment() {
    state++; // Update the state
  }
}

final counter = CounterNotifier();

它最适合用于全局状态,即多个控制器使用的状态,但也可以用于单个控制器来抽象状态及其事件,例如,如果一个状态有很多事件,与其使控制器复杂化,不如为该单个状态使用 RxNotifier。

注意: 使用 RxNotifier 类时,为防止内存泄漏,请务必在对象不再需要时调用其 dispose() 方法。这可以通过控制器上的 onDisable 方法来完成。

Rx 集合

这些与 RxT 类似,但不需要使用 .value,它们通过响应式扩展了常规 Dart 集合的功能。

  • RxMap
  • RxSet
  • RxList

依赖注入

从管理器检索依赖项的实例,并在依赖项扩展 SuperController 时启动控制器。

Super.of<T>();

init

初始化并检索依赖项的实例,或者在不存在时创建新实例。

Super.init<T>(T instance);

create

创建依赖项的单例实例并将其注册到管理器。

Super.create<T>(T instance, {bool lazy = false});

delete

从管理器中删除依赖项的实例。如果 autoDispose 设置为 false,则必须将 [force] 设置为 true 才能删除资源。

Super.delete<T>({String? key, bool force = false});

deleteAll

从管理器中删除所有依赖项的实例。如果 autoDispose 设置为 false,则必须将 [force] 设置为 true 才能删除资源。

Super.deleteAll({bool force = false});

有用的 API

错误处理

用于处理 [Future] 结果的扩展方法,带有成功和错误回调。

result 方法允许您提供两个回调:一个用于处理 [Future] 成功完成时的成功情况,另一个用于处理发生异常时的错误情况。

用法示例

Future<int> fetchNumber() async {
  // Simulating an asynchronous operation
  await Future.delayed(Duration(seconds: 2));

  // Simulating an error
  throw Failure('Failed to fetch number');
}

void handleSuccess(int number) {
  print('Fetched number: $number');
}

void handleError(Failure error) {
  print('Error occurred: ${error.message}');
}

void main() {
  fetchNumber().result(handleError, handleSuccess);

  // or

  final request = fetchNumber();

  request.result<Failure, int>(
  (e) => print('Error occurred: ${e.message}');  // could replace `e` with error
  (s) => print('Fetched number: $s');            // could replace `s` with number
  );
}

context.read

其工作方式与 Super.of<T>() 完全相同,但带有 BuildContext,并且非常熟悉。

context.read<T>();

附加信息

Super 结构

有关整理项目的干净方法,请查看 Super Structure

有关所有 API 的更多信息,请查看 API 参考

要求

  • Dart 3: >= 3.0.0

维护者

开发者笔记

大家好,我是 DrDejaVu!我付出了巨大的努力来以一种可读且易于理解的方式构建和记录 Super 框架。我想创建一个符合 Flutter 团队设定的高标准的框架,他们为记录 Flutter 框架做了令人惊叹的工作。

在使用 Super 进行开发时,您可能会注意到 API 名称与其他状态管理解决方案(如 Bloc 等)相似。这是因为我从这些解决方案中汲取了灵感,并利用了我之前与它们打交道的经验来创建 Super。通过采用熟悉的观念和命名约定,我的目标是为已经熟悉这些状态管理解决方案的开发人员降低学习曲线。

我希望您觉得 Super 框架和我预期的那样令人愉悦且易于使用。如果您有任何反馈或改进建议,请随时与我联系。祝您编码愉快!

致以最诚挚的问候,DrDejaVu

鸣谢

所有功劳归于全能的上帝,他引导我完成了这个项目。

GitHub

查看 Github