如何使用 Pigeon 包从 Flutter 调用本机 iOS 函数?
How to call native iOS function from Flutter using the Pigeon package?
我正在尝试将一些本机代码集成到我的项目中。为此,我正在使用新的 Pigeon 包:https://pub.dev/packages/pigeon
在 Android 上它工作得很好但是在 iOS 上,每当我尝试调用在 swift 中编写的方法(按下按钮时),它会抛出一个 PlatformException(channel-错误,无法在通道上建立连接。, null, null)
我已经设置了 flutter 启动项目,然后在按下 floatingActionButton 时调用本机方法。当执行本机方法时,它 returns 一个字符串,在 android 端说“Hello from Android”,在 iOS 端说“Hello from Xcode”。
main.dart 文件:
import 'package:flutter/material.dart';
import 'package:platforms/pigeon.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
Api api = Api();
final searchReply = await api.search(SearchRequest());
print(searchReply.result);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
MainActivity.java 文件:
package com.example.platforms;
import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
private class Api implements Pigeon.Api {
@Override
public Pigeon.SearchReply search(Pigeon.SearchRequest arg) {
Pigeon.SearchReply searchReply = new Pigeon.SearchReply();
searchReply.setResult("Hello from Android!");
return searchReply;
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Pigeon.Api.setup(getFlutterEngine().getDartExecutor().getBinaryMessenger(), new Api());
}
}
AppDelegate.swift 文件:
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, Api {
var engine: FlutterEngine = {
let result = FlutterEngine.init()
// This could be `run` earlier in the app to avoid the overhead of doing it the first time the
// engine is needed.
result.run()
return result
}()
func search(_ input: SearchRequest, error: AutoreleasingUnsafeMutablePointer<FlutterError?>) -> SearchReply? {
let reply = SearchReply()
reply.result = "Hello from Xcode!!!!"
return reply
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
ApiSetup(engine.binaryMessenger, self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Android 上 onPressed 的结果:
iOS 上 onPressed 的结果:
另外值得注意的是,当我在didChangedDependencies函数中调用onPressed中的代码时,swift中的代码做了运行,结果确实出现了,但同样的异常是然后再次抛出。这意味着确实创建了频道,我只是用错了。
当这个添加到main.dart
@override
void didChangeDependencies() async {
Api api = Api();
final searchReply = await api.search(SearchRequest());
print(searchReply.result);
super.didChangeDependencies();
}
它打印 swift 的结果,然后抛出相同的错误。
我认为 BinaryMessenger
设置不正确。
我试过了,效果很好:
在 didFinishLaunchingWithOptions
方法中获取 rootViewController
并将其转换为 FlutterBinaryMessenger
:
let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
// get binaryMessenger
let binaryMessenger = rootViewController as! FlutterBinaryMessenger
// set binaryMessenger
ApiSetup(binaryMessenger, self)
完整的方法变成这样:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self);
let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
// get binaryMessenger
let binaryMessenger = rootViewController as! FlutterBinaryMessenger
// set binaryMessenger
ApiSetup(binaryMessenger, self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
我正在尝试将一些本机代码集成到我的项目中。为此,我正在使用新的 Pigeon 包:https://pub.dev/packages/pigeon
在 Android 上它工作得很好但是在 iOS 上,每当我尝试调用在 swift 中编写的方法(按下按钮时),它会抛出一个 PlatformException(channel-错误,无法在通道上建立连接。, null, null)
我已经设置了 flutter 启动项目,然后在按下 floatingActionButton 时调用本机方法。当执行本机方法时,它 returns 一个字符串,在 android 端说“Hello from Android”,在 iOS 端说“Hello from Xcode”。
main.dart 文件:
import 'package:flutter/material.dart';
import 'package:platforms/pigeon.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
Api api = Api();
final searchReply = await api.search(SearchRequest());
print(searchReply.result);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
MainActivity.java 文件:
package com.example.platforms;
import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
private class Api implements Pigeon.Api {
@Override
public Pigeon.SearchReply search(Pigeon.SearchRequest arg) {
Pigeon.SearchReply searchReply = new Pigeon.SearchReply();
searchReply.setResult("Hello from Android!");
return searchReply;
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Pigeon.Api.setup(getFlutterEngine().getDartExecutor().getBinaryMessenger(), new Api());
}
}
AppDelegate.swift 文件:
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, Api {
var engine: FlutterEngine = {
let result = FlutterEngine.init()
// This could be `run` earlier in the app to avoid the overhead of doing it the first time the
// engine is needed.
result.run()
return result
}()
func search(_ input: SearchRequest, error: AutoreleasingUnsafeMutablePointer<FlutterError?>) -> SearchReply? {
let reply = SearchReply()
reply.result = "Hello from Xcode!!!!"
return reply
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
ApiSetup(engine.binaryMessenger, self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Android 上 onPressed 的结果:
iOS 上 onPressed 的结果:
另外值得注意的是,当我在didChangedDependencies函数中调用onPressed中的代码时,swift中的代码做了运行,结果确实出现了,但同样的异常是然后再次抛出。这意味着确实创建了频道,我只是用错了。
当这个添加到main.dart
@override
void didChangeDependencies() async {
Api api = Api();
final searchReply = await api.search(SearchRequest());
print(searchReply.result);
super.didChangeDependencies();
}
它打印 swift 的结果,然后抛出相同的错误。
我认为 BinaryMessenger
设置不正确。
我试过了,效果很好:
在 didFinishLaunchingWithOptions
方法中获取 rootViewController
并将其转换为 FlutterBinaryMessenger
:
let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
// get binaryMessenger
let binaryMessenger = rootViewController as! FlutterBinaryMessenger
// set binaryMessenger
ApiSetup(binaryMessenger, self)
完整的方法变成这样:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self);
let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
// get binaryMessenger
let binaryMessenger = rootViewController as! FlutterBinaryMessenger
// set binaryMessenger
ApiSetup(binaryMessenger, self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}