Flutter では、プラットフォーム チャネルにより、Flutter とネイティブ プラットフォーム (Android や iOS など) の間の双方向通信が可能になり、Flutter アプリケーションとネイティブ コード間でメッセージを渡したり、関数を呼び出したりできます。
以下は、プラットフォーム チャネルを使用してネイティブと通信するための一般的な手順です。
1. Flutter 側で MethodChannel オブジェクトを作成し、ネイティブ プラットフォームにメッセージを送信します。MethodChannel は通常、Flutter Widget の初期化メソッドで作成されます。
import 'package:flutter/services.dart';
// 创建MethodChannel对象
MethodChannel _channel = MethodChannel('com.example.channelName');
2. Flutter 側のネイティブ プラットフォームにメッセージを送信するには、MethodChannel の invokeMethod メソッドを使用できます。
// 发送消息给原生平台
dynamic result = await _channel.invokeMethod('methodName', arguments);
3. Flutter によって送信されたメッセージを受信するために、ネイティブ プラットフォーム (Android や iOS など) に対応するメソッドを実装します。
Android プラットフォームの場合、MethodChannel をMainActivity
またはApplication
クラスに登録し、対応するメソッドを実装できます。
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.channelName";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler((call, result) -> {
if (call.method.equals("methodName")) {
// 处理Flutter端发送的消息
String arg = call.arguments.toString();
// 执行相应的操作
// ...
// 将结果返回给Flutter端
result.success("Result from native");
} else {
result.notImplemented();
}
});
}
}
iOS プラットフォームの場合、AppDelegate.m ファイルに MethodChannel を登録し、対応するメソッドを実装します。
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
FlutterMethodChannel *channel = [FlutterMethodChannel
methodChannelWithName:@"com.example.channelName"
binaryMessenger:controller.binaryMessenger];
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"methodName" isEqualToString:call.method]) {
// 处理Flutter端发送的消息
NSString *arg = call.arguments;
// 执行相应的操作
// ...
// 将结果返回给Flutter端
result(@"Result from native");
} else {
result(FlutterMethodNotImplemented);
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
以上の手順により、Flutter側とネイティブコード間で双方向の通信を行うことができます。
以下は、私のプロジェクトの開始時の研究コード ブロックです。
フラッターのコード:
class GetxStatePage extends StatefulWidget {
const GetxStatePage({super.key});
@override
State<StatefulWidget> createState() {
return GetxStatePageState();
}
}
class GetxStatePageState extends State<GetxStatePage> {
late MethodChannel _channel;
@override
void initState() {
super.initState();
//初始化MethodChannel,通道名称“multiple-flutters”,Android端ios也要统一
_channel = const MethodChannel('multiple-flutters');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: SGColors.white,
title: const Text(
'Getx',
style: TextStyle(color: SGColors.textColor),
),
),
body: Center(
child: InkWell(
onTap: () {
String value = "Hello from Flutter";
//这里定义方法和参数
_channel.invokeMethod<void>("nextData", {'data': value});
// 在此处添加您希望执行的点击事件
print('跳转到Android');
},
child: SizedBox(
width: 100,
height: 100,
child: Text("Getx"),
),
),
),
),
);
}
}
Android 側の実装コードを見てみましょう。まず、チャネルを開くには、エンジン、チャネル、フラッター側との統合が必要です。
class EngineBindings(
activity: Activity, delegate: EngineBindingsDelegate, entrypoint: String,
initialRoute: String) :
DataModelObserver {
val channel: MethodChannel
val engine: FlutterEngine
val delegate: EngineBindingsDelegate
init {
// This has to be lazy to avoid creation before the FlutterEngineGroup.
val dartEntrypoint =
DartExecutor.DartEntrypoint(
FlutterInjector.instance().flutterLoader().findAppBundlePath(), entrypoint
)
// engine = BaseApplication.getApplication().engines.createAndRunEngine(activity, dartEntrypoint)
engine = BaseApplication.getApplication().engines.createAndRunEngine(activity, dartEntrypoint, initialRoute)
this.delegate = delegate
channel = MethodChannel(engine.dartExecutor.binaryMessenger, "multiple-flutters")
}
/**
* This setups the messaging connections on the platform channel and the DataModel.
*/
fun attach() {
DataModel.instance.addObserver(this)
channel.invokeMethod("setCount", DataModel.instance.counter)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"incrementCount" -> {
DataModel.instance.counter = DataModel.instance.counter + 1
result.success(null)
}
"next" -> {
this.delegate.onNext()
result.success(null)
}
"nextData" -> {
val data: String? = call.argument("data")
this.delegate.onNext(data)
result.success(null)
}
else -> {
result.notImplemented()
}
}
}
}
/**
* This tears down the messaging connections on the platform channel and the DataModel.
*/
fun detach() {
engine.destroy();
DataModel.instance.removeObserver(this)
channel.setMethodCallHandler(null)
}
override fun onCountUpdate(newCount: Int) {
channel.invokeMethod("setCount", newCount)
}
}
特定のアクティビティは FlutterActivity を継承する必要があり、このクラスはメイン構成リスト AndroidMainfest.xml で構成する必要があります。
<activity
android:name=".ui.demo.SingleFlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
class FlutterToAndroidActivity : FlutterActivity(), EngineBindingsDelegate {
var mFlutterApi: SGAndroid2Flutter? = null
private val engineBindings: EngineBindings by lazy {
EngineBindings(activity = this, delegate = this, entrypoint = FlutterRouter.MESSAGE_CENTER_ENTRY_POINTER, initialRoute = "${FlutterRouter.MESSAGE_CENTER_ROUTER}?psId=1234")
}
override fun onDestroy() {
super.onDestroy()
engineBindings.detach()
}
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return engineBindings.engine
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
engineBindings.attach()
setContentView(layout.activity_flutter_to_android)
val extraString = intent.getStringExtra("extraString")
tv_center.text = extraString
val binaryMessenger: BinaryMessenger = provideFlutterEngine(this)!!.getDartExecutor().getBinaryMessenger()
mFlutterApi = SGAndroid2Flutter(binaryMessenger)
tv_center.setOnClickListener {
callFlutterMethod()
}
}
private fun callFlutterMethod() {
val handler = Handler(Looper.getMainLooper())
(0..2)
.map { it.toLong() * 100 }
.forEach {
handler.postDelayed(it) {
mFlutterApi?.sendData(it.toString()) { // 必须在主线程中调用
println("从 Flutter 获取到的值是:$it ") // true,回调在主线程
}
}
}
}
override fun onNext() {
}
override fun onNext(str: String?) {
}
}
次に、onNext メソッドでオペレーションを呼び出すことができます。
/**
* flutter 调用原生的方法,原生写法
* */
override fun onNext(str: String?) {
val flutterIntent = Intent(this, FlutterToAndroidActivity::class.java)
flutterIntent.putExtra("extraString", str)
startActivity(flutterIntent)
}
最後に、flutter は FlutterToAndroidActivity に正常にジャンプし、flutter はネイティブ Android メソッドを正常に呼び出します。
経験:
私はこれまで純粋なフラッターを使って開発を行ってきましたが、ネイティブとフラッターの開発が必要になると、実際にはコストが高くなります。
彼はブリッジング コードを書くために Android とセットを書くために iOS の両方を必要とし、フラッター側はセットです。
なぜ?船は大きすぎて向きを変えることができないため、一部の大規模な既存プロジェクトでは技術スタックを短期間で変更することができず、一部の企業はその一部を海域のテストに使用する予定だ。部分的なコードの移行。将来のプロジェクトの基礎を築きます。
ネイティブ側でネットワークデータを取得してインターフェースをリクエストし、そのデータをflutterに渡すなどの特殊な要件が発生するとさらに面倒になりますが、最初にどのインターフェースをリクエストするのか、どのパラメータをリクエストするのかなどネイティブ、Android、iOS の両端で実現するには、flutter で渡す必要があります。
そのため、プロジェクトでは Pigeon のプラグインを使用しました。これにより、あまり多くのコードを記述する必要がなくなります。通信プロセス中に Pigeon が自動的にコードを生成します。練習のために、次の記事を参照してください。