Flutter状态管理插件Okito的使用

Flutter状态管理插件Okito的使用

特性

状态管理

创建控制器

class CounterController extends OkitoController {
  int count = 0;

  void increment() => setState(() => count++);

  void decrement() {
    count--;
    update();
  }
}

CounterController counterController = CounterController();

你也可以使用 setStateAsync 函数来异步更新数据。

使用控制器

OkitoBuilder(
  controller: counterController,
  builder: () => Text('${counterController.count}'),
);

更新控制器

main(){
  // 你可以从任何地方更改状态,而无需上下文!
  counterController.increment();
}

// 在Flutter中
ElevatedButton(
  onPressed: counterController.increment,
  child: const Text('Increment'),
)
// 或者
ElevatedButton(
  onPressed: () => counterController.setState(() => counterController.count--),
  child: const Text('Decrement'),
)

Rockitos

Rockitos 是我们使用依赖注入进行状态管理的方式!

// Rockito - 我最喜欢使用Okito状态的方式。
Rockito<CounterController>(
  (controller) => Text('${controller.count}')
);
// 非常简单吧?
// 要使用 Rockito,你应该首先注入 CounterController。
Okito.inject(CounterController);
// RockitoBuilder - Rockito但具有更多功能和构建器!
RockitoBuilder<CounterController>(
  inject: CounterController(), // 可选,如果你还没有注入它。
  builder: (controller) => Text('${controller.count}'),
  // 你可以在这里使用所有 OkitoBuilder 的功能,比如 otherControllers 等等。
);

监视控制器

OkitoWatcher(
  watch: counterController,
  onChange: (CounterController controller) {
    // 你也可以在那里更新状态。
    // onChange 给你的就是控制器的实例。
    print(controller.count);
  },
);
counterController.increment();

// OkitoWatcher 还会返回一个停止监视的函数,这可以减少内存使用,当你不再需要时可以使用它。
final stopWatching = OkitoWatcher(/* 代码在此 */);
// 做你想做的事情,然后:
stopWatching();
// 你也可以用 Rockitos 来监视。
RockitoWatcher<CounterController>(
  (controller) => print(controller.count)
);
// 你必须先注入控制器。

状态方法

状态方法类似于 Flutter 中的 State 类的方法。

class CounterController extends OkitoController{
  int count = 0;

  // 当你的 [OkitoBuilder] 挂载到小部件树时将被调用。
  @override
  void initState() {
    count++;
  }
  // 当你的 [OkitoBuilder] 从小部件树中移除时将被调用。
  @override
  void dispose() {
    count = 0;
  }

  // 当你使用 Okito 的注入方法时将被调用。
  @override
  void onInject() {
    count++;
  }
}

我个人在使用控制器作为 StatefulWidget 替换器时使用状态方法。

class EditProductController extends OkitoController {
  // 其他节点在此
  final priceFocusNode = FocusNode();

  void submitForm(){
    // 我在这里使用我的 inputControllers 获取它们的值。
    // 然后我使用我的 Okito 路由而不使用上下文!
    Okito.pushNamed('/productUpdatedSuccessfully/31')
  }

  @override
  void dispose() {
    // 其他节点在这里 [dispose]。
    priceFocusNode.dispose();
  }
}

你可能在应用中使用了流,并且可能希望监视它们,根据变化更新状态等等。

