Flutter核心功能扩展插件dart_board_core的使用

Flutter核心功能扩展插件dart_board_core的使用

dart_board_coredart_board 的核心模块。它是 dart_board 的心脏,提供了主要的组件和导航接口。如果你将 dart_board 比喻为家庭影院系统,那么 dart_board_core 就像是你的接收器。你将各种功能(如有线电视、VCR、DVD、任天堂游戏机)插入到接收器中,而接收器再连接到电视和扬声器上。

DartBoardCore

DartBoardCore 接口让你能够访问有关系统的各种信息,例如已安装的功能、可用的输入和准备查看的通道。

通常情况下,如果你不进行AB测试或特性标志管理,你只需要使用 DartBoard 组件即可。

void main() => runApp(DartBoard(
      features: [
        SpaceXUIFeature(),
        SpaceSceneFeature(),
        ThemeFeature(data: ThemeData.dark()),
        EntryPoint()
      ],
      initialPath: '/entry_point',
    ));

这段代码启动了 DartBoard,加载了你的功能,并进入初始路径 /entry_point。对于标准用法,到这里就完成了。

AB测试与特性标志

AB切换实现

你可以通过以下方式在运行时切换不同的实现:

DartBoardCore
  .instance
  .setFeatureImplementation(
      'YourFeatureNamespace', 'YourImplementationName')

特性标志/禁用

你可以通过以下方式禁用某个特性:

DartBoardCore
  .instance
  .setFeatureImplementation(
      'YourFeatureNamespace', null)

这与AB切换相似,但传递 null 作为 ImplementationName 会禁用该特性。

导航

你可以通过 DartBoardCore.nav 全局访问导航功能。它维护了一个路径栈。

你可以推送新的路径,并选择是否展开它们(例如,[/a/b/c] -> [/a, /a/b, /a/b/c])。它提供了几种编辑栈的方法,以便根据需要修改栈。

abstract class DartBoardNav {
  /// 当前活动(前台)路由
  String get currentPath;

  /// 监听导航更改的通知器
  ChangeNotifier get changeNotifier;

  /// 获取当前栈
  List<DartBoardPath> get stack;

  /// 将一个路由推入栈中
  /// expanded 参数决定是否展开子路径(例如 /a/b/c 会推入 [/a, /a/b, /a/b/c])
  void push(String path, {bool expanded});

  /// 弹出最顶层的路由
  void pop();

  /// 弹出路由直到满足给定条件
  void popUntil(bool Function(DartBoardPath path) predicate);

  /// 清除所有匹配给定条件的路由
  void clearWhere(bool Function(DartBoardPath path) predicate);

  /// 替换栈顶路由
  /// 不适用于 '/'
  void replaceTop(String path);

  /// 在当前路由后附加路径(例如 /a 后追加 /b 得到 /a/b)
  void appendPath(String path);

  /// 替换根路由(入口点)
  /// 通常用于Add2App
  void replaceRoot(String path);

  /// 推送带有动态路由名称的路由
  void pushDynamic(
      {required String dynamicPathName, required WidgetBuilder builder});
}

路由类型

这些路由类型允许你匹配各种URI模式。

  • NamedRouteDefinition - 匹配特定名称的部分路径,例如 /page/details
  • MapRoute - 支持多个页面的命名路由(语法糖)
  • UriRoute - 匹配所有到达它的路径。它可以全局处理路由,也可以与 PathedRoute 结合使用以提供详细的资源解析。
  • PathedRoute - 用于深度链接树。例如 /category/details/50 它接受一组路由定义列表。每个深度级别代表一棵树。

如何处理复杂的路由?

NamedRouteDefinition 对于静态固定目标很好用。但如果需要更高级的功能怎么办?

例如,你想让 /store/pots/2141 解析。

UriRoutePathedRoute 可以解决这些问题。

PathedRoute 可以处理目录结构。你可以使用多层列表来实现。每一层可以包含任意数量的匹配器。如果路径匹配到某一层,最低级别的匹配器就会接管。

// 假代码
[
  [
    NamedRoute('/store', (ctx, settings) => StorePage()),    
  ],
  [
    NamedRoute('/pots', (ctx, settings) => PotsPage()),
    NamedRoute('/pans'  (ctx, settings) => PotsPage()),
  ],
  [
    UriRoute((context, uri) => Parse and Display)
  ]
]

