簡體   English   中英

如何使用 Flutter MethodChannel 從本機 swift 代碼調用 dart 代碼中的方法?

[英]How do I use a Flutter MethodChannel to invoke a method in dart code from the native swift code?

我看過關於這個主題很多類似的問題,但沒有解決方案,為我工作。我正在開發中的顫振應用程序,但要調用特定的方法在我main.dart從文件AppDelegate.swift在本機iOS項目。

為了刪除所有其他變量,我已將問題提取到一個新的 dart 項目中。 我正在嘗試使用setChannelText()AppDelegate.swift調用setChannelText() methodChannel.invokeMethod() ,但沒有成功。

有誰知道我哪里出錯了? 我知道我沒有對methodChannel.invokeMethod()的“name”參數采取行動,但那是因為我只想調用調用方法......

這是我的 main.dart 文件:

 import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { MethodChannel channel = new MethodChannel("com.example.channeltest/changetext"); String centerText; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.purple, body: Center( child: Text( centerText, style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 30.0, ), ), ), ), ); } @override void initState() { super.initState(); this.channel.setMethodCallHandler((call) async => await setChannelText()); this.centerText = "Hello World!"; } Future setChannelText() async { Future.delayed(Duration(milliseconds: 200)); setState(() => this.centerText = "Another Text."); } }

這是我的 AppDelegate.swift 文件:

 import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { var methodChannel: FlutterMethodChannel! override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController methodChannel = FlutterMethodChannel(name: "com.example.channeltest/changetext", binaryMessenger: rootViewController as! FlutterBinaryMessenger) //This call would obviously be somewhere else in a real world example, but I'm just //testing if I can invoke the method in my dart code at all.. methodChannel.invokeMethod("some_method_name", arguments: nil) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }

最后,我試圖在啟動后立即更改文本,但事實並非如此。

在 iOS 模擬器上運行的應用程序截圖

在此先感謝您的幫助!!

顫振側代碼:

 import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class _MyHomePageState extends State<MyHomePage> { static const platform = MethodChannel('samples.flutter.dev/battery'); // Get battery level. String _batteryLevel = 'Unknown battery level.'; Future<void> _getBatteryLevel() async { String batteryLevel; try { final int 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; }); } @override Widget build(BuildContext context) { return Material( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( child: Text('Get Battery Level'), onPressed: _getBatteryLevel, ), Text(_batteryLevel), ], ), ), ); } }

Swift 代碼在這里:

 @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery", binaryMessenger: controller.binaryMessenger) batteryChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in // Note: this method is invoked on the UI thread. guard call.method == "getBatteryLevel" else { result(FlutterMethodNotImplemented) return } self?.receiveBatteryLevel(result: result) }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }

或者參考這個鏈接:

平台渠道

問題

問題是您的平台端(在本例中為 iOS)Flutter 准備就緒之前調用 Flutter 端的方法。 無法從平台端檢查,因此您的Flutter 應用程序必須告訴您的平台端 你會在 Android 上遇到同樣的問題。

解決方案

為了克服這個問題,您必須告訴平台端應用程序已准備就緒(通過發送平台方法)並將其保存在一個布爾值中。 然后平台端就可以開始發送消息了。

你應該真正閱讀日志,它應該警告你一些類似的東西:“沒有任何東西在聽這個,或者 Flutter 引擎沒有連接”。

import 'dart:async';

import 'package:flutter/src/services/platform_channel.dart';

class StringService {
  final methodChannel =
      const MethodChannel("com.example.app_name.method_channel.strings");

  final StreamController<String> _stringStreamController =
      StreamController<String>();

  Stream<String> get stringStream => _stringStreamController.stream;

  StringService() {
    // Set method call handler before telling platform side we are ready to receive.
    methodChannel.setMethodCallHandler((call) async {
      print('Just received ${call.method} from platform');
      if (call.method == "new_string") {
        _stringStreamController.add(call.arguments as String);
      } else {
        print("Method not implemented: ${call.method}");
      }
    });
    // Tell platform side we are ready!
    methodChannel.invokeMethod("isReady");
  }
}

你可以在reverse_platform_methods看到一個工作項目,尤其是AppDelegate.swift 我沒有為 Android 實現它,但你可以在MainActivity.kt中以類似的方式實現它。

運行項目的 iOS 應用截圖

大多數應用程序不希望代碼首先從平台端調用。 你的用例是什么? 根據您的回答,我可能會提供更好的建議。 我實現了這個來處理傳遞到設備的推送通知,所以“事件”肯定是從平台端觸發的。

此外,如果您遇到錯誤和警告,您應該顯示它們,例如No implementation found for method $method on channel $name'

嗯,問題全在於初始化過程。 在 dart/flutter 部分准備好處理它之前,您嘗試從 swift 代碼中調用您的方法。

您必須執行以下步驟才能獲得結果:

  1. 重要的。 在你的AppDelegate for ios 中使用這個方法
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    
 var methodChannel: FlutterMethodChannel? = nil

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

    print("Setup methodChannel from Swift")
    let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
    methodChannel = FlutterMethodChannel(name: "com.example.channeltest/changetext", binaryMessenger: rootViewController as! FlutterBinaryMessenger)

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
    
    //THIS METHOD
    override func applicationDidBecomeActive(_ application: UIApplication) {
        methodChannel?.invokeMethod("some_method_name", arguments: "ios string")
    }
}

對於安卓:

class MainActivity : FlutterActivity() {
    var channel: MethodChannel? = null

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        channel = MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "com.example.channeltest/changetext"
        )

    }

    override fun onStart() {
        super.onStart()
        channel?.invokeMethod("some_method_name", "android str")
    }
}
  1. 使用MethodChannel創建您自己的類( MethodChannel答案)
class TestChannel {
  static MethodChannel channel =
  const MethodChannel("com.example.channeltest/changetext");

  final StreamController<String> _controller =
  StreamController<String>();

  Stream<String> get stringStream => _controller.stream;

  TestChannel() {
    channel.setMethodCallHandler((call) async {
      if (call.method == "some_method_name") {
        _controller.add(call.arguments as String);
      } else {
        print("Method not implemented: ${call.method}");
      }
    });
  }
}
  1. 重要的。 創建它的全局實例
final _changeTextChannel = TestChannel(); //<--- like this

void main() {
  runApp(MyApp());
}
  1. 在 UI 中處理它
class TestPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: StreamBuilder<String>(
        stream: _changeTextChannel.stringStream,
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if (snapshot.hasError) {
            return Text("Error");
          }

          if (!snapshot.hasData) {
            return Text("Loading");
          }

          return Text(snapshot.data ?? "NO_DATA");
        },
      )),
    );
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM