一个用于加速 Flutter 应用程序中微前端(或独立功能)结构创建的包(测试版)

Screen Shot 2022-02-03 at 00 32 35

页面间的导航

使用 [NavigatorInstance] 在页面之间进行导航

NavigatorInstance.pop();
NavigatorInstance.pushNamed();
NavigatorInstance.pushNamedNative();
NavigatorInstance.pushReplacementNamed();
NavigatorInstance ...

以此方式打开原生(Android/iOS)页面

这需要原生实现,你可以在 android 文件夹中找到一个示例

// If not implemented, always return null
final isValidEmail = await NavigatorInstance.pushNamedNative<bool>(
    'emailValidator',
    arguments: 'validateEmail:[email protected]'
);
print('Email is valid: $isValidEmail');

// Listen to all flutter navigation events
NavigatorInstance.eventController.flutterLoggerStream.listen((event) {
    logger.d('[flutter: navigation_log] -> $event');
});

// Listen to all native (Android/iOS) navigation events (if implemented)
NavigatorInstance.eventController.nativeLoggerStream.listen((event) {});

// Listen to all native (Android/iOS) navigation requests (if implemented)
NavigatorInstance.eventController.nativeCommandStream.listen((event) {});

定义微应用配置和契约

配置偏好设置(可选)

  MicroAppPreferences.update(
    MicroAppConfig(
      nativeEventsEnabled: true, // If you want to dispatch and listen to events between native(Android/iOS) [default = false]
      pathSeparator: MicroAppPathSeparator.slash // It joins the routes segments using slash "/" automatically
    )
  );

注册所有路由

这只是一个路由策略的建议(可选)
重要的是所有路由都必须在项目之外可用,避免微应用之间的依赖。
在一个新包中创建所有路由,并将其作为依赖项导入到任何项目中。这将使得从任何地方以简单透明的方式打开路由成为可能。

创建路由包:flutter create template=package micro_routes

// Export all routes
class Application1Routes extends MicroAppRoutes {
  @override
  MicroAppBaseRoute get baseRoute => MicroAppBaseRoute('application1');
  String get page1 => baseRoute.path('page1');
  String get page2 => baseRoute.path('page2', ['segment1', 'segment2']);
}

例如,你可以通过这种方式打开一个位于其他 MicroApp 中的页面

NavigatorInstance.pushNamed(OtherMicroAppRoutes().specificPage);
NavigatorInstance.pushNamed(Application1Routes().page1);

通过契约 `MicroApp` 暴露所有页面(在外部项目或功能文件夹中)

import 'package:micro_routes/exports.dart';

class Application1MicroApp extends MicroApp with Application1Routes {

  @override
  List<MicroAppPage> get pages => [
        MicroAppPage(name: baseRoute.name, builder: (context, arguments) => const Initial()),
        MicroAppPage(name: page1, builder: (context, arguments) => const Page1()),
        MicroAppPage(name: page2, builder: (context, arguments) {
            final page2Params.fromMap(arguments);
            return Page2(params: page2Params);
        }),
      ];
}

初始化宿主,注册所有微应用

  • MicroHost 本身也是一个 MicroApp,因此你也可以在这里注册页面。
  • MyApp 需要继承 MicroHostStatelessWidget 或 MicroHostStatefulWidget
  • MicroHost 是根组件,它拥有所有 MicroApps,而 MicroApps 拥有所有 Micro Pages。

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

class MyApp extends MicroHostStatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      navigatorKey: NavigatorInstance.navigatorKey, // Required
      onGenerateRoute: onGenerateRoute, // [onGenerateRoute] this is created automatically, so just use it, or override it, if needed.
      initialRoute: baseRoute.name,
      navigatorObservers: [
        NavigatorInstance // Add NavigatorInstance here, if you want to get didPop, didReplace and didPush events
      ],
    );
  }

  // Base route of host application
  @override
  MicroAppBaseRoute get baseRoute => MicroAppBaseRoute('/');

  // Register all root [MicroAppPage]s here
  @override
  List<MicroAppPage> get pages => [
        MicroAppPage(name: baseRoute.name, builder: (_, __) => const HostHomePage())
      ];

  // Register all [MicroApp]s here
  @override
  List<MicroApp> get microApps => [MicroApplication1(), MicroApplication2()];
}

处理微应用事件

分发事件

// dispatching in channel "user_auth", only
MicroAppEventController()
    .emit(const MicroAppEvent(
        name: 'my_event',
        payload: {'data': 'lorem ipsum'},
        channels: ['user_auth'])
    );

监听事件(MicroApp)

// It listen to all events
@override
  MicroAppEventHandler? get microAppEventHandler =>
      MicroAppEventHandler((event) => logger.d([ event.name, event.payload]));

// It listen to events with id equals 123, only!
@override
  MicroAppEventHandler? get microAppEventHandler =>
      MicroAppEventHandler((event) {
        logger.d([ event.name, event.payload]);
      }, id: '123');

// It listen to events with channels "chatbot" and "user_auth"
@override
  MicroAppEventHandler? get microAppEventHandler =>
      MicroAppEventHandler((event) {
        // User auth feature, asked to show a popup :)
        myController.showDialog(event.payload);
      }, channels: ['chatbot', 'user_auth']);

管理事件

MicroAppEventController().unregisterHandler(id: '123');
MicroAppEventController().unregisterHandler(channels: ['user_auth']);
MicroAppEventController().pauseAllHandlers();
MicroAppEventController().resumeAllHandlers();
MicroAppEventController().unregisterAllHandlers();

在应用程序中的任何地方(例如,在 StatefulWidget 中)初始化事件订阅

使用订阅

final subscription = MicroAppEventController().stream.listen((MicroAppEvent event) {
    logger.d(event);
  });

// later, in dispose method of the widget
@override
void dispose() {
    subscription.cancel();
    super.dispose();
}

使用处理程序

MicroAppEventController().registerHandler(MicroAppEventHandler(id: '1234'));

// later, in dispose method of the widget
@override
void dispose() {
    MicroAppEventController().unregisterSubscription(id: '1234');
    super.dispose();
}

重写 onGenerateRoute 方法

如果获取页面路由失败,则请求原生(Android/iOS)打开该页面

  @override
  Route? onGenerateRoute(RouteSettings settings, {bool? routeNativeOnError}) {
    //! If you wish native app receive requests to open routes, IN CASE there
    //! is no route registered in Flutter, please set [routeNativeOnError: true]
    return super.onGenerateRoute(settings, routeNativeOnError: true);
  }

如果获取页面路由失败,则显示默认错误页面

  @override
  Route? onGenerateRoute(RouteSettings settings, {bool? routeNativeOnError}) {
    
    final pageRoute = super.onGenerateRoute(settings, routeNativeOnError: false);

    if (pageRoute == null) {
       // If pageRoute is null, this route wasn't registered(unavailable)
       return MaterialPageRoute(
           builder: (_) => Scaffold(
                 appBar: AppBar(),
                 body: const Center(
                   child: Text('Page Not Found'),
                 ),
            ));
    }
    return pageRoute;
  }
     

下表显示了 Dart 值如何在平台端接收以及反之亦然

Dart Kotlin Swift Java
nil
布尔值 Boolean(布尔值) NSNumber(value: Bool) java.lang.Boolean
整数 Int(整数) NSNumber(value: Int32) java.lang.Integer
int,如果 32 位不够 Long NSNumber(value: Int) java.lang.Long
双精度 Double(双精度浮点数) NSNumber(value: Double) java.lang.Double
字符串 字符串 字符串 java.lang.String
Uint8List ByteArray FlutterStandardTypedData(bytes: Data) byte[]
Int32List IntArray FlutterStandardTypedData(int32: Data) int[]
Int64List LongArray FlutterStandardTypedData(int64: Data) long[]
Float32List FloatArray FlutterStandardTypedData(float32: Data) float[]
Float64List DoubleArray FlutterStandardTypedData(float64: Data) double[]
列表 列表 Array java.util.ArrayList
地图 HashMap 词典 java.util.HashMap

GitHub

查看 Github