上一篇: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