Flutter插件hisma_flutter的探索使用

Flutter插件hisma_flutter的探索使用

Flutter插件hisma_flutter的特性

  • 将屏幕创建器映射到状态
  • 将覆盖屏幕创建器映射到状态
  • 将对话框创建器映射到状态
  • 将实用程序创建器([NoUIChange])映射到状态
  • 支持层次结构 - 嵌套导航

示例演示

在以下示例中,我们将逐步展示如何使用hisma_flutter插件来构建一个具有不同功能的应用程序。

最小应用:单页面

让我们创建一个最简单的hisma_flutter应用程序,该应用程序包含一个单一状态机和一个映射到此状态的单一屏幕。

状态机声明

enum S { a }

enum E { e1 }

enum T { t1 }

final machine = StateMachineWithChangeNotifier<S, E, T>(
  initialStateId: S.a,
  name: 'machine',
  states: {
    S.a: hisma.State(),
  },
  transitions: {},
);

屏幕定义

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Screen')),
    );
  }
}

屏幕与状态映射

final hismaRouterGenerator = HismaRouterGenerator<S, Widget, E>(
  machine: machine,
  creators: {S.a: MaterialPageCreator<S>(widget: const Screen())},
);

配置主Widget

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerDelegate: hismaRouterGenerator.routerDelegate,
      routeInformationParser: hismaRouterGenerator.routeInformationParser,
    );
  }
}

启动状态机

Future<void> main() async {
  hisma.StateMachine.monitorCreators = [
    (m) => VisualMonitor(m),
  ];

  await machine.start();
  runApp(const MyApp());
}

简单应用:三页面

接下来,我们创建一个包含三个状态和三个屏幕的应用程序。

状态机声明

enum S { a, b, c }

enum E { forward, backward }

enum T { toA, toB, toC }

final machine = StateMachineWithChangeNotifier<S, E, T>(
  events: E.values,
  initialStateId: S.a,
  name: 'machine',
  states: {
    S.a: hisma.State(
      etm: {
        E.forward: [T.toB],
      },
    ),
    S.b: hisma.State(
      etm: {
        E.forward: [T.toC],
        E.backward: [T.toA],
      },
    ),
    S.c: hisma.State(
      etm: {
        E.backward: [T.toB],
      },
    ),
  },
  transitions: {
    T.toA: hisma.Transition(to: S.a),
    T.toB: hisma.Transition(to: S.b),
    T.toC: hisma.Transition(to: S.c),
  },
);

屏幕定义

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ScreenA')),
      body: createButtonsFromStates([machine.states[S.a]]),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ScreenB')),
      body: createButtonsFromStates([machine.states[S.b]]),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ScreenC')),
      body: createButtonsFromStates([machine.states[S.c]]),
    );
  }
}

屏幕与状态映射

final hismaRouterGenerator = HismaRouterGenerator<S, Widget, E>(
  machine: machine,
  creators: {
    S.a: MaterialPageCreator<S>(widget: const ScreenA()),
    S.b: MaterialPageCreator<S>(widget: const ScreenB()),
    S.c: MaterialPageCreator<S>(widget: const ScreenC()),
  },
);

使用覆盖页面

在本节中,我们将使用覆盖页面。这将导致ScreenC堆叠在ScreenB上,而ScreenB又堆叠在ScreenA上。

映射屏幕到状态

final hismaRouterGenerator = HismaRouterGenerator<S, Widget, E>(
  machine: machine,
  creators: {
    S.a: MaterialPageCreator<S>(widget: const ScreenA()),
    S.b: OverlayMaterialPageCreator<S, E>(
      widget: const ScreenB(),
      event: E.backward,
    ),
    S.c: OverlayMaterialPageCreator<S, E>(
      widget: const ScreenC(),
      event: E.backward,
    ),
  },
);

对话框

在这一部分中,我们将扩展之前的状态机以包括两个新的状态:b1表示一个AboutDialogc1表示一个DatePicker

新状态机声明

enum S { a, b, b1, c, c1 }

