flutter_boost学习3:iOSNative添加与flutter交互调用

上一篇:flutter_boost学习2:iOS集成flutter_boost

前言

在上一篇集成了flutter_boost后,还缺少flutter调用原生的方法,以及原生调用flutter的相互交互,这一块只要根据flutter提供的文档就能很好的接入使用

练习GitHub:HybridFlutter

一、原生设置相关内容可供flutter调用

1、iOS原生设置可供flutter调用
2、iOS原生调用flutter的channel
完整代码如下:

//
//  MyFlutterRouter.m
//  HybridiOS
//
//  Created by loneylyflow on 15/05/2019.
//  Copyright © 2019 Lonely traveller. All rights reserved.
//

#import "MyFlutterRouter.h"
#import "MyFlutterViewController.h"
#import <UIKit/UIKit.h>
#import "UINavigationController+FDFullscreenPopGesture.h"

@interface MyFlutterRouter() <FlutterStreamHandler>
@property(nonatomic, copy) FlutterEventSink nativeCallFlutterEventSink;
@property(nonatomic, strong) UINavigationController *navigationController;
@end

@implementation MyFlutterRouter
  
+ (instancetype)sharedRouter
{
    static MyFlutterRouter *_instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[MyFlutterRouter alloc] init];
        // 在这里初始化FlutterViewController
        // 初始化Router
        [FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:_instance onStart:^(FlutterViewController * fvc){
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                MyFlutterRouter.sharedRouter.fvc = fvc;
               
            });
        }];
    });
    return _instance;
}
- (void)setFvc:(FlutterViewController *)fvc
{
    _fvc = fvc;
    if(fvc != nil){
        [self setupFlutterCallNative];
        [self setupNativeCallFlutter];
    }
    
}
#pragma mark - FlutterCallNative
- (void)setupFlutterCallNative
{
    // 用于Flutter 调用 Native
    // 这个channelname必须与Native里接收的一致
    FlutterMethodChannel *flutterCallNativeChannel = [FlutterMethodChannel methodChannelWithName:@"samples.flutter.io/flutterCallNative" binaryMessenger:self.fvc];
    
    [flutterCallNativeChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
        NSLog(@"method: %@",call.method);
        NSLog(@"arguments: %@",call.arguments);
        if([@"getBatteryLevel" isEqualToString:call.method]){
//            result([FlutterError errorWithCode:@"UNAVAILABLE"
//                                       message:@"iOS ====== Battery info unavailable2222"
//                                       details:nil]);
            result(@"Battery info iOS 1234567");
        }else if([@"updateUserInfo" isEqualToString:call.method]){
            NSDictionary *params = call.arguments;
            NSInteger userId = [params[@"id"] integerValue];
            NSString *name = params[@"name"];
            result(@{@"id":@1000,
                     @"name":@"story",
                     @"fromId":@(userId),
                     @"fromName":name
                     });
        }else if([@"nativeCallFlutter" isEqualToString:call.method]){
            //batteryChannel cal
        }else if([@"isShowNav" isEqualToString:call.method]){
           result(@(! self.navigationController.childViewControllers.lastObject.fd_prefersNavigationBarHidden));
            
        }else if([@"hideNav" isEqualToString:call.method]){
            self.navigationController.childViewControllers.lastObject.fd_prefersNavigationBarHidden = YES;
            BOOL animited = [call.arguments boolValue];
            [self.navigationController setNavigationBarHidden:YES animated:animited];
            result(@(NO));
        }else if([@"showNav" isEqualToString:call.method]){
            self.navigationController.childViewControllers.lastObject.fd_prefersNavigationBarHidden = NO;
            BOOL animited = [call.arguments boolValue];
            [self.navigationController setNavigationBarHidden:NO animated:animited];
            result(@(YES));
        }
        else{
            result(FlutterMethodNotImplemented);
        }
        
    }];
}
#pragma mark - NativeCallFlutter
- (void)setupNativeCallFlutter
{
    // 用于Native调用Flutter
    FlutterEventChannel *nativeCallFlutterChannel = [FlutterEventChannel eventChannelWithName:@"samples.flutter.io/nativeCallFlutter" binaryMessenger:self.fvc];
    [nativeCallFlutterChannel setStreamHandler:self];
}
- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events
{
    self.nativeCallFlutterEventSink = events;
//    static NSInteger mm = 0;
//    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
//        if (!_eventSink) return;
//        NSString *txt = [NSString stringWithFormat:@"    iOS倒计时 %ld秒",++mm];
//        _eventSink(txt);
//    }];
    return nil;
}
- (FlutterError*)onCancelWithArguments:(id)arguments {
    //[[NSNotificationCenter defaultCenter] removeObserver:self];
    self.nativeCallFlutterEventSink = nil;
    return nil;
}
- (void)callFluterWithName:(NSString *)name params:(id)params
{
    if(self.nativeCallFlutterEventSink){
        self.nativeCallFlutterEventSink(@{@"name":name?:@"",@"params":params ?: [NSNull null]});
    }else{
        WS(weakSelf)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [weakSelf callFluterWithName:name params:params];
        });
    }
}
#pragma mark - push/pop/close
- (void)openPage:(NSString *)name params:(NSDictionary *)params animated:(BOOL)animated completion:(void (^)(BOOL))completion
{
        if([params[@"present"] boolValue]){
            MyFlutterViewController *vc = MyFlutterViewController.new;
            [vc setName:name params:params];
            vc.hidesBottomBarWhenPushed = YES;
            [self.navigationController presentViewController:vc animated:animated completion:^{}];
        }else{
            MyFlutterViewController *vc = MyFlutterViewController.new;
            vc.hidesBottomBarWhenPushed = YES;
            [vc setName:name params:params];
            [self.navigationController pushViewController:vc animated:animated];
        }
    }
    
    
- (void)closePage:(NSString *)uid animated:(BOOL)animated params:(NSDictionary *)params completion:(void (^)(BOOL))completion
{
    FLBFlutterViewContainer *vc = (id)self.navigationController.presentedViewController;
    if([vc isKindOfClass:FLBFlutterViewContainer.class] && [vc.uniqueIDString isEqual: uid]){
        [vc dismissViewControllerAnimated:animated completion:^{}];
    }else{
        [self.navigationController popViewControllerAnimated:animated];
    }
}
- (UINavigationController *)navigationController
{
    UITabBarController *tabVC = (UITabBarController *)[UIApplication sharedApplication].delegate.window.rootViewController;
    if([tabVC isKindOfClass:[UITabBarController class]]){
        UINavigationController *nav = (UINavigationController *)tabVC.selectedViewController;
        if([nav isKindOfClass:[UINavigationController class]]){
            return nav;
        }else{
            return [[UINavigationController alloc] init];
        }
    }
    return [[UINavigationController alloc] init];
}
@end

二、flutter设置相关内容可调用Native及被Native调用

typedef void NativeCallBack(Object event);
Map<String,NativeCallBack> callbakcs = {};

 const EventChannel eventChannel = const EventChannel("samples.flutter.io/nativeCallFlutter");
 const MethodChannel platform = const MethodChannel("samples.flutter.io/flutterCallNative");


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


class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  void _onEvent(Object event)
  {
    if(event is Map){
      String name = event['name'];
      if(callbakcs[name] != null){
        callbakcs[name](event);
      }
    }

  }

  void _onError(Object error)
  {
    setState(() {
     // _changingStatus = "${error}";
    });
  }
  @override
  void initState() {
    // 接收Native调用
    eventChannel.receiveBroadcastStream().listen(_onEvent,onError:_onError);
    super.initState();

    FlutterBoost.singleton.registerPageBuilders({
      'first': (pageName, params, _) => FirstRouteWidget(),
      'second': (pageName, params, _) => SecondRouteWidget(),
      'tab': (pageName, params, _) => TabRouteWidget(),
      'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),

      ///可以在native层通过 getContainerParams 来传递参数
      'flutterPage': (pageName, params, _) {
        print("flutterPage params:$params");

        return FlutterRouteWidget();
      },
    });
    FlutterBoost.handleOnStartPage();
  }
}

具体调用:

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'main.dart';
import 'package:flutter/services.dart';

class FirstRouteWidget extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return new _FirstRouteWidgetState();
  }
}
class _FirstRouteWidgetState extends State<FirstRouteWidget>{

  String _changingStatus = "init...";
 String _batteryLevel = "leve";
 String _userInfo = "";
 bool _isShowingNav = false;

