Flutter应用上下文管理插件app_context的使用

Flutter应用上下文管理插件app_context的使用

Context Library

Context 是一个Dart库,它提供了一种灵活且轻量级的方法来在整个系统中传递上下文对象。通过利用匿名对象、函数式编程概念如部分应用以及键值存储结构,它可以实现组件之间的松耦合。这使得依赖注入(DI)和配置管理变得简单而无需高度耦合。

特性

  • 上下文传播:使用灵活的接口在系统中传递上下文对象。
  • 部分应用:动态地将上下文附加到方法和函数上,实现更干净、更功能化的设计。
  • 匿名对象:以不可变、轻量级的方式实现上下文,类似于Java中的匿名类。
  • 键值存储:使用高效的键值模型存储和检索配置数据或服务实例。
  • 灵活的DI:使用上下文解释和特性实现无紧耦合的依赖注入。
  • 飞重量构造器:在系统的隔离部分之间共享配置。

开始使用

安装

pubspec.yaml 文件中添加以下依赖:

dependencies:
  context: ^1.0.0

然后运行:

dart pub get
使用
定义一个上下文

Context 对象用于存储在不同系统组件之间传递的配置信息。上下文是不可变的,并通常在应用程序启动时注入。

import 'package:app_context/src/core/traits/trait.dart';

// 实现Context接口以传递配置数据的对象。
abstract interface class Context {
  // 将上下文解释为特定类型(表单)。
  //
  // [as] 指定所需的特征或接口。
  Form interpret<Form>({required final Trait<Form> as});
}
将上下文附加到对象

要将上下文传递给系统组件,可以使用 ContextNode 接口,该接口允许通过将上下文附加到基对象上来进行部分应用。

import 'package:app_context/src/core/ctx/context.dart';

// 用于使用部分应用传递上下文的基对象包装器。
abstract interface class ContextNode<Base> {
  // 将上下文附加到基对象。
  Base attach({required final Context ctx});
}
上下文的键值存储

库还提供了一个用于不可变键值存储的接口,用于存储实际的配置值。

// 用于上下文配置的键值存储接口。
abstract interface class KeyValueStorage<Key> {
  // 按键获取所需类型的值。
  Value fetch<Value>({required final Key as});
}
使用特征解释上下文

特征用于解释上下文。当组件需要特定的服务或对象时,特征会从上下文内的键值存储中检索所需的值。

// 用于传递给上下文解释方法的类型的包装器。
abstract interface class Trait<Form> {
  // 从上下文的存储中获取特征所需的值。
  //
  // [to] 是用于检索值的存储。
  Form refer({required final KeyValueStorage<Type> to});
}

许可证

该库根据MIT许可证授权。详情请参阅LICENSE文件。

示例代码

import 'dart:io';
import 'dart:async';

import 'package:app_context/app_context.dart';

// 尝试编写“启动日志服务器”

abstract interface class Server {
  Future<void> start({required final String address, required final int port});
}

abstract interface class Logger {
  Future<void> log({required final String message});
}

final class AnonymousServer implements Server {
  final Future<void> Function(String, int) _callback;

  const AnonymousServer({
    required final Future<void> Function(String, int) callback,
  }) : _callback = callback;

  [@override](/user/override)
  Future<void> start({
    required final String address,
    required final int port,
  }) async =>
      await _callback.call(address, port);
}

final class Cmd implements Logger {
  const Cmd();

  [@override](/user/override)
  Future<void> log({required final String message}) async => print(message);
}

final class StdHttpServer implements Server {
  const StdHttpServer();

  [@override](/user/override)
  Future<void> start({
    required final String address,
    required final int port,
  }) async {
    final origin = await HttpServer.bind(address, port);
    final subscription = origin.listen((_) {}, cancelOnError: false);
    await subscription.asFuture();
  }
}

final class LaunchLogServer implements ContextNode<Server> {
  final ContextNode<Server> _origin;
  final Trait<Logger> _trait;

  const LaunchLogServer({
    required final ContextNode<Server> origin,
    required final Trait<Logger> trait,
  })  : _origin = origin,
        _trait = trait;

  [@override](/user/override)
  Server attach({required final Context ctx}) => AnonymousServer(
        callback: (address, port) async {
          final logger = ctx.interpret(as: _trait);
          await logger.log(message: 'server started on $address:$port');
          final server = _origin.attach(ctx: ctx);
          await server.start(address: address, port: port);
        },
      );
}

final class App {
  final Server _server;

  static const _address = '127.0.0.1';
  static const _port = 8080;

  const App._internal({
    required Server server,
  }) : _server = server;

  factory App({required final Context ctx}) => App._internal(
        server: const LaunchLogServer(
          origin: CtxNodeAdapter(
            base: StdHttpServer(),
          ),
          trait: SimpleTrait<Logger>(),
        ).attach(ctx: ctx),
      );

  Future<void> run() async => await _server.start(
        address: _address,
        port: _port,
      );
}

final class AppContext implements Context {
  final Context _dependency;

  const AppContext._inner({
    required final Context dependency,
  }) : _dependency = dependency;

  factory AppContext() => const AppContext._inner(
        dependency: StdContext(
          state: InMemoryStorage(
            inner: {
              Logger: Cmd(),
            },
          ),
        ),
      );

  [@override](/user/override)
  Form interpret<Form>({required final Trait<Form> as}) =>
      _dependency.interpret(as: as);
}

Future<void> main(final List<String> arguments) async => await runZonedGuarded(
      () async {
        final app = App(ctx: AppContext());
        await app.run();
      },
      (error, stack) => print({error, stack}.join('\n')),
    )?.whenComplete(() => exit(0));

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

1 回复

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


在Flutter中,管理应用上下文(context)对于状态管理和跨组件通信至关重要。虽然Flutter本身提供了许多状态管理方案,如Provider、Riverpod、Bloc等,但如果你提到的app_context是一个自定义的插件或库用于上下文管理,这里我假设它是一个全局上下文管理器,并提供一个假设性的代码案例来展示如何使用它(请注意,实际app_context插件的API可能有所不同,以下代码仅为示例)。

假设的app_context插件使用示例

首先,我们需要假设app_context插件已经安装并导入到我们的项目中。如果这是一个假想的库,以下代码将展示如何设计和使用它。

1. 安装插件(假设步骤)

pubspec.yaml中添加依赖(注意:这一步是假设的,因为app_context可能不是真实存在的库):

dependencies:
  flutter:
    sdk: flutter
  app_context: ^1.0.0  # 假设版本号

然后运行flutter pub get

2. 设置全局上下文管理器

创建一个单例类来管理全局上下文。这里我们假设app_context提供了一个全局的上下文存储机制。

// app_context_manager.dart
import 'package:flutter/material.dart';
import 'package:app_context/app_context.dart'; // 假设的导入

class AppContextManager {
  static AppContextManager? _instance;
  AppContext? _context;

  AppContextManager._internal();

  factory AppContextManager() {
    if (_instance == null) {
      _instance = AppContextManager._internal();
    }
    return _instance!;
  }

  set context(AppContext? context) {
    _context = context;
  }

  AppContext? get context => _context;
}

3. 在应用启动时初始化上下文

main.dart中初始化全局上下文管理器。

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    AppContextManager().context = context; // 假设有一个方法将BuildContext转换为AppContext
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

注意:这里的AppContextManager().context = context;行是假设性的,因为实际的app_context库可能有不同的API来设置和获取上下文。

4. 在其他组件中使用全局上下文

假设我们有一个需要访问全局上下文的组件。

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

class SomeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    AppContext? appContext = AppContextManager().context;

    // 使用appContext做一些操作,这里只是示例
    return Text('App Context is set: ${appContext != null}');
  }
}

重要说明

  • 上面的代码是一个高度假设性的示例,因为app_context这个库在Flutter社区中可能并不存在,或者它的API与这里展示的完全不同。
  • 在实际开发中,使用全局状态管理通常推荐使用Flutter社区广泛接受的解决方案,如Provider、Riverpod或Bloc。
  • 直接管理全局BuildContext通常不是最佳实践,因为它可能导致代码难以维护和测试。考虑使用更结构化和响应式的状态管理方案。

如果你确实有一个特定的app_context插件或库,并希望获取更具体的帮助,请查阅该库的官方文档或提供其实际的API描述。

回到顶部