All Products
Search
Document Center

SuperApp:Integrate the WindVane miniapp container into a Flutter app

Last Updated:Feb 14, 2025

After the WindVane miniapp container is integrated into a Flutter app, you can use WindVane in the Flutter app to load a miniapp.

Procedure

If you want to integrate the WindVane miniapp container into a Flutter app, you must integrate the WindVane SDK into a native app, and use MethodChannel to call methods provided by the WindVane SDK in the native app to open a miniapp. Perform the following operations:

  1. Integrate the WindVane SDK into a native Android or iOS app.

  2. Create a MethodChannel instance in Flutter to enable the communication between Flutter and the native app, and then define the methods that you want to call.

  3. Implement the methods that are defined by MethodChannel in the Android or iOS app.

  4. Use the created MethodChannel instance in Flutter to call the methods defined in the native app.

Step 1: Integrate WindVane into a native app

For more information about how to integrate WindVane into a native app, see the following topics:

Step 2: Create a MethodChannel instance in Flutter

Create a MethodChannel instance in Flutter to enable the communication between Flutter and the native app. Sample code:

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

You can set MethodChannel to a channel name based on your business requirements. windvane_miniapp in the preceding code is provided for reference only.

Step 3: Implement MethodChannel in the native app

Android

  1. Inherit FlutterActivity.

  2. Implement the configureFlutterEngine method to create a MethodChannel instance.

    Important

    The channel name that you specify for MethodChannel in the native app must be the same as the channel name that you specified for MethodChannel in Flutter in Step 2: Create a MethodChannel instance in Flutter.

    In the following sample code, three methods are defined:

    • loadMiniApp: opens a miniapp.

    • initWindVaneMiniApp: initializes WindVane.

    • getMiniApps: queries miniapps.

    @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);
        }});
    }
    • You can use MethodCall.method to query the name of a method.

    • You can use MethodCall.argument("${key}") to query the parameter of a method.

    • You can check the result of a method by using MethodChannel.Result.success or MethodChannel.Result.error that is returned after the method is called.

iOS

  1. Inherit FlutterViewController.

  2. Create a MethodChannel instance.

    @interface EMASMainViewController : FlutterViewController
    
    @end
    
    @interface EMASMainViewController ()
    @property (nonatomic, strong) FlutterMethodChannel* messageChannel;
    @end
    
    @implementation EMASMainViewController
    - (void)viewDidLoad {
        [super viewDidLoad]; 
        [self configMessageChannel];
    }
    
    -(void) configMessageChannel{
        // Query the current 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

Step 4: Call MethodChannel

Use the created MethodChannel instance in Flutter to call the methods defined in the native app. Sample code:

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

invokeMethod returns a Future object.

iOS supports flutter projects to open WindVane miniapps in window mode

Solution Overview

  1. When the window passed in from outside is not set as the key window, calling the miniapp launch method will not take effect. Therefore, customer must provide a loading page as a transitional page before launching the miniapp (this loading page must be the rootViewController)

  2. When users click the close/back button or a miniapp triggers the close action via JSAPI, AliCloud container SDK will not automatically close the current miniapp page. Instead, it will first send a "close" callback message to customer (the callback method is registered during container initialization). The customer must then destroy the current window.

  3. When users attempt to close the mini-program via a swipe gesture(interactivePopGestureRecognizer), this action directly triggers the closure of the miniapp page, which would expose the previously provided loading page again and degrade user experience. To address this, AliCloud container SDK must disable the swipe-to-close functionality.

  4. For scenarios where Miniapp A rediret to another Miniapp B, the JSAPI for miniapp navigation (wv.navigateToMiniApp) must be implemented by customer. When closing a miniapp, The customer must determine whether to close the current miniapp or destroy the current window within the close callback method.

  5. When a miniapp opens another native pages (e.g., photo gallery or camera), AliCloud container SDK must present these pages using the window provided by the customer.

Note

  • The window passed in needs to be keyWindowAndVisiable, and the rootViewController has been set

  • The swipe gesture function will be disabled for WindVane type miniapps, and customer need to implement the closing logic and js api (miniapp to open miniapp)

  • Not applicable to uniapp containers

Step 1:Param window

class EMASMiniAppOpenConfiguration has propertieswindvaneWindowWhen calling the methodopenMiniApp:openConfiguration:completionBlock: to open the miniapp, pass in the parameter window

Step 2Re-implement the miniapp closing method

EMASMiniAppService adds a method closeMiniApp: , which needs to be registered when the container is initialized

[miniAppService closeMiniApp:^(NSString * _Nonnull appId) {
        //TODO:close the applet's processing logic
    }];

Close miniapp logic sample code:

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

Step 3Re-implement the miniapp to open miniapp JS API

Refer to Custom JSAPI and use the alias method to rewrite the original wv.navigateToMiniapp method of the container

Add a method of class WVMiniNavigatorApp callednavigateToMiniApp:(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"];
    //get the window to show windvane miniapp
    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) {
            //path
            config.path = path;
        }
        if (extraData){
            //extended Parameters
            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];
    }
    
}

Use the alias method to rewrite the miniapp to open miniapp method

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