  @override
  void initState() {
    // 接收Native调用
    //callbakcs["isShowNavBar"]= _responseNative;

    super.initState();
//    _getIsNavShow();
  }
//  void _responseNative(Object event){
//      setState(() {
//        _changingStatus  = "${event}";
//      });
//  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
        leading: IconButton(icon: Icon(Icons.arrow_back),
          onPressed:(){
            FlutterBoost.singleton.closePageForContext(context);
          } ,
        ),
      ),
      body: Column(
        children: [
          new RaisedButton(
          child: Text('Flutter call Native'),
          onPressed: _getBatteryLevel
        ),
        new Text(_batteryLevel),
        new RaisedButton(
        child: Text('Flutter call updateUserInfo'),
        onPressed: _updateUserInfo
      ),
          new Text(_userInfo),
          new RaisedButton(
              child: Text(_isShowingNav ? 'hideNav222' : "showNav222"),
              onPressed: _showOrHideNav
          ),
          new RaisedButton(
              child: Text('hideNav'),
              onPressed: (){
                platform.invokeMethod("hideNav");
              }
          ),

          new RaisedButton(
          child: Text('Open second 3333'),
          onPressed: () {
            FlutterBoost.singleton.openPage("second", {}, animated: true);
          },
        ),
        new Text(_changingStatus)
      ]
      ),
    );
  }


  Future<Null> _getIsNavShow() async
  {

    bool result = false;
    try{
      result = await platform.invokeMethod("isShowNav");
    } on PlatformException catch(e) {
      result = false;
    }
//    Scaffold
//        .of(context)
//        .showSnackBar(new SnackBar(content: new Text("$result")));
    setState(() {
      _isShowingNav = result;
    });
  }
  Future<Null> _showOrHideNav() async
  {

    bool result = _isShowingNav;
    try{
      String name = _isShowingNav ? "hideNav" : "showNav";
      result = await platform.invokeMethod(name,false);
    } on PlatformException catch(e) {

    }

    setState(() {
      _isShowingNav = result;
    });
  }

  Future<Null> _getBatteryLevel() async
  {
    String batteryLevel;
    try{
      final String result = await platform.invokeMethod("getBatteryLevel");
      batteryLevel = "Battery level at $result % .";
    } on PlatformException catch(e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  Future<Null>  _updateUserInfo() async
  {
    String msg;
    try{
      final Map result = await platform.invokeMethod("updateUserInfo",{"id":10,"name":"Kusina"});
      msg = "Battery level at $result % .";
    } on PlatformException catch(e) {
      msg = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _userInfo = msg;
    });
  }
}

class SecondRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Navigate back to first route when tapped.
            FlutterBoost.singleton.closePageForContext(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

class TabRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Tab Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            FlutterBoost.singleton.openPage("second", {}, animated: true);
          },
          child: Text('Open second route'),
        ),
      ),
    );
  }
}

class FlutterRouteWidget extends StatelessWidget {
  final String message;

  FlutterRouteWidget({this.message});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('flutter_boost_example'),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            margin: const EdgeInsets.only(top: 80.0),
            child: Text(
              message ?? "This is a flutter activity",
              style: TextStyle(fontSize: 28.0, color: Colors.blue),
            ),
            alignment: AlignmentDirectional.center,
          ),
          Expanded(child: Container()),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'open native page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),

            ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
            ///例如:sample://nativePage?aaa=bbb
            onTap: () =>
                FlutterBoost.singleton.openPage("sample://nativePage", {
                  "query": {"aaa": "bbb"}
                }),
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'open flutter page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),

            ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
            ///例如:sample://nativePage?aaa=bbb
            onTap: () =>
                FlutterBoost.singleton.openPage("sample://flutterPage", {
                  "query": {"aaa": "bbb"}
                }),
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'push flutter widget',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () {
              Navigator.push(
                  context, MaterialPageRoute(builder: (_) => PushWidget()));
            },
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 80.0),
                color: Colors.yellow,
                child: Text(
                  'open flutter fragment page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () => FlutterBoost.singleton
                .openPage("sample://flutterFragmentPage", {}),
          )
        ],
      ),
    );
  }
}

class FragmentRouteWidget extends StatelessWidget {
  final Map params;

  FragmentRouteWidget(this.params);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('flutter_boost_example'),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            margin: const EdgeInsets.only(top: 80.0),
            child: Text(
              "This is a flutter fragment",
              style: TextStyle(fontSize: 28.0, color: Colors.blue),
            ),
            alignment: AlignmentDirectional.center,
          ),
          Container(
            margin: const EdgeInsets.only(top: 32.0),
            child: Text(
              params['tag'] ?? '',
              style: TextStyle(fontSize: 28.0, color: Colors.red),
            ),
            alignment: AlignmentDirectional.center,
          ),
          Expanded(child: Container()),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'open native page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () =>
                FlutterBoost.singleton.openPage("sample://nativePage", {}),
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'open flutter page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () =>
                FlutterBoost.singleton.openPage("sample://flutterPage", {}),
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 80.0),
                color: Colors.yellow,
                child: Text(
                  'open flutter fragment page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () => FlutterBoost.singleton
                .openPage("sample://flutterFragmentPage", {}),
          )
        ],
      ),
    );
  }
}

class PushWidget extends StatefulWidget {
  @override
  _PushWidgetState createState() => _PushWidgetState();
}

class _PushWidgetState extends State<PushWidget> {
  VoidCallback _backPressedListenerUnsub;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();

//    if (_backPressedListenerUnsub == null) {
//      _backPressedListenerUnsub =
//          BoostContainer.of(context).addBackPressedListener(() {
//        if (BoostContainer.of(context).onstage &&
//            ModalRoute.of(context).isCurrent) {
//          Navigator.pop(context);
//        }
//      });
//    }
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _backPressedListenerUnsub?.call();
  }

  @override
  Widget build(BuildContext context) {
    return FlutterRouteWidget(message:"Pushed Widget");
  }
}

完整内容参考:HybridFlutter

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容