Flutter状态管理宏定义插件inherited_model_macro的使用

Flutter状态管理宏定义插件inherited_model_macro的使用

宏生成InheritedModel模板代码并简化状态管理

Dart 宏功能正在开发中,并且目前处于暂停状态,因此此包可能无法正常工作,直到宏功能发布后才会修复。

通过在类中声明字段(以及更新这些字段的方法)并使用[@InheritedModelMacro](/user/InheritedModelMacro)()进行注解,可以生成完整的InheritedModel代码(该类必须扩展InheritedModel<String>):

[@InheritedModelMacro](/user/InheritedModelMacro)()
class LogoModel extends InheritedModel<String> {
  final Color? backgroundColor; // 设置值为null也是支持的,在更新方法中
  final bool large;

  void toggleColor(BuildContext context) {
    final newValue = (backgroundColor == null) ? Colors.red : null;
    updateState(backgroundColor: newValue); // updateState 是生成的方法
  }

  void toggleSize(BuildContext context) {
    updateState(large: large != true);
  }
}

然后像ProviderInheritedWidget一样插入生成的持有者类,并提供字段的初始值:

Scaffold(
  body: const LogoModelHolder(
    backgroundColor: Colors.blue,
    large: false,
    child: Content(),
  )
)

在您的类中将自动生成以下代码:

class LogoModel {
  // 获取字段值
  static FieldType readField(context) // 对于每个字段

  // 获取字段值并订阅其更改。当值更改时,小部件将被重绘
  static FieldType watchField(context) // 对于每个字段

  // 查找树中最近的实例并更新给定字段
  static void update(context, {fields?}) 

  // 直接更新字段
  void updateState({fields?}) 

  // 获取树中最近的类实例
  static Type getInstance(context) 
}

您可以这样使用它:

// 获取当前值并更新它
final currentSize = LogoModel.readLarge(context);
LogoModel.update(context, large: !currentSize);

// 订阅更改 
final Color? color = LogoModel.watchBackgroundColor(context);

使用示例可以在这里找到。生成的代码可以在这里查看这里

注意事项:

  • 您的类必须扩展InheritedModel<String>,直到未来版本中宏功能extendType不再破坏分析器。
  • Dart SDK 版本必须是^3.5.0-152或更高版本,并且在analysis_options.yaml中启用宏并且在构建期间启用宏(文档)。
  • 分析器可能会在对宏本身以外的其他项目应用宏时抛出错误,因为生成的代码不存在。与其处理不存在的代码,您可以尝试将此包解压到您的项目中(issue)。

在幕后

持有者类提供了setState回调给您的类,以便在持有者内部更新值并向下提供给您的类。

InheritedModel和其他InheritedWidget一样,用于O(1)树搜索。但是它被标记为@immutable,所以所有字段都应该是最终的并且不能更改->由持有者提供。

此外,InheritedModel很有用,因为它允许单独订阅字段的更改。这是通过将字段名称作为aspect传递给inheritFrom方法来实现的。这就是为什么您的类必须扩展InheritedModel<String>

设置可空字段是通过获取Object stub来实现的,以处理默认参数为空的情况,并将其与当前字段值进行比较。

其余部分是基本的InheritedModel模板代码。


示例代码

import 'package:flutter/material.dart';
import 'package:inherited_model_macro/inherited_model_macro.dart';

/// 修改自[InheritedModel文档](https://api.flutter.dev/flutter/widgets/InheritedModel-class.html#widgets.InheritedModel.2)
[@InheritedModelMacro](/user/InheritedModelMacro)()
class LogoModel extends InheritedModel<String> {
  final Color? backgroundColor;
  final bool large;

  void toggleColor(BuildContext context) {
    final newValue = (backgroundColor == null) ? Colors.red : null;
    updateState(backgroundColor: newValue);
  }
}

void main() => runApp(const InheritedModelApp());

class InheritedModelApp extends StatelessWidget {
  const InheritedModelApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: InheritedModelExample(),
    );
  }
}

class InheritedModelExample extends StatefulWidget {
  const InheritedModelExample({super.key});
  [@override](/user/override)
  State<InheritedModelExample> createState() => _InheritedModelExampleState();
}

class _InheritedModelExampleState extends State<InheritedModelExample> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('InheritedModel Sample')),
      body: const LogoModelHolder(
        backgroundColor: Colors.blue,
        large: false,
        child: Content(),
      ),
    );
  }
}

class Content extends StatelessWidget {
  const Content({
    super.key,
  });

