Flutter中dart和原生代码的通信之MethodChannel

前言

据我所知,真正掌握Flutter的Android开发工程师的比例并不是很高,如果你不这么认为,可能是因为你的圈子比较高端。你知道Android原生开发工程师把所有知识体系融汇贯通需要的时间是多久吗?当然这里把Flutter排除在外。资质顶尖的至少要3年,而大部分人智商属于中等偏上,大概要5年。如果是一个普通的码农,可能要8~10年甚至更久。当然我只是按之前非AI时代来进行估计的,未来AI时代可能会略有降低。10年啊,也就是一个普通的程序员需要从毕业干到快35岁危机时才能真正腾出精力来仔细研究Flutter这种跨平台的技术。当然你说我一开始的目标就是Flutter,那没问题,这不在讨论的范畴。

很多安卓开发其实压根没有练手Flutter项目的机会,因为,小公司很难招到这种十年以上的大佬。当然也不会有人从全局角度带你玩转Android Flutter混合开发,除非你运气好,正好有人带。

引入正题

如果完全没接触过Android Flutter混合开发的同学需要先看我这篇文章juejin.cn/post/725412... 。混合开发要想真正落地,不仅需要扎实的原生开发功底,还需要有刻苦钻研新技术的勇气。混合开发避不开一个话题,如何进行通信或者说相互调用的互操作性?这就引入了我们今天的主角------Method Channel。它直译过来就是方法通道,你可以理解成一个调用另一端方法的桥梁。我们看一下怎么写。

通信流程

原生调Dart
kt 复制代码
val intent = FlutterActivity
    .withNewEngine()
    .initialRoute(ROUTE_HOME)
    .build(this)
intent.setClass(this, MyFlutterActivity::class.java)
startActivity(intent)

先启动Flutter界面的原生壳Activity,清单文件该注册注册。

kt 复制代码
package com.doracrypto.crypto.ui.activity

import android.content.Intent
import com.doracrypto.crypto.AppConfig
import dora.util.SPUtils
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MyFlutterActivity : FlutterActivity() {

    companion object {
        private const val CHANNEL = "com.doracrypto.crypto/native"
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "isVipUser" -> {
                        result.success(SPUtils.readBoolean(context, AppConfig.IS_VIP))
                    }
                    "startBuyVipActivity" -> {
                        startActivity(Intent(this@MiniAppActivity, BuyVipActivity::class.java))
                        result.success(null)
                    }
                    else -> result.notImplemented()
                }
            }
    }
}

这东西虽不难,但确是必经之路。在原生侧,我们需要提供各种各样的方法给Dart侧调用。

dart 复制代码
import 'package:flutter/services.dart';

class MethodChannelPlugin {

  static const MethodChannel methodChannel = MethodChannel('com.doracrypto.crypto/native');

  // /// 调用原生浏览器组件
  // static Future<void> gotoWebView(String url) async {
  //   try {
  //     await methodChannel.invokeMethod('gotoWebView',{'url': url});
  //   } on PlatformException {
  //     print('Failed go to gotoWebView');
  //   }
  // }

  /// 检测是否是VIP用户
  static Future<bool> isVipUser() async {
    try {
      final result = await methodChannel.invokeMethod('isVipUser');
      return result == true; // 保证返回 bool
    } on PlatformException catch (e) {
      print('Failed isVipUser: $e');
      return false; // 出错时兜底返回 false
    } catch (e) {
      print('Unexpected error in isVipUser: $e');
      return false;
    }
  }

  /// 跳转购买VIP界面
  static Future<void> startBuyVipActivity() async {
    try {
      await methodChannel.invokeMethod('startBuyVipActivity');
    } on PlatformException catch (e) {
      print('Failed startBuyVipActivity: $e');
    } catch (e) {
      print('Unexpected error in startBuyVipActivity: $e');
    }
  }
}

调用时就这样去调。

dart 复制代码
bool isVip = await MethodChannelPlugin.isVipUser();
if (!isVip) {
  MethodChannelPlugin.startBuyVipActivity();
  return;
}
Dart调原生

主要都是Dart调原生,原生调用Dart的场景不是很多。不多但不代表没有,我这里简单提一嘴。

dart 复制代码
import 'package:flutter/services.dart';

class NativeBridge {

  static const MethodChannel _channel = MethodChannel("com.doracrypto.crypto/dart");

  static void init() {
    // 接收原生调用
    _channel.setMethodCallHandler((call) async {
      if (call.method == "fromNative") {
        String msg = call.arguments as String;
        print("收到原生调用:$msg");
        return "Dart收到: $msg";
      }
      return null;
    });
  }
}

在main.dart里面初始化。

dart 复制代码
void main() {
  // 等待初始化完成 
  WidgetsFlutterBinding.ensureInitialized();
  NativeBridge.init();
  runApp(MyApp());
}

原生侧调用Dart。

kt 复制代码
class MainActivity: FlutterActivity() {

    private lateinit var channel: MethodChannel

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        channel = MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "com.doracrypto.crypto/dart"
        )
        Handler(Looper.getMainLooper()).postDelayed({
            channel.invokeMethod("fromNative", "Hello from Android")
        }, 3000)
    }
}

通道名称只要唯一就行,并不一定要这样的格式。最后编译aar,三行命令,搞定。

bash 复制代码
flutter clean
flutter pub get
flutter build aar

把编译出的aar包依赖进原生项目就大功告成了,最后正常走打原生包的流程。

写在最后

Flutter是真正的好东西,对创业者相当友好,达到高级开发后,大大节省开发和维护时间,因为只维护一份代码。当你把核心算法用Dart实现后,原生的壳实际上是可以下发给别人开发的,这又进一步节省了开发时间,前提是你的项目先要稳定盈利。欢迎体验混合开发出的app www.pgyer.com/cryptotools

相关推荐
火柴就是我4 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
王晓枫4 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
砖厂小工10 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心11 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心11 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
shankss12 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
Kapaseker13 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴14 小时前
Android17 为什么重写 MessageQueue
android
忆江南1 天前
iOS 深度解析
flutter·ios
明君879971 天前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter