全部产品
Search
文档中心

SuperApp:Flutter App接入WindVane小程序容器

更新时间:Feb 14, 2025

Flutter App接入WindVane小程序容器后,则可以在Flutter App中使用WindVane小程序容器加载小程序。

接入流程

Flutter App接入WindVane小程序容器,首先需要通过Native端集成小程序容器,再通过MethodChannel调用Native端小程序容器SDK提供的方法打开小程序,详细流程如下:

  1. Android端和iOS端集成小程序容器SDK。

  2. 定义Flutter和Native通信的MethodChannel,以及需要调用的方法。

  3. 在Android端或iOS端,分别实现MethodChannel已经定义好的方法。

  4. 在Flutter侧通过定义好的MethodChannel调用Native方法。

步骤一、Native端集成小程序容器

集成小程序容器的详细操作请参见:

步骤二、定义MethodChannel

在Flutter中定义和Native侧通信的MethodChannel, 示例代码如下:

class WindVaneMiniAppManager {
 static const MethodChannel channel = const MethodChannel("windvane_miniapp");
}
说明

MethodChannel的名称可以自定义,上述代码中的windvane_miniapp仅为示例。

步骤三、在Native中实现MethodChannel

Android

  1. 继承FlutterActivity

  2. 实现configureFlutterEngine方法,创建MethodChannel

    重要

    MethodChannel的名称须和步骤二、定义MethodChannel中Flutter定义的MethodChannel名称保持一致。

    示例代码如下,其中定义了三个方法:

    • loadMiniApp:打开小程序。

    • initWindVaneMiniApp:初始化小程序容器。

    • getMiniApps:查询小程序列表。

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
      super.configureFlutterEngine(flutterEngine);
      GeneratedPluginRegistrant.registerWith(flutterEngine);
      MethodChannel channel = new MethodChannel(flutterEngine.getDartExecutor(),
    			WINDVANE_MINIAPP);
      channel.setMethodCallHandler((methodCall, result) -> {
        String method = methodCall.method;
        if ("initMiniApp".equals(method)) {
          initMiniApp(result);
        } else if ("getMiniAppList".equals(method)) {
          getMiniAppList(result);
        } else if ("openMiniapp".equals(method)) {
          openMiniApp(methodCall, result);
        }});
    }
    • 方法名通过MethodCall.method获取。

    • 方法的参数通过MethodCall.argument("${key}")获取

    • 方法执行后的结果通过MethodChannel.Result.successMethodChannel.Result.error返回。

iOS

  1. 继承FlutterViewController

  2. 创建MethodChannel

    @interface EMASMainViewController : FlutterViewController
    
    @end
    
    @interface EMASMainViewController ()
    @property (nonatomic, strong) FlutterMethodChannel* messageChannel;
    @end
    
    @implementation EMASMainViewController
    - (void)viewDidLoad {
        [super viewDidLoad]; 
        [self configMessageChannel];
    }
    
    -(void) configMessageChannel{
        //获取当前的 controller
    //    FlutterViewController* controller = (FlutterViewController*)[UIApplication sharedApplication].delegate.window.rootViewController;
    
        
        self.messageChannel = [FlutterMethodChannel methodChannelWithName:@"windvane_miniapp" binaryMessenger:self.binaryMessenger];
        
        __weak __typeof__(self) weakSelf = self;
        [self.messageChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            NSLog(@"call method is %@",call.method);
            
            __strong typeof(weakSelf) strongSelf = weakSelf;
            NSString *method = call.method;
            
            if ([method isEqualToString:@"initMiniApp"]) {
                [strongSelf initWindVaneMiniApp:call result:result];
            } else if ([method isEqualToString:@"openMiniapp"]) {
                [strongSelf loadMiniApp:call result:result];
            } else if ([method isEqualToString:@"getMiniAppList"]) {
                [strongSelf getMiniApps:call result:result];
            }
            
        }];
        
    }
    @end

步骤四、调用MethodChannel

通过定义好的MethodChannel调用相应方法,示例代码如下:

WindVaneMiniAppManager.channel.invokeMethod(
 "openMiniapp", {'appId': appId}).then((value) => {});
重要

invokeMethod返回的是Future对象。

iOS 支持flutter工程以window方式打开WindVane小程序

方案概述

  1. 当外部传入的window并没有设置为主window时,调用打开小程序方法不起作用,所以需要客户提供loading页作为打开小程序前的过渡页(此过渡页须为rootViewController)

  2. 当用户点击关闭按钮/返回按钮或小程序调用jsapi关闭当前小程序时,容器sdk不会自动关闭当前小程序页面而是先发送“关闭”回调消息给客户(回调方法在容器初始化时注册),然后需要客户销毁当前window

  3. 当用户侧滑关闭当前小程序时,因为会直接触发小程序页面关闭,导致此前客户提供的loading页面被再次露出影响用户体验,所以需容器sdk禁用侧滑功能

  4. 当出现小程序A跳转启动另一个小程序B时,小程序跳转的jsapi(wv.navigateToMiniApp)需客户自行实现。当关闭小程序时,客户需在关闭回调方法中自行判断应关闭当前小程序或销毁当前window

  5. 当出现小程序打开相册/拍照等其他页面时,容器sdk需根据用户传入的window来present当前页面

注意事项

  • 传入的 window 需要为 keyWindowAndVisiable, 并且已设置 rootViewController

  • WindVane类型小程序会禁用侧滑功能,需要客户自己实现关闭逻辑和js api 小程序打开小程序功能

  • 对uniapp容器不适用

第一步:传入window

EMASMiniAppOpenConfiguration类新增属性windvaneWindow,在调用打开小程序方法openMiniApp:openConfiguration:completionBlock:方法时传入window

第二步:重新实现小程序关闭方法

EMASMiniAppService新增方法closeMiniApp:,该方法需要在容器初始化时注册

[miniAppService closeMiniApp:^(NSString * _Nonnull appId) {
        //TODO:关闭小程序的处理逻辑
    }];

关闭小程序逻辑示例代码:

if ([currentVC.navigationController respondsToSelector:@selector(popViewControllerAnimated:)]) {
    if (currentVC.navigationController.viewControllers.count > 1) {
        [currentVC.navigationController popViewControllerAnimated:YES];
    } else {
        [currentVC dismissViewControllerAnimated:YES completion:nil];
        //TODO:销毁window
    }
            
}

第三步:重新实现小程序跳转小程序 JS API

参考自定义JSAPI并使用别名方式重写容器原有的wv.navigateToMiniapp 方法

示例:

新增一个类WVMiniNavigatorApp的方法navigateToMiniApp:(NSDictionary *)params withBridgeContext:(id<WMBridgeCallbackContext>)context

@implementation WVMiniNavigatorApp

- (void)navigateToMiniApp:(NSDictionary *)params withBridgeContext:(id<WMBridgeCallbackContext>)context {
    
    NSString *appId = [params wvStringValue:@"appId"];
    NSString *path = [params wvStringValue:@"path"];
    NSDictionary *extraData = [params wvDictionaryValue:@"extraData"];
    //获取需要展示的window
    AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    
    id<EMASMiniAppService> miniAppService = [[EMASServiceManager sharedInstance] serviceForProtocol:@"EMASMiniAppService"];
    
    if (miniAppService) {
        EMASMiniAppOpenConfiguration *config = [[EMASMiniAppOpenConfiguration alloc] init];
        config.windvaneWindow = delegate.windVaneWindow;
        if (path.length > 0) {
            //deeplink 路径
            config.path = path;
        }
        if (extraData){
            //扩展参数
            config.extraData = extraData;
        }
        
        [miniAppService openMiniApp:appId openConfiguration:config completionBlock:^(int resultCode, NSDictionary * _Nonnull resultDict) {
            NSLog(@"resultCode = %d, resultDict = %@",resultCode,resultDict);
            if (resultCode == 200) {
                //打开小程序成功,自动跳转到小程序界面
                // 返回结果
                [context callbackSuccess:@{@"msg": @"success"}];
            }else{
                [context callbackSuccess:@{@"resultCode": @(resultCode),@"msg": resultDict}];
            }
        }];
    }else{
        NSDictionary *errorResult = @{
            @"detailErrorCode": @"12",
            @"detailErrorMsg": @"EMASMiniAppService is nil"
        };

        [context callbackFailure:WMBridgeStatusFailed withResult:errorResult];
    }
    
}

使用别名方式重写小程序跳转小程序方法

NSDictionary *dic = @{
        @"WVMiniApp.navigateToMiniApp":@"WVMiniNavigatorApp.navigateToMiniApp"
 };
WMBridgeRegisterAlias(dic);