flutter 和ios平台混合开发
Flutter使用了一个灵活的系统,允许您调用特定平台的API,无论在Android上的Java或Kotlin代码中,还是iOS上的ObjectiveC或Swift代码中均可用。
Flutter平台特定的API支持不依赖于代码生成,而是依赖于灵活的消息传递的方式:
- 应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。
- 宿主监听平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。
框架概述: 平台通道
使用平台通道在客户端(Flutter UI)和宿主(平台)之间传递消息,如下图所示:
平台通道注意消息和响应是异步传递的,以确保用户界面保持响应(不会挂起)。
在客户端,MethodChannel
(API)可以发送与方法调用相对应的消息。 在宿主平台上,MethodChannel
在Android((API) 和 FlutterMethodChannel iOS (API) 可以接收方法调用并返回结果。这些类允许您用很少的“脚手架”代码开发平台插件。
-
注意: 如果需要,方法调用也可以反向发送,宿主作为客户端调用Dart中实现的API。 这个
quick_actions
插件就是一个具体的例子
平台通道数据类型支持和解码器
标准平台通道使用标准消息编解码器,以支持简单的类似JSON值的高效二进制序列化,例如 booleans,numbers, Strings, byte buffers, List, Maps(请参阅StandardMessageCodec
了解详细信息)。
- 当您发送和接收值时,这些值在消息中的序列化和反序列化会自动进行。*
下表显示了如何在宿主上接收Dart值,反之亦然:
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: |
int, if 64 bits not enough | java.math.BigInteger | FlutterStandardBigInteger |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
举例
1打开终端随便cd一个目录下,生成flutterIosMix 目录
mkdir flutterIosMix |ls
2 进入该目录并且创建一个新的应用程序
cd flutterIosMix
flutter create flutteriosmix
这个时候的目录结构如下
目录结构
3.用vscode 打开该工程并运行该工程
结果如下:
image.png
这个时候有默认工程的代码
4.修改main.dart 代码
修改main.dart 代码如下
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: PlatformChannel(),
);
}
}
class PlatformChannel extends StatefulWidget {
@override
_PlatformChannelState createState() => _PlatformChannelState();
}
class _PlatformChannelState extends State<PlatformChannel> {
int _hitNum = 0;
int _time = 0;
Future<void> _hitEvent() async{
}
@override
Widget build(BuildContext context) {
return Material(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("当前点击次数:$_hitNum", key: const Key('hitEvent')),
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: const Text('hit'),
onPressed: _hitEvent,
),
),
],
),
Text("计时器时间${_time}s"),
],
),
);
}
}
运行上述代码结果如下
这是准备好的工程基本工程.接下来我们就准备flutter 和ios平台的混合开发代码。我们准备开发的内容如下
- 1 点击hit按钮,我们从ios平台获取已经点击的次数
- 2.我们从ios平台的计时器中获取回调给flutter的计时时间。
我们调用ios平台的方法是通过MethodChannel 类来实现的
我们监听来自ios的调用是通过EventChannel 类来实现的
5. 实现点击hit 按钮获取ios平台回传回来的点击次数
<1> 添加引用文件
import 'package:flutter/services.dart';
在main.dart 文件中头部添加上述代码。
<2>类_PlatformChannelState增加一个成员变量
class _PlatformChannelState extends State<PlatformChannel> {
static const MethodChannel methodChannel =
MethodChannel('hit.flutter.io/count');
...
}
这里我们生成一个MethodChannel类型的变量methodChannel 并且实例化
"hit.flutter.io/count" 这里需要注意,这是与ios平台桥接的key,可以随便命名,但是必须和ios平台接受时候的key 一致。
<3> 在_hitEvent 函数中添加如下函数
Future<void> _hitEvent() async{
int hitNum;
try {
final int result = await methodChannel.invokeMethod('getBatteryLevel');
hitNum =result;
} on PlatformException {
}
if (hitNum !=null) {
setState(() {
_hitNum = hitNum;
});
}
}
这个时候保存main.dart 代码。点击hit按钮是崩溃的
<4> 打开ios工程代码
打开ios工程ios->右键reveal in finder -> xcode 打开该工程
image.png<5>编辑ios工程代码
打开appdelegate.m 文件,修改文件如下
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import <Flutter/Flutter.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
///hit.flutter.io/count 必须与flutter 请求的key一样才会调用到函数中
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel methodChannelWithName:@"hit.flutter.io/count" binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
///这个是flutter 请求的事件
if ([@"hitCount" isEqualToString:call.method]) {
static int count =0;
return result(@(++count));
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
这个时候,我们就实现了flutter 平台调用ios平台的函数了
停止应用,重新启动app。保证app对ios平台的代码进行了重新编译。
运行结果如下
6.实现ios平台调用flutter平台的方法
ios 平台调用flutter 是通过flutter监听事件来完成的
<1> 引入头文件
import 'package:flutter/services.dart';
<2>添加成员变量
class _PlatformChannelState extends State<PlatformChannel> {
static const EventChannel eventChannel =
EventChannel('time.flutter.io/count');
...
}
<3>添加监听事件
@override
void initState() {
super.initState();
eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
void _onEvent(Object event) {
setState(() {
print("{$event}");
_chargingStatus =
"Battery status: ${event}";
});
}
void _onError(Object error) {
setState(() {
_chargingStatus = 'Battery status: unknown.';
});
}
<4>打开ios工程代码 并添加代码如下
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
···
FlutterEventChannel* chargingChannel = [FlutterEventChannel eventChannelWithName:@"time.flutter.io/count" binaryMessenger:controller];
[chargingChannel setStreamHandler:self];
···
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
给appdelegate类添加成员变量
@interface AppDelegate()
{
FlutterEventSink _eventSink;
}
@end
增加代理函数
- (FlutterError*)onListenWithArguments:(id)arguments
eventSink:(FlutterEventSink)eventSink {
_eventSink = eventSink;
static int mm = 0;
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (!_eventSink) return;
_eventSink(@(++mm));
}];
return nil;
}
- (FlutterError*)onCancelWithArguments:(id)arguments {
[[NSNotificationCenter defaultCenter] removeObserver:self];
_eventSink = nil;
return nil;
}
这样就实现了ios 调用flutter函数