这个 PathedRoute 配置可以响应多种路由:[/store, /store/pots, /store/pans, /store/pots/*, /store/pans/*]

*UriRoute。你可以使用它来管理所有路由,或者将其与 PathedRoute 结合使用以解析信息。

UriRoute 会解析资源请求并让你访问查询参数、路径段和其他编码在页面请求中的内容。

匿名路由

有时你可能只想推送一个屏幕,而不想在特性中注册它。你可以使用以下方法:

void pushDynamic({required String dynamicRouteName, required WidgetBuilder builder});

你可以给它一个唯一的名称,例如 /YourDynamicRoute3285。如果看到 _,这意味着你不能共享这个路由。如果你把它给别人,他们会收到404错误。它是动态分配给用户会话的。

路由演示:SpaceX示例

SpaceX 特性被设计为演示 Add2AppNavigator 2.0 的用法。

[@override](/user/override)
List<RouteDefinition> get routes => [
      PathedRouteDefinition([
        [
          NamedRouteDefinition(
              route: '/launches', builder: (ctx, settings) => LaunchScreen())
        ],
        [UriRoute((ctx, uri) => LaunchDataUriShim(uri: uri))]
      ]),
    ];

这将匹配 /launches/launches/[ANY_ROUTE_NAME]

/launches 会将任务名称附加到URL,例如 /launches/Starlink%207

UriRoute 然后可以从URI中提取数据,并将其传递给页面以加载所需的数据。

有用的Widget(通用工具)

RouteWidget(嵌入式路由)

想要在任何地方使用命名路由吗?例如在一个对话框中,或者作为屏幕的一部分?

showDialog(
    useSafeArea: true,
    context: navigatorContext,
    barrierDismissible: true,
    builder: (ctx) => RouteWidget("/request_login"));

并且传递参数 RouteWidget(itemPreviewRoute, args: {"id": id})

RouteWidget 可以帮助你实现这一点,使你能够将屏幕分解成多个解耦的功能,同时共享一个核心和状态。

Convertor<In, Out>

在小部件树中进行转换

Convertor<MinesweeperState, MineFieldViewModel>(
    convertor: (input) => buildVm(input),
    builder: (ctx, out) => MineField(vm: out),
    input: state)

只有当VM改变时才会触发更新。

这对于从数据源生成视图模型非常有用,有助于减少由于无关变化而导致的小部件重建次数。

LifecycleWidget

LifeCycleWidget(
    key: ValueKey("LocatorDecoration_${T.toString()}"),
    preInit: () => doSomethingBeforeCtx,
    child: Builder(builder: (ctx) => child))

这个小部件可以接入生命周期的三个钩子:

/// 在 initState() 中调用,但在 super.initState() 之前
final Function() preInit;

/// 在 initState() 之后调用(带上下文)
final Function(BuildContext context) init;

/// 在 dispose() 中调用
final Function(BuildContext context) dispose;

你可以使用这个小部件来启动屏幕计时器,或者定期设置提醒/开始/停止服务等。

它在功能设置和集成方面非常有用。

示例代码

以下是一个简单的示例代码,展示了如何使用 dart_board_core 插件。

import 'package:dart_board_core/dart_board_core.dart';
import 'package:dart_board_core/interface/nav_interface.dart';
import 'package:flutter/material.dart';

/// 最小化的 Dart Board 示例。
///
/// 提供一个简单的页面和一个应用于所有页面的基本框架,
/// 并支持在两个路由之间进行基本导航。
///
/// 对于高级用法,例如应用装饰、多个功能、AB测试等,
/// 请查看项目根目录下的示例项目或访问 https://dart-board.io
void main() => runApp(DartBoard(
      initialPath: '/home',
      features: [ScaffoldFeature(), SimpleRouteFeature()],
    ));

class ScaffoldFeature extends DartBoardFeature {
  [@override](/user/override)
  String get namespace => 'scaffold';

  [@override](/user/override)
  List<DartBoardDecoration> get pageDecorations => [
        DartBoardDecoration(
            name: 'scaffold_widget',
            decoration: (ctx, child) => Scaffold(
                  appBar: AppBar(title: Text('example')),
                  body: Container(
                      color: Colors.blue,
                      width: double.infinity,
                      height: double.infinity,
                      child: FittedBox(fit: BoxFit.contain, child: child)),
                ))
      ];
}

class SimpleRouteFeature extends DartBoardFeature {
  [@override](/user/override)
  String get namespace => 'main_page';

  [@override](/user/override)
  List<RouteDefinition> get routes => [
        MapRouteDefinition(routeMap: {
          '/home': (ctx, settings) => Card(
                  child: Column(
                children: [
                  Text('Home Page'),
                  MaterialButton(
                    onPressed: () => DartBoardCore.nav.push("/second"),
                    child: Text('push another route'),
                  ),
                ],
              ))
        }),
        NamedRouteDefinition(
            route: '/second',
            builder: (ctx, settings) => Card(
                    child: Column(
                  children: [
                    Text('Second Page'),
                    MaterialButton(
                      onPressed: DartBoardCore.nav.pop,
                      child: Text('pop'),
                    ),
                  ],
                ))),
      ];
}

更多关于Flutter核心功能扩展插件dart_board_core的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter核心功能扩展插件dart_board_core的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


dart_board_core 是一个用于扩展 Flutter 应用核心功能的插件。它提供了一种模块化的方式来组织和管理你的 Flutter 应用,使得代码更加结构化、可维护性更高。以下是如何使用 dart_board_core 的一些基本步骤和核心功能。

1. 安装依赖

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

dependencies:
  flutter:
    sdk: flutter
  dart_board_core: ^1.0.0  # 请使用最新版本

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

2. 基本使用

dart_board_core 的核心思想是通过 Feature 来扩展应用的功能。每个 Feature 可以包含路由、状态管理、依赖注入等。

创建一个简单的 Feature

import 'package:dart_board_core/dart_board_core.dart';

class MyFeature extends Feature {
  [@override](/user/override)
  List<RouteDefinition> get routes => [
        RouteDefinition(
          path: '/my-feature',
          builder: (context) => MyFeaturePage(),
        ),
      ];
}

class MyFeaturePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My Feature'),
      ),
      body: Center(
        child: Text('Welcome to My Feature!'),
      ),
    );
  }
}

注册 Feature

main.dart 中,将 MyFeature 注册到 DartBoard 中:

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

void main() {
  runApp(DartBoard(
    features: [
      MyFeature(),
    ],
    home: '/my-feature',
  ));
}

3. 路由管理

dart_board_core 提供了一种简单的方式来管理路由。你可以通过 RouteDefinition 来定义路由,并在 Feature 中注册它们。

class MyFeature extends Feature {
  [@override](/user/override)
  List<RouteDefinition> get routes => [
        RouteDefinition(
          path: '/my-feature',
          builder: (context) => MyFeaturePage(),
        ),
        RouteDefinition(
          path: '/my-feature/details',
          builder: (context) => MyFeatureDetailsPage(),
        ),
      ];
}

4. 状态管理

dart_board_core 支持与不同的状态管理库(如 ProviderRiverpod 等)集成。你可以在 Feature 中注册状态管理的提供者。

class MyFeature extends Feature {
  [@override](/user/override)
  List<Dependency> get dependencies => [
        Dependency(
          (context) => MyFeatureState(),
        ),
      ];
}

class MyFeatureState extends ChangeNotifier {
  // 状态管理逻辑
}

5. 依赖注入

dart_board_core 提供了一种简单的方式来管理依赖注入。你可以通过 Dependency 来注册依赖,并在整个应用中访问它们。

class MyFeature extends Feature {
  [@override](/user/override)
  List<Dependency> get dependencies => [
        Dependency(
          (context) => MyService(),
        ),
      ];
}

class MyService {
  void doSomething() {
    print('Doing something!');
  }
}

6. 其他功能

dart_board_core 还支持其他一些功能,如:

  • Middleware:可以在路由导航前后执行一些逻辑。
  • Theming:可以全局管理应用的主题。
  • Localization:支持多语言管理。

7. 示例代码

以下是一个完整的示例代码:

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

void main() {
  runApp(DartBoard(
    features: [
      MyFeature(),
    ],
    home: '/my-feature',
  ));
}

class MyFeature extends Feature {
  [@override](/user/override)
  List<RouteDefinition> get routes => [
        RouteDefinition(
          path: '/my-feature',
          builder: (context) => MyFeaturePage(),
        ),
        RouteDefinition(
          path: '/my-feature/details',
          builder: (context) => MyFeatureDetailsPage(),
        ),
      ];

  [@override](/user/override)
  List<Dependency> get dependencies => [
        Dependency(
          (context) => MyService(),
        ),
      ];
}

class MyFeaturePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final myService = context.read<MyService>();
    return Scaffold(
      appBar: AppBar(
        title: Text('My Feature'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Welcome to My Feature!'),
            ElevatedButton(
              onPressed: () {
                myService.doSomething();
                Navigator.pushNamed(context, '/my-feature/details');
              },
              child: Text('Go to Details'),
            ),
          ],
        ),
      ),
    );
  }
}

class MyFeatureDetailsPage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Details'),
      ),
      body: Center(
        child: Text('Details Page'),
      ),
    );
  }
}

class MyService {
  void doSomething() {
    print('Doing something!');
  }
}
回到顶部