  [@override](/user/override)
  Widget build(BuildContext context) {
    print("Rebuild content");
    return const Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        Center(
          child: BackgroundWidget(
            child: LogoWidget(),
          ),
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            UpdateBackgroudButton(),
            UpdateSizeButton(),
          ],
        )
      ],
    );
  }
}

class UpdateBackgroudButton extends StatelessWidget {
  const UpdateBackgroudButton({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    print("Rebuild UpdateBackgroudButton");
    return ElevatedButton(
      onPressed: () => LogoModel.getInstance(context).toggleColor(context),
      child: const Text('Update background'),
    );
  }
}

class UpdateSizeButton extends StatelessWidget {
  const UpdateSizeButton({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    print("Rebuild UpdateSizeButton");
    return ElevatedButton(
      onPressed: () {
        final currentSize = LogoModel.readLarge(context);
        LogoModel.update(context, large: !currentSize);
      },
      child: const Text('Resize Logo'),
    );
  }
}

class BackgroundWidget extends StatelessWidget {
  const BackgroundWidget({super.key, required this.child});

  final Widget child;

  [@override](/user/override)
  Widget build(BuildContext context) {
    final Color? color = LogoModel.watchBackgroundColor(context);
    print("Rebuild BackgroundWidget");
    return AnimatedContainer(
      padding: const EdgeInsets.all(12.0),
      color: color ?? Colors.green,
      duration: const Duration(seconds: 2),
      curve: Curves.fastOutSlowIn,
      child: child,
    );
  }
}

class LogoWidget extends StatelessWidget {
  const LogoWidget({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    final largeLogo = LogoModel.watchLarge(context) == true;
    print("Rebuild LogoWidget");
    return AnimatedContainer(
      padding: const EdgeInsets.all(20.0),
      duration: const Duration(seconds: 2),
      curve: Curves.fastLinearToSlowEaseIn,
      alignment: Alignment.center,
      child: FlutterLogo(size: largeLogo ? 200.0 : 100.0),
    );
  }
}

更多关于Flutter状态管理宏定义插件inherited_model_macro的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter状态管理宏定义插件inherited_model_macro的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


inherited_model_macro 是一个用于 Flutter 的宏定义插件,它旨在简化使用 InheritedModel 进行状态管理的过程。通过使用宏,你可以减少样板代码,使得状态管理的实现更加简洁和高效。

安装

首先,你需要在 pubspec.yaml 中添加 inherited_model_macro 依赖:

dependencies:
  flutter:
    sdk: flutter
  inherited_model_macro: ^1.0.0  # 请检查最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

inherited_model_macro 提供了一个宏 @InheritedModel,你可以将其应用于一个类,自动生成必要的 InheritedModel 相关代码。

1. 定义一个状态管理类

import 'package:flutter/material.dart';
import 'package:inherited_model_macro/inherited_model_macro.dart';

@InheritedModel()
class CounterModel extends InheritedWidget {
  final int count;

  CounterModel({
    Key? key,
    required this.count,
    required Widget child,
  }) : super(key: key, child: child);

  [@override](/user/override)
  bool updateShouldNotify(CounterModel oldWidget) {
    return oldWidget.count != count;
  }

  static CounterModel? of(BuildContext context, {bool listen = true}) {
    return listen
        ? context.dependOnInheritedWidgetOfExactType<CounterModel>()
        : context.getInheritedWidgetOfExactType<CounterModel>();
  }
}

2. 使用 CounterModel 在 Widget 树中传递状态

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterModel(
        count: 0,
        child: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final counterModel = CounterModel.of(context)!;

    return Scaffold(
      appBar: AppBar(
        title: Text('InheritedModel Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counterModel.count}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // TODO: Update the counter
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

3. 更新状态

由于 InheritedModel 是不可变的,你需要通过将其包裹在另一个 Widget 中来更新状态。通常可以使用 StatefulWidget 来实现状态的更新。

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int _count = 0;

  void _incrementCounter() {
    setState(() {
      _count++;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterModel(
        count: _count,
        child: MyHomePage(
          onIncrement: _incrementCounter,
        ),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final VoidCallback onIncrement;

  MyHomePage({required this.onIncrement});

  [@override](/user/override)
  Widget build(BuildContext context) {
    final counterModel = CounterModel.of(context)!;

    return Scaffold(
      appBar: AppBar(
        title: Text('InheritedModel Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counterModel.count}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: onIncrement,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
回到顶部