enum E { forward, show, backward }

enum T { toA, toB, toB1, toC, toC1 }

final machine = StateMachineWithChangeNotifier<S, E, T>(
  events: E.values,
  initialStateId: S.a,
  name: 'machine',
  states: {
    S.a: hisma.State(
      etm: {
        E.forward: [T.toB],
      },
    ),
    S.b: hisma.State(
      etm: {
        E.forward: [T.toC],
        E.backward: [T.toA],
        E.show: [T.toB1],
      },
      onEntry: getAction(),
    ),
    S.b1: hisma.State(
      etm: {
        E.backward: [T.toB],
      },
    ),
    S.c: hisma.State(
      etm: {
        E.backward: [T.toB],
        E.show: [T.toC1],
      },
      onEntry: getAction(),
    ),
    S.c1: hisma.State(
      etm: {
        E.backward: [T.toC],
      },
    ),
  },
  transitions: {
    T.toA: hisma.Transition(to: S.a),
    T.toB: hisma.Transition(to: S.b),
    T.toB1: hisma.Transition(to: S.b1),
    T.toC: hisma.Transition(to: S.c),
    T.toC1: hisma.Transition(to: S.c1),
  },
);

hisma.Action getAction() => hisma.Action(
      description: 'Print out argument passed.',
      action: (machine, dynamic arg) async =>
          print('Arg passed: $arg'),
    );

创建对话框

Future<bool?> b1(BuildContext context) => showDialog<bool>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Simple AlertDialog'),
          content: const Text('Hello'),
          actions: <Widget>[
            TextButton(
              child: const Text('OK'),
              onPressed: () {
                Navigator.of(context).pop(true);
              },
            ),
          ],
        );
      },
    );

Future<DateTime?> c1(BuildContext context) => showDatePicker(
      context: context,
      firstDate: DateTime(2021),
      initialDate: DateTime.now(),
      currentDate: DateTime.now(),
      lastDate: DateTime(2028),
    );

映射屏幕到状态

final hismaRouterGenerator = HismaRouterGenerator<S, Widget, E>(
  machine: machine,
  creators: {
    S.a: MaterialPageCreator<S>(widget: const ScreenA()),
    S.b: OverlayMaterialPageCreator<S, E>(
      widget: const ScreenB(),
      event: E.backward,
    ),
    S.b1: PagelessCreator(show: b1, event: E.backward),
    S.c: OverlayMaterialPageCreator<S, E>(
      widget: const ScreenC(),
      event: E.backward,
    ),
    S.c1: PagelessCreator(show: c1, event: E.backward),
  },
);

实用状态

在某些情况下,你可能希望某个状态不映射到任何UI元素。在这种情况下,我们可以使用NoUIChange创建器。

扩展状态机

S.b2: hisma.State(
  etm: {
    E.backward: [T.toBFromB2],
  },
  onEntry: hisma.Action(
    description: 'Fetch weather report.',
    action: (machine, dynamic arg) async {
      Future<void>.delayed(const Duration(seconds: 1), () {
        print('Weather data is fetched.');
        machine.fire(E.backward, data: 'Sunny weather.');
      });
    },
  ),
),

映射屏幕到状态

final hismaRouterGenerator = HismaRouterGenerator<S, Widget, E>(
  machine: machine,
  creators: {
    S.a: MaterialPageCreator<S>(widget: const ScreenA()),
    S.b: OverlayMaterialPageCreator<S, E>(
      widget: const ScreenB(),
      event: E.backward,
    ),
    S.b1: PagelessCreator(show: b1, event: E.backward),
    S.b2: NoUIChange(),
    S.c: OverlayMaterialPageCreator<S, E>(
      widget: const ScreenC(),
      event: E.backward,
    ),
    S.c1: PagelessCreator(show: c1, event: E.backward),
  },
);

添加层次结构

最后,我们将添加层次结构,以便我们的状态机可以嵌套导航。

状态机声明

enum AS { signedIn, signedOut }

enum AE { signIn, signOut }

enum AT { toSignedIn, toSignedOut }