class YourController extends OkitoController with OkitoStreamMixin{
  // 你的代码,完全一样。
}
// 对于常规流。
class CounterController extends OkitoController with OkitoStreamMixin {
  int count = 0;
  @override
  void onInject(){
    initStream<int>(stream: yourIntStream, onData(data) {
      count = data; // 你不需要做任何事情或更新。
    });
  }
}
// 对于 Firestore 查询流
// 使用时,你需要获取查询的快照作为流。
class PeopleController extends OkitoController with OkitoStreamMixin {
  List<Person> people = [];
  @override  // 或者,你可以使用构造函数。
  void onInject(){
    initFirestoreQueryStream(querySnapshot: querySnapshot, onData(data) {
      // 这些数据是你集合中的文档(映射)列表。
      people = data;
    });
  }
}
// 对于 Firestore 查询流
// 使用时,你需要获取查询的快照作为流。
class PersonController extends OkitoController with OkitoStreamMixin{
  Person person = Person();
  @override  // 或者,你可以使用构造函数。
  void onInject(){
    initFirestoreDocumentStream(
      documentSnapshot: documentSnapshot,
      onData(data) {
        // 这些数据是你的文档作为映射。
        person = Person.fromMap(data);
      });
  }
}

工具

导航和使用不带上下文的小部件

首先,我们应该用 Okito 包裹我们的应用或手动提供 Okito。

// 基本上,你应该在你的应用开始处添加 *Okito* 或手动提供 key/observers。

OkitoMaterialApp(/* 所有东西都与 [MaterialApp] 相同 */);

// 或者
OkitoCupertinoApp(/* 所有东西都与 [CupertinoApp] 相同 */);

// 或者
Material/CupertinoApp(
    navigatorKey: Okito.navigatorKey,
    navigatorObservers: [OkitoObserver()]);

然后你就可以使用所有 Okito 的好处!

Okito.width;
Okito.height;
Okito.aspectRatio;
Okito.devicePixelRatio;
Okito.isLandscape;
Okito.isPortrait;
Okito.theme;

Okito.showSnackBar();
Okito.showToast(); // 不带小部件的 Snackbar,适合简单的使用。
Okito.showModal();
Okito.showDialog();

Okito.push();
Okito.pushReplacement();
Okito.pushNamed();
Okito.pushReplacementNamed();
Okito.pop();
Okito.arguments;
Okito.routeName;

路由管理

假设你想创建动态 URL,例如:

/posts/:id
/posts/23

这个 id 是一个动态变量,对吗?

使用 Okito,你可以轻松地做到这一点!

// 你不需要添加像 OkitoPage 或其他东西。
// Okito 让你不用改变代码就能完成任务。
OkitoMaterialApp(
  routes: {
    '/': (ctx) => FirstPage(),
    '/second/:id': (ctx) => SecondPage(),
  }
);

现在,每当你尝试这样做时:

ElevatedButton(
  onPressed: () => Okito.pushNamed(
    // 你可以添加任何类型的参数
    '/second/33?name=Rago&postId=123&isMaterial=true',
    arguments: 'This is an extra argument'),
    child: const Text('Go to second page'),
  )

它将把页面推送到第二个页面,参数 [id] : [33]

此外,你会看到你的参数如下:

print(Okito.arguments);
// 结果
{'id' : '33', 'name' : 'Rago', 'postId' : 123, 'isMaterial' : true, 'arguments': 'This is an extra argument'};
// 是的,你甚至可以手动获取额外的参数。

检查 example/flutter_dynamic_routing/lib/main.dart 以查看实时示例。

主题管理

// 首先,底部的方法给你应用程序控制器,你可以手动更新任何东西。
Okito.app; /* 或 */ Okito.use<AppController>();

// 然后你将拥有它的所有用法。

Okito.app.setThemeData();
Okito.app.setThemeMode();
Okito.app.setCupertinoThemeData();

Okito.app.locale;
Okito.app.setLocale();

本地存储

OkitoStorage 是一种将变量保存到本地存储的方法。

// 它的工作方式类似于 SharedPereferences,但它同步,就像 GetStorage。

// 要使用,你应该初始化存储,这在 Web 上不是必需的,但在其他平台上是必需的。

void main() async{
  // 只有初始化是异步的,你也可以在没有 await 的情况下调用它,但这不推荐。
  await OkitoStorage.init();

  // 使用
  final box = OkitoStorage; // 为了更方便的引用。

  box.write('count', 0);

  final int count = box.read<int>('count');
  // 就这么简单!

  print('Count is $count');
  // 你的其余代码将在这里。
}

