在集成flutter_boost的情況下實(shí)現(xiàn)Flutter頁(yè)面內(nèi)嵌iOSView

在集成了flutter_boost后,實(shí)現(xiàn)了flutter頁(yè)面和iOS頁(yè)面之間的互相跳轉(zhuǎn)。如果我們又想在flutter頁(yè)面中內(nèi)嵌iOSView,我們需要怎么做?

1. Dart部分

  • 1.1. 新建native.dart,flutter混合原生view界面
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

/// flutter混合原生view
class CMNativePage extends StatelessWidget {
  const CMNativePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: IOSCompositionWidget()
      ),
    );
  }
}

class IOSCompositionWidget extends StatelessWidget {
  const IOSCompositionWidget({super.key});

  @override
  Widget build(BuildContext context) {
    // This is used in the platform side to register the view.
    const String viewType = 'custom_platform_view';
    // Pass parameters to the platform side.
    final Map<String, dynamic> creationParams = {'content': 'Flutter傳給IOSView的參數(shù)'};

    return UiKitView(
      viewType: viewType,
      creationParams: creationParams,
      creationParamsCodec: const StandardMessageCodec(),
    );
  }
}
  • 1.2. 在main.dart的路由表中,增加nativePage。供iOS原生進(jìn)行跳轉(zhuǎn)
'nativePage': (settings, uniqueId) {
      return PageRouteBuilder<dynamic>(
          settings: settings,
          pageBuilder: (_, __, ___) {
            return const CMNativePage();
          });
    },

2. iOS部分

  • 2.1. 新建FLNativeViewFLNativeViewFactory
    FLNativeView.h
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>

NS_ASSUME_NONNULL_BEGIN

@interface FLNativeViewFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end

@interface FLNativeView : NSObject <FlutterPlatformView>

@property (nonatomic, strong) UILabel *label;

- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;

@end

NS_ASSUME_NONNULL_END

FLNativeView.m

#import "FLNativeView.h"

@implementation FLNativeViewFactory {
  NSObject<FlutterBinaryMessenger> *_messenger;
}

- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
  self = [super init];
  if (self) {
    _messenger = messenger;
  }
  return self;
}

- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args {
  return [[FLNativeView alloc] initWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:_messenger];
}

-(NSObject<FlutterMessageCodec> *)createArgsCodec{
    return [FlutterStandardMessageCodec sharedInstance];
}

@end

@implementation FLNativeView

- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
  if (self = [super init]) {
      self.label = [UILabel new];
      self.label.textColor = [UIColor redColor];
      NSDictionary *dict = (NSDictionary *)args;
      NSString *textValue = dict[@"content"];
      self.label.text = [NSString stringWithFormat:@"我是iOS View,傳值:%@", textValue];
  }
  return self;
}

- (nonnull UIView *)view {
    return self.label;
}

@end
  • 2.2. 其中FLNativeViewFactory中的createArgsCodec方法一定不能遺漏,否則會(huì)導(dǎo)致傳值不成功。類型也一定要和Dart部分的native.dart->IOSCompositionWidget-> UiKitView-> creationParamsCodec保持一致。否則會(huì)導(dǎo)致崩潰:
  • 2.3. 修改AppDelegate.h:修改繼承為FlutterAppDelegate,并刪除window屬性,因?yàn)?code>FlutterAppDelegate中已經(jīng)自帶window屬性
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>

@interface AppDelegate : FlutterAppDelegate

@end
  • 2.4. 在AppDelegate.m中引入頭文件
#import "FLNativeView.h"
#import "GeneratedPluginRegistrant.h"
  • 2.5. 在AppDelegate.m中注冊(cè)插件,注意這里有非常大的坑。官方文檔中是這么寫(xiě)的:
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  NSObject<FlutterPluginRegistrar>* registrar = [self registrarForPlugin:@"plugin-name"];
  FLNativeViewFactory* factory = [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
  [[self registrarForPlugin:@"<plugin-name>"] registerViewFactory:factory withId:@"<platform-view-type>"];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

這樣寫(xiě)本身是沒(méi)問(wèn)題的,但是在引入flutter_boost的情況下,就不能這么寫(xiě)了!正確的做法是,需要等flutter_boost初始化完成后,用FlutterEngine對(duì)插件進(jìn)行初始化!代碼如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 初始化window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    ViewController *vc = [ViewController new];
    UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:vc];
    self.window.rootViewController = navigation;
    [self.window makeKeyAndVisible];
    self.window.backgroundColor = [UIColor whiteColor];

    // 初始化FlutterBoost
    BoostDelegate *delegate = [BoostDelegate sharedInstance];
    delegate.navigationController = (UINavigationController *)self.window.rootViewController;
    [[FlutterBoost instance] setup:application delegate:delegate callback:^(FlutterEngine *engine) {
        // 初始化Flutter內(nèi)嵌iOSView插件
        NSObject<FlutterPluginRegistrar> *registrar = [engine registrarForPlugin:@"custom_platform_view_plugin"];
        FLNativeViewFactory *factory = [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
        [registrar registerViewFactory:factory withId:@"custom_platform_view"];
    }];
    
    return YES;
}

如果按照官方相同的寫(xiě)法,跳轉(zhuǎn)過(guò)去會(huì)一直是一個(gè)空白頁(yè)面,不會(huì)有原生組件嵌入其中。這個(gè)問(wèn)題,我查了很久網(wǎng)上都沒(méi)有相關(guān)資料,希望能幫到后面遇到坑的人~

  • 其中withId:xxx,xxx代表控件的ID,需要和Dart部分的IOSCompositionWidget中的viewType保持一致。命名為:custom_platform_view
  • 其中registrarForPlugin:xxx,xxx代表插件的ID。命名為:custom_platform_view_plugin
  • 2.6. 在ViewController.m測(cè)試頁(yè)面中,增加跳轉(zhuǎn)Flutter頁(yè)面的按鈕和跳轉(zhuǎn)方法
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
  
    UIButton *pushFlutterNativePageButton = [UIButton buttonWithType:UIButtonTypeSystem];
    pushFlutterNativePageButton.frame = CGRectMake(100, 300, 300, 100);
    [pushFlutterNativePageButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [pushFlutterNativePageButton setTitle:@"跳轉(zhuǎn)到Flutter混合原生view界面" forState:UIControlStateNormal];
    [pushFlutterNativePageButton addTarget:self action:@selector(pushFlutterNativePage) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:pushFlutterNativePageButton];
    
}

// 跳轉(zhuǎn)Flutter混合原生view界面
- (void)pushFlutterNativePage{
    FlutterBoostRouteOptions *options = [FlutterBoostRouteOptions new];
    options.pageName = @"nativePage";
    options.arguments = @{@"animated": @(YES)};
    options.completion = ^(BOOL completion) {
    };
    [[FlutterBoost instance] open:options];
    options.onPageFinished = ^(NSDictionary *dic) {
        NSLog(@"%@", dic);
    };
}
  • 2.7. 最后重新執(zhí)行pod install,重新將FlutterModule導(dǎo)入到項(xiàng)目中。運(yùn)行iOS項(xiàng)目,跳轉(zhuǎn)后得到正確結(jié)果:

3. 參考文檔

附:Demo下載

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容