final authMachine = StateMachineWithChangeNotifier<AS, AE, AT>(
  events: AE.values,
  name: 'authMachine',
  initialStateId: AS.signedOut,
  states: {
    AS.signedOut: hisma.State(
      etm: {
        AE.signIn: [AT.toSignedIn],
      },
    ),
    AS.signedIn: hisma.State(
      etm: {
        AE.signOut: [AT.toSignedOut],
      },
      regions: [
        hisma.Region<AS, AE, AT, S>(machine: machine),
      ],
    ),
  },
  transitions: {
    AT.toSignedOut: hisma.Transition(to: AS.signedOut),
    AT.toSignedIn: hisma.Transition(to: AS.signedIn),
  },
);

屏幕定义

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('LoginScreen')),
      body: createButtonsFromStates([authMachine.states[AS.signedOut]]),
    );
  }
}

映射屏幕到状态

final authRouterGenerator = HismaRouterGenerator<AS, Widget, AE>(
  machine: authMachine,
  creators: {
    AS.signedOut: MaterialPageCreator<AS>(widget: const LoginScreen()),
    AS.signedIn: MaterialPageCreator<AS>(
      widget: Router(routerDelegate: hismaRouterGenerator.routerDelegate),
    ),
  },
);

更多关于Flutter插件hisma_flutter的探索使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter插件hisma_flutter的探索使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


hisma_flutter 是一个基于 Hisma 状态管理库的 Flutter 插件,它允许开发者通过状态机的方式来管理应用的复杂状态。Hisma 是一个轻量级的状态机库,专注于简化状态管理和状态转换的复杂性。hisma_flutter 将这些概念引入到 Flutter 中,使得开发者可以更容易地管理应用的状态。

由于 hisma_flutter 是一个相对较新的插件,可能没有广泛的文档或社区支持,因此在探索和使用时可能需要一些实验和调试。以下是一些基本步骤和概念,帮助你开始使用 hisma_flutter

1. 安装 hisma_flutter

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

dependencies:
  flutter:
    sdk: flutter
  hisma_flutter: ^0.0.1  # 请使用最新版本

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

2. 基本概念

hisma_flutter 基于状态机(State Machine)的概念,状态机由以下几个核心部分组成:

  • States(状态):应用可以处于的不同状态。
  • Events(事件):触发状态转换的事件。
  • Transitions(转换):定义状态之间如何转换。
  • Actions(动作):在状态转换时执行的操作。

3. 创建一个简单的状态机

假设我们有一个简单的应用,只有两个状态:IdleActive

import 'package:hisma_flutter/hisma_flutter.dart';

enum States { idle, active }
enum Events { activate, deactivate }

final stateMachine = StateMachine<States, Events, void>(
  initialState: States.idle,
  states: {
    States.idle: State(
      onEntry: [Action(() => print('Entered Idle state'))],
      onExit: [Action(() => print('Exited Idle state'))],
    ),
    States.active: State(
      onEntry: [Action(() => print('Entered Active state'))],
      onExit: [Action(() => print('Exited Active state'))],
    ),
  },
  transitions: {
    States.idle: {
      Events.activate: Transition(to: States.active),
    },
    States.active: {
      Events.deactivate: Transition(to: States.idle),
    },
  },
);

4. 在 Flutter 中使用状态机

你可以在 Flutter 的 StatefulWidget 中使用这个状态机来管理状态。

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

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

class _MyAppState extends State<MyApp> {
  late final StateMachine<States, Events, void> stateMachine;

  [@override](/user/override)
  void initState() {
    super.initState();
    stateMachine = createStateMachine(); // 使用之前定义的状态机
    stateMachine.start();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Hisma Flutter Example')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  stateMachine.fire(Events.activate);
                },
                child: Text('Activate'),
              ),
              ElevatedButton(
                onPressed: () {
                  stateMachine.fire(Events.deactivate);
                },
                child: Text('Deactivate'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    stateMachine.stop();
    super.dispose();
  }
}
回到顶部