其他用法

box.watchKey('count', () =>
  print('这个函数将在计数改变时被调用。')
);

box.watchAll(() =>
  print('这个函数将在存储改变时被调用。')
);

box.removeKey('count'); // 移除键

box.readAllKeys(); // 返回存储中的所有键

box.readAllValues(); // 返回存储中的所有值

box.readAll(); // 返回整个存储

box.clearStorage(); // 从存储中删除所有内容,但存储仍然存在。

box.deleteStorage(); // 从文件系统中完全删除存储,在此操作之后,OkitoStorage 将无法写入或读取。

使用 OkitoBuilder 观察 OkitoStorage

// 查看 example/flutter_okito_storage/lib/main.dart 以获取更多示例!

// 每当 'count' 键发生变化时都会运行。
OkitoBuilder(
  controller: yourController,
  watchStorageKeys: ['count'],
  builder: () => Text('${box.read('count')}'),
);

OkitoStorage 的优点

  • 极速
  • 你可以从任何地方观察更改,甚至在你的构建器中
  • 它是同步的,所以你不需要使用 ‘await’ 关键字
  • 你可以存储字符串、整数、映射甚至列表
  • 在支持 Flutter 的任何设备上工作!

注意

OkitoStorage 是可靠的,但在将其用作数据库时要小心,因为它不是为复杂工作设计的数据库。对于复杂的工作,你可以试试 Hive!

本地化

对于全球应用,找到一个新的本地化库或创建自己的可能会很困难,然后你可能会遇到其他问题,比如更改语言后更新整个应用等等。为什么要做这些呢?

Okito 提供本地化解决方案

// 创建你的翻译,非常简单!
const translations = {
  'en': {
    'hello': 'Hello from Okito!',
  },
  'tr': {
    'hello': "Okito'dan selamlar!",
  },
};
// 你可以有无限数量的区域和翻译
// 你可以使其动态,分离文件等等。只是一个 Dart 映射!
// 创建后,将其传递给应用。
OkitoMaterialApp /* 或 OkitoCupertinoApp */(
  translations: translations,
  /* 你的代码这里没有任何变化 */
);
// 使用它?很简单!让我们在一个文本小部件中使用它。
Text('hello'.loc); // 它将显示 'Hello from Okito!'

// 改变语言再看看。
Okito.app.setLocale(Locale('tr','TR'));
// 现在它说: 'Okito'dan selamlar!' 如在翻译中声明的一样。
// 你也可以这样设置:
Okito.localize('hello'); // 返回翻译为字符串。

更好的例子请查看 example/flutter_localization/lib/main.dart

扩展

// 上下文扩展
context.width;
context.height;
context.aspectRatio;
context.devicePixelRatio;
context.isLandscape;
context.isPortrait;
context.theme;
context.arguments;
context.routeName;

依赖注入

依赖注入是你的方法,将变量注入到 Okito 并可以在应用的任何地方使用它。使用 Okito,它是如此简单!

// 示例变量
class Counter {
  int count = 0;
}

// 注入它
Okito.inject(Counter());

// 在任何地方使用它!
Okito.use<Counter>();

// 用类型支持分配它!
final counter = Okito.use<Counter>();

// 无论你想要如何更新
counter.count++;
// 或
Okito.use<Counter>().count++;

那么,假设你的工作已经完成了,为什么要让它占用内存?

// Counter 将永远消失!
Okito.eject<Counter>();

更多细节,请查看测试或示例

使用键进行注入

当你要使用字符串键而不是类型存储多个相同类型的注入时,这很有用。

// 示例变量
class Counter {
  int count = 0;
}

// 注入它
Okito.injectWithKey('firstCounter', Counter());
Okito.injectWithKey('secondCounter', Counter());

