Flutter学习为了更好的活着flutter

iOS原生和Flutter交互

2019-03-12  本文已影响881人  loongod

先来个图

flutter_iOS.gif

1. Flutter调原生方法并返回结果给Flutter

先添加一个交互事件

            RaisedButton(
              onPressed: invokeNativeGetResult,
              child: Text('Invoke Native function get callback result'),
            ),

flutter调用方法

  static const platform = const MethodChannel('qd.flutter.io/qd_present'); // 交互通道
  String nativeBackString = 'Not Return';

  Future<void> invokeNativeGetResult() async {
    String backString;
    try {
      // 调用原生方法并传参,以及等待原生返回结果数据
      var result = await platform.invokeMethod(
          'getNativeResult', {"key1": "value1", "key2": "value2"});
      backString = 'Native return $result';
    } on PlatformException catch (e) {
      backString = "Failed to get native return: '${e.message}'.";
    }

    setState(() {
      nativeBackString = backString;
    });
  }

iOS接收事件

    self.flutterPresentVC = [[FlutterViewController alloc] init];
    [self.flutterPresentVC setInitialRoute:@"presentPage"];
    [self presentViewController:self.flutterPresentVC animated:false completion:nil];
    
    FlutterMethodChannel *presentChannel = [FlutterMethodChannel methodChannelWithName:@"qd.flutter.io/qd_present" binaryMessenger:self.flutterPresentVC];
    __weak typeof(self) weakSelf = self;
    __weak typeof(presentChannel) wsChannel = presentChannel;

    // 注册方法等待flutter页面调用
    [presentChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        NSLog(@"%@", call.method);
        NSLog(@"%@", result);
        
        if ([call.method isEqualToString:@"getNativeResult"]) {
            NSString *name = [weakSelf getDeiveName];
            if (name == nil) {
                FlutterError *error = [FlutterError errorWithCode:@"UNAVAILABLE" message:@"Device info unavailable" details:nil];
                result(error);
            } else {
                // 通过result返回给Flutter回调结果
                result(name);
            }
            // 下面是原生又调用了Flutter方法
            // 原生调用Flutter方法,带参数, 接收回传结果并弹窗
            [wsChannel invokeMethod:@"flutterMedia" arguments:@{@"key1": @"value1"} result:^(id  _Nullable result) {
                NSLog(@"%@", result);
                [weakSelf showAlert:result];
            }];
        } else if ([call.method isEqualToString:@"dismiss"]) {
            [weakSelf.flutterPresentVC dismissViewControllerAnimated:YES completion:nil];
        } else if ([call.method isEqualToString:@"presentNativeSecondPage"]) {
            NativeSecondVC *secondVC = [[NativeSecondVC alloc] init];
            [self.flutterPresentVC presentViewController:secondVC animated:YES completion:nil];
        }
        else {
            result(FlutterMethodNotImplemented);
        }
    }];
- (NSString *)getDeiveName {
    UIDevice *device = UIDevice.currentDevice;
    return device.name;
}

2. 原生调用Flutter方法并返回结果给原生

我新建了一个VC继承FlutterViewController

NativeFlutterVC.h

#import <Flutter/Flutter.h>

@interface NativeFlutterVC : FlutterViewController

- (void)gotoBack;

@end

NativeFlutterVC.m

#import "NativeFlutterVC.h"
#import <Flutter/Flutter.h>
#import "NativeSecondVC.h"

@interface NativeFlutterVC ()

@property (nonatomic, strong) FlutterMethodChannel *pushChannel;

@end

@implementation NativeFlutterVC

- (void)viewDidLoad {
    [super viewDidLoad];

    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(gotoBack)];
    if (self.navigationController != nil) {
        self.navigationItem.leftBarButtonItem = backItem;
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (self.pushChannel) {
        return;
    }
    
    __weak typeof(self) ws = self;
    self.pushChannel = [FlutterMethodChannel methodChannelWithName:@"qd.flutter.io/qd_push_main" binaryMessenger:ws];
    [self.pushChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        NSLog(@"%@", call.method);
        NSLog(@"%@", result);
        [ws handleFlutterMethod:call result:result];
    }];
}


- (void)handleFlutterMethod:(FlutterMethodCall *)call result:(FlutterResult )result {
    if ([call.method isEqualToString:@"pushNativePage"]) {
        NativeSecondVC *secondVC = [[NativeSecondVC alloc] init];
        [self.navigationController pushViewController:secondVC animated:YES];
    }
}


- (void)gotoBack {
    if (self.navigationController != nil) {
        [self.pushChannel invokeMethod:@"goback" arguments:@{@"number": @"1"} result:^(id  _Nullable result) {
            NSLog(@"%@", result);
            if ([result isEqualToString:@"gobackError"]) {
                [self.navigationController popViewControllerAnimated:YES];
            }
        }];
    }else {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
}

@end

里面注册了FlutterMethodChannel,通过Channel来调用Flutter中的方法。并接收返回结果。

注意:原生调flutter方法时,flutter返回给原生结果的方式,和flutter调原生,原生返回给flutter结果的方式不一样。

在NativeFlutterVC.m中,我们重写了返回按钮,我们需要点击原生返回键的时候通知Flutter回退页面,如果Flutter已经不能回退页面,说明Flutter已到根页面,就需要返回信息给原生,让原生pop来回退页面。

如果不重写FlutterViewController的返回按钮,就会导致我在FlutterViewController(flutter页面)页面中push了好几层之后,点击原生返回按钮,会把所有flutter页面关闭掉。

原生通过Channel调用了goback方法,看看flutter里面怎么监听

// 我在class外面生成了Channel
const pushMainPlatform = const MethodChannel('qd.flutter.io/qd_push_main');

class PushFirstPageState extends State<PushFirstPage> {

//  static const pushMainPlatform = const MethodChannel('qd.flutter.io/qd_push_main');

  // Native调用原生监听
  Future<dynamic> handelPushCall(MethodCall methodCall) {
    print(methodCall.toString());
    String backResult = "gobackSuccess";
    if (methodCall.method == "goback"){
      if (Navigator.of(this.context).canPop()) {
        Navigator.of(this.context).pop();
      }else {
        backResult = "gobackError";
      }
    }
    return Future.value(backResult);
  }

  void pushNativePage() {
    pushMainPlatform.invokeMethod('pushNativePage', {'key1':'value1'});
  }

  @override
  Widget build(BuildContext context) {

    pushMainPlatform.setMethodCallHandler(handelPushCall);

    return Scaffold(
      // push 过来的页面,原生没隐藏导航栏,所以把flutter页面的导航栏隐藏
//      appBar: new AppBar(
//        title: Text('First'),
//      ),
        body: Center (
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              new Text('First Page Content'),
              new RaisedButton(
                onPressed: (){
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => PushSecondPage(),
                    ),
                  );
                },
                child: Text('Go to Flutter Second Page'),
              ),
              new RaisedButton(
                onPressed: (){
                  SystemNavigator.pop();
                },
                child: Text('Close Flutter Page'),
              ),
              new RaisedButton(
                onPressed: (){
                  pushNativePage();
                },
                child: Text('Go to Native Page'),
              ),
            ],
          ),
        )
    );
  }
}

只看需要的就行了。在 build 中我们设置了监听方法 handelPushCall
判断 methodCall.method == 'goback'来调用 Navigator.of(this.context).pop();
重点:通过调用 Future.value(backResult) 来返回原生结果。

原生再根据返回的结果做具体处理。

3. 原生跳转Flutter页面

根据Flutter文档

4. Flutter页面跳转原生

Flutter页面跳转原生,原理还是Flutter和Native交互达到的。

比如我在Flutter页面中添加一个按钮和方法,通过Channel调用传递到原生。

              new RaisedButton(
                onPressed: (){
                  pushNativePage();
                },
                child: Text('Go to Native Page'),
              ),


  void pushNativePage() {
    pushMainPlatform.invokeMethod('pushNativePage', {'key1':'value1'});
  }

原生添加监听方法

    __weak typeof(self) ws = self;
    self.pushChannel = [FlutterMethodChannel methodChannelWithName:@"qd.flutter.io/qd_push_main" binaryMessenger:ws];
    [self.pushChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        NSLog(@"%@", call.method);
        NSLog(@"%@", result);
        [ws handleFlutterMethod:call result:result];
    }];

添加判断执行方法:

- (void)handleFlutterMethod:(FlutterMethodCall *)call result:(FlutterResult )result {
    if ([call.method isEqualToString:@"pushNativePage"]) {
        NativeSecondVC *secondVC = [[NativeSecondVC alloc] init];
        [self.navigationController pushViewController:secondVC animated:YES];
    }
}

iOS工程和Flutter页面:取用

参考:Writing custom platform-specific code

上一篇 下一篇

猜你喜欢

热点阅读