// 使用类型支持在任何地方使用它!
final firstCounter = Okito.useWithKey<Counter>('firstCounter');
final secondCounter = Okito.useWithKey<Counter>('secondCounter');

// 无论你想要如何更新
firstCounter.count++;
secondCounter.count++;

那么,假设你的工作已经完成了,为什么要让它占用内存?

// 第二个计数器将永远消失!
Okito.ejectWithKey('secondCounter');

技巧

清洁小部件

// 在你的小部件文件夹或其他文件夹中声明构建器。
OkitoBuilder CounterBuilder({
  required Widget Function() builder,
}) =>
    OkitoBuilder(
      controller: counterController,
      builder: () => builder(),
    );

// 使用
CounterBuilder(builder: () => Text('${counterController.count}'));

我最喜欢的方式

OkitoBuilder CounterBuilder({
  required Widget Function(CounterController state) builder,
}) =>
    OkitoBuilder(
      controller: counterController,
      builder: () => builder(counterController),
    );

// 使用
CounterBuilder(builder: (state) => Text('${state.count}'));

更新状态

class CounterController extends OkitoController {
  int _count = 0;

  int get count => _count;

  set count(int count) {
    _count = count;
    // 现在,每当你改变 count 像 'count++',它将更新状态。
    update();
  }
}

应用控制器

它是应用的控制器,你可以包装你想要根据大更新更改的小部件,比如主题更改,如果数据不是来自控制器。

Rockito<AppController>(
  (app) => // 你的小部件
)

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

1 回复

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


Okito 是一个轻量级且易于使用的状态管理和依赖注入库,专为 Flutter 应用设计。它提供了简单而强大的工具来管理应用的状态和依赖关系,同时保持了代码的简洁性和可维护性。以下是如何使用 Okito 进行状态管理的基本指南。

1. 添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  okito: ^3.0.0

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

2. 创建一个状态管理类

你可以通过继承 OkitoStore 来创建一个状态管理类。这个类将包含你的应用状态和操作状态的方法。

import 'package:okito/okito.dart';

class CounterStore extends OkitoStore {
  int count = 0;

  void increment() {
    count++;
    notify();
  }

  void decrement() {
    count--;
    notify();
  }
}

3. 在应用中使用 Okito

你可以在应用的任何地方使用 Okito 来访问状态和管理依赖。

import 'package:flutter/material.dart';
import 'package:okito/okito.dart';
import 'counter_store.dart';

void main() {
  // 初始化 Okito
  Okito.runApp(MyApp());

  // 注册 CounterStore
  Okito.inject(CounterStore());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 获取 CounterStore 实例
    final counterStore = Okito.use<CounterStore>();

    return Scaffold(
      appBar: AppBar(title: Text('Okito Counter Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: ${counterStore.count}'),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => counterStore.increment(),
              child: Text('Increment'),
            ),
            ElevatedButton(
              onPressed: () => counterStore.decrement(),
              child: Text('Decrement'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 响应状态变化

通过在 OkitoStore 中调用 notify() 方法,你可以通知所有订阅者状态已经发生了变化。Okito.use<T>() 会自动订阅状态变化,并在状态更新时重新构建 UI。

5. 依赖注入

Okito 还提供了依赖注入功能。你可以在应用启动时注册依赖项,然后在任何需要使用它们的地方获取这些依赖项。

void main() {
  Okito.runApp(MyApp());

  // 注册依赖项
  Okito.inject(CounterStore());
  Okito.inject(SomeOtherService());
}
class SomeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final someService = Okito.use<SomeOtherService>();
    // 使用 someService
    return Container();
  }
}

6. 路由管理

Okito 还提供了简单的路由管理功能。你可以使用 Okito.push, Okito.pop, Okito.replace 等方法来管理导航。

Okito.push(MaterialPageRoute(builder: (context) => AnotherPage()));
回到顶部