Flutter 与 Compose 应该怎么选择?它们冲突吗?

Flutter与Compose的选择
本文探讨了Flutter与Jetpack Compose在Android开发中的角色与选择。分析了两者的设计初衷、未来目标及适用场景,建议开发者根据自身发展方向进行选择。

没用的前言

其实自从 Jetpack Compose 面世以来,关于 Flutter 与 Compose 之间的选择问题就开始在 Android 开发中出现,就如同之前有 iOSer 纠结在 Flutter 和 SwiftUI 之间选谁一样,对于 Android 开发来说似乎“更头痛”的是 Flutter 与 Compose “同出一爹”

本来关于这个话题没什么好写的,因为这个话题属于“吃力不讨好”的类型 ,毕竟被原生开发看到了,可能就有人就要痛批我:

“是我 Andorid 提不动刀了,还是这些一两年的’小框架‘飘了,搞不好我们再等等它们就都凉了,你整天就知道’贩卖焦虑’收割流量。”

但是看你们讨论得热火朝天,加之时不时要回答类似的问题,实在憋不住了,所以最终还是选择“冒死”输出一波,从我的观点来介绍它们之间的关系和如何选择。

当然,有人说我都不选不行吗?当然可以,因为在目前阶段 Flutter 和 Compose 都不影响你的 Android 开发地位,它们最多只是属于竞争工具。

Flutter 和 Compose 初衷

Flutter 和 Compose 的未来目标会比较一致,但是至少它们出现的初衷是不一样。

首先 Compose 是 Jetpack 系列的全新 UI 库,理解下这点!Compose 是 Jetpack 系列的成员之一,所以可以被应用到 Android 界面开发中,所以你也可以选择不用,用不用都能开发 Android 的 UI

然后再说 Compose 出生的目的:就是为了重新定义 Android 上 UI 的编写方式,为了提高 Android 原生的 UI 开发效率,让 Android 的 UI 开发方式能跟上时代的步伐

不管你喜不喜欢,声明式的界面开发就是如今的潮流,不管是 React 、SwiftUI 、Flutter 等都在表明这一点。

而对于 Flutter 而言就是跨平台,因为 Flutter 没有自己的平台 ,有人说 Fuchsia 会是 Flutter 的家,但那已经属于后话,毕竟 Fuchsia 要先能养活自己。

因为 Flutter 出生就是为了跨平台存在的全新 UI 框架,从底层到上层都是“创新”和“大胆”的设计,就选择 Dart 本身就是一项很“大胆”的决定,甚至在 Web 平台都敢支持选用 CanvaskitWebAssembly 模式。

所以 Flutter 的“任性”从一出来就不被看好,当然至今也有不看好它的人,因为它某种程度很“偏激”和不友好。

所以扯了那么多,总结下就是:

  • Compose 是 Android UI 的未来,现阶段你可以不会,但是如果未来你会继续在 Android 平台的话,你就必须会。

  • Flutter 的未来在于多平台,更稳定可靠的多平台 UI 框架。如果你的路线方向不是大前端或者多端开发者,那你不需要会。

对了,鸿蒙上也是有类似 Flutter 的实现,感兴趣的可以自己关注下。

Compose 和 Flutter 未来一致

虽然 Compose 和 Flutter 初始服务的对象并不一致,但是它们未来目标肯定是一致。

为什么这么说?[《Jetpack Compose for Desktop: 里程碑1发布》](Jetpack Compose for Desktop: 里程碑1发布) 不就表明了这一态度么? Compose 虽然只是作为 Jetpack 的一个 UI 子集,但是它设计的理念和架构本身就带有跨平台支持的潜力

本质是 Compose 也是类似于一个编译器加上一个 Skia 的工作模式,这和 Flutter 没有什么区别,不说开发方式,仅从控件命名上 Flutter 和 Compose 就不会让你感觉陌生。

不说控件,就说这次 Flutter 2.0 更新中 Dart 1.12 的 null-safety 和 Kotlin 像不像?

所以回归到主题的另外一个问题, Flutter 和 Compose 冲突吗?

从立项的意义上看 Flutter 和 Compose 好像是冲突的,但是从使用者的角度看,它们并不冲突

因为对于开发者而言,不管你是先学会 Compose 还是先学会 Flutter,对于你掌握另外一项技能都有帮助,相当于学会一种就等于学会另一种的 70%

从未来的角度看:

  • 如果你是原生开发,还没接触过 Flutter , 那先去学 Compose ,这对你的 Android 生涯更有帮助,然后再学 Flutter 也不难。

  • 如果你已经在使用或者学习 Flutter ,那么请继续深造,不必因为担心 Compose 而停滞不前,当你掌握了 Flutter 后其实离 Compose 也不远了。

它们二者的未来都会是多平台,而我认为的冲突主要是在于动手学起来,而不是在二者之间徘徊纠结。

从现实角度出发:目前 Flutter 2.0 下的 Android 和 iOS 已经趋向稳定,Web 已经进入 Stable 分支,而 Macos/Linux/Win 也进入了 Beta 阶段,并且可以在 Stable 分支通过 snapshot 预览。所以从这个阶段考虑,如果你需要跨平台开发,甚至 PC 平台,那么优先考虑 Flutter 吧。

你选择 React Native 也没问题,说起来最近 React Native 的版本号已经到了 0.64 了😏

当然大家可能会关心框架是否有坑的问题,本质上所有框架都有坑,甚至网络因素都可能会成为你的痛点,问题在于你是否接受这些坑

跨平台的背后本身就是“脏活”和“累活”, Flutter 的全平台之路很艰难,就像之前写的[《解读 Flutter 全平台开发的误解与偏见》](解读 Flutter 全平台开发的误解与偏见), 现阶段 Flutter 全平台更多只是噱头,只是提供了“多一种可能”的阶段。

最后还是要例行补充这一点:

跨平台之所以是跨平台,首先就是要有对应原生平台的存在, 很多原生平台的问题都需要回归到平台去解决,那些喜欢吹 xxx 制霸原生要凉的节奏,仅仅是因为“你的焦虑会成为它们的利润”。

聊点废话

说点“道理我都懂”的实话,本质是我们作为开发者,其实并不应该把自己归纳为于某种语言和特定的框架之下,我们现在被归纳在某个领域仅仅是因为工作需要,而对于未来我们的发展,其实更应该注重的是编程基础和动手能力。

我本身是通过 Weex 接触的 Vue ,也用过 uni-app 做个简单的小程序,用 React Native 开发过两端 App ,也用 Flutter 写过 Web ,甚至手贱地在 SpringBoot 上用 Kotlin 写 API。

所以在我眼中,现在客户端和前端之间的划分已经越来越模糊,我遇到不少 Android 开发写过小程序或者 Vue ,不少前端也通过 uni-app, RN 和 Flutter 在写 App ,这是很正常的趋势,因为平台成熟了,越成熟的平台就会开始和相近的领域融合贯通

你想说“卷”也行,这种趋势会让一些简单、重复或者需要共享的内容通过跨平台来得到落地,我相信有的人不看好跨平台,但是它存在的场景确实有它关键的价值。

也许某些领域我的认识不是很深,但是在需要的时候我可以动手满足需求,甚至去深入探索一下,而我也有自己精通的领域,二次并不冲突。

当然你说我只想在某个平台深入研究有没有问题?那肯定没有问题,这是好事,因为精通某个领域本来就是非常好的一件事

但是!对,我还是要说这个但是,因为很多时候精通某项技术,是需要业务场景去验证和推进的,如果不是大体量的业务场景,没有经历过各种极端的考验,很多时候所谓的精通只是表层精通。

为什么说这个?因为在交流过程中经常有一些人说:想要深入xxx去精通某项技术或者领域,但是最终还是“三过门而不入”。

最后的最后,我想说学习其实本身是一件长期投资的事情,我们追求“性价比”和“高回报”很正常,但是这和投资金融一样,如果一直想要通过“投机”来获利,那就要有做好韭菜的准备。

<think>我们正在讨论的是在Flutter混合开发中实现Flutter原生(Android/iOS)双向通信的方法。根据引用[1][2][4][5],Flutter提供了三种通信机制:MethodChannel、BasicMessageChannel和EventChannel。下面将详细说明如何实现双向通信,特别是使用MethodChannel(最常用)和BasicMessageChannel。 ### 一、双向通信的核心:Channel机制 Flutter原生通信基于**平台通道(Platform Channel)**,其工作原理如下: 1. Flutter端创建通道并发送消息 2. 原生端监听相同名称的通道 3. 消息通过编解码器转换为二进制传输 4. 接收端处理并返回结果(双向) ```dart // Flutter端通道创建 const channel = MethodChannel('com.example/app'); ``` ### 二、MethodChannel 双向通信实现 #### 场景:Flutter调用原生方法获取设备信息,原生主动通知Flutter网络状态变化 #### 步骤1:Flutter端代码 ```dart // 1. 创建MethodChannel const _methodChannel = MethodChannel('com.example/app'); // 2. 调用原生方法(Flutter→原生) Future<String> getDeviceModel() async { try { return await _methodChannel.invokeMethod('getDeviceModel'); } on PlatformException catch (e) { return "Failed: ${e.message}"; } } // 3. 设置原生调用Flutter的回调(原生→Flutter) @override void initState() { super.initState(); _methodChannel.setMethodCallHandler((call) async { switch (call.method) { case 'networkStatusChanged': setState(() => _networkStatus = call.arguments); return true; default: throw MissingPluginException(); } }); } ``` #### 步骤2:Android端(Kotlin) ```kotlin class MainActivity : FlutterActivity() { private val CHANNEL = "com.example/app" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // 注册通道 MethodChannel(flutterEngine.dartExecutor, CHANNEL).apply { // 处理Flutter调用 setMethodCallHandler { call, result -> when (call.method) { "getDeviceModel" -> { result.success(Build.MODEL) // 返回设备型号 } else -> result.notImplemented() } } // 主动通知Flutter(模拟网络变化) Handler(Looper.getMainLooper()).postDelayed({ invokeMethod("networkStatusChanged", "WiFi", object : MethodChannel.Result { override fun success(p0: Any?) { /* 成功 */ } override fun error(p0: String, p1: String?, p2: Any?) { /* 失败 */ } override fun notImplemented() {} }) }, 5000) } } } ``` #### 步骤3:iOS端(Swift) ```swift @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: "com.example/app", binaryMessenger: controller.binaryMessenger) // 处理Flutter调用 channel.setMethodCallHandler { call, result in switch call.method { case "getDeviceModel": result(UIDevice.current.model) // 返回设备型号 default: result(FlutterMethodNotImplemented) } } // 主动通知Flutter(模拟网络变化) DispatchQueue.main.asyncAfter(deadline: .now() + 5) { channel.invokeMethod("networkStatusChanged", arguments: "4G") } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } ``` ### 三、BasicMessageChannel 双向通信实现 #### 场景:实时传递用户输入(适合连续数据流) #### Flutter端 ```dart // 创建通道(使用String编解码器) final _messageChannel = BasicMessageChannel<String>( 'com.example/message', StringCodec(), ); // 发送消息到原生 _messageChannel.send('Hello from Flutter!'); // 监听原生消息 _messageChannel.setMessageHandler((message) async { print('Received: $message'); return null; // 可返回响应 }); ``` #### Android端(Kotlin) ```kotlin val messageChannel = BasicMessageChannel<String>( flutterEngine.dartExecutor, "com.example/message", StringCodec.INSTANCE ) // 接收Flutter消息 messageChannel.setMessageHandler { message, reply -> Log.d("MSG", "Received: $message") reply.reply("Android received: $message") // 回复 } // 主动发送消息 messageChannel.send("Hello from Android") { reply -> Log.d("MSG", "Flutter replied: $reply") } ``` #### iOS端(Swift) ```swift let messageChannel = FlutterBasicMessageChannel( name: "com.example/message", binaryMessenger: controller.binaryMessenger, codec: FlutterStringCodec.sharedInstance() ) // 接收Flutter消息 messageChannel.setMessageHandler { message, reply in print("Received: \(message ?? "")") reply("iOS received: \(message ?? "")") } // 主动发送消息 messageChannel.sendMessage("Hello from iOS") { reply in print("Flutter replied: \(reply ?? "")") } ``` ### 四、通信数据类型支持 所有Channel支持相同的数据类型[^5]: | Dart 类型 | Android 类型 | iOS 类型 | |--------------------|-------------------|------------------| | null | null | nil | | bool | java.lang.Boolean | NSNumber(bool) | | int | java.lang.Integer | NSNumber(int) | | double | java.lang.Double | NSNumber(double) | | String | java.lang.String | NSString | | Uint8List | byte[] | FlutterStandardTypedData(bytes: ) | | Int32List | int[] | FlutterStandardTypedData(int32: )| | Int64List | long[] | FlutterStandardTypedData(int64: )| | Float64List | double[] | FlutterStandardTypedData(float64: )| | List | java.util.ArrayList | NSArray | | Map | java.util.HashMap | NSDictionary | ### 五、混合开发最佳实践 1. **通道命名规范**:使用反向域名格式(如 `com.company.app/channel_name`) 2. **线程安全**: - Android端确保在主线程更新UI - iOS端使用`DispatchQueue.main.async` 3. **错误处理**: ```dart try { await channel.invokeMethod('method'); } on PlatformException catch (e) { print("Error: ${e.message}, code: ${e.code}"); } ``` 4. **释放资源**:在`dispose`中取消监听 ```dart @override void dispose() { _channel.setMethodCallHandler(null); super.dispose(); } ``` ### 六、调试技巧 1. **检查通道注册**: ```dart debugPrint(services.defaultBinaryMessenger .checkMessageHandler('com.example/app')?.toString()); // 输出:Handler exists? true ``` 2. **原生端断点**:在Android Studio/Xcode中设置断点监听通道调用 3. **日志追踪**: ```kotlin // Android MethodChannel(flutterEngine.dartExecutor, CHANNEL).apply { setMethodCallHandler { call, result -> Log.d("CHANNEL", "Received: ${call.method}") } } ``` > **注意**:混合开发中若使用`FlutterFragment`,需通过`getFlutterEngine()`获取正确引擎[^1][^3] ### 常见问题解决方案 | 问题 | 解决方法 | |-----------------------|--------------------------------------------------------------------------| | 调用无响应 | 检查两端通道名称是否完全一致(包括大小写) | | 数据类型不匹配 | 使用`call.arguments as Map`强制转换或检查编解码器 | | 原生调用Flutter失败 | 确保Flutter端已调用`setMethodCallHandler` | | 热重载后通信中断 | 原生修改后需完全重启应用(非热重载) | | 跨引擎通信 | 多Flutter实例时使用`FlutterEngineGroup`[^1] | ### 总结流程图 ```mermaid graph LR A[Flutter调用] -->|MethodChannel| B(原生处理) B --> C{执行操作} C --> D[返回结果] --> A E[原生事件] -->|BasicMessageChannel| F(Flutter处理) F --> G[更新状态] --> E ``` 通过以上方法,可实现Flutter原生平台的高效双向通信。实际开发中,推荐: - **简单调用**:使用`MethodChannel` - **数据流**:使用`EventChannel` - **频繁消息**:使用`BasicMessageChannel` --- ### §§相关问题§§ 1. 当 Flutter 插件依赖原生 SDK 时如何正确处理依赖冲突? 2. 如何为 Flutter 插件编写跨平台(Android/iOS)的单元测试? 3. 在 FlutterBoost 架构下如何处理原生导航栈 Flutter 路由的映射?</think>在 Flutter 混合开发中实现 Flutter 原生双向通信,主要通过 Flutter 提供的 **Channel 通信机制**实现。以下是详细实现方法和步骤: --- ### 一、三种通信方式对比 | **通道类型** | **通信方向** | **适用场景** | 特点 | |--------------------|-------------|--------------------------------------|--------------------------| | **MethodChannel** | 双向 | 方法调用(请求-响应) | 最常用,支持异步返回值 | | **BasicMessageChannel** | 双向 | 基础数据传递(字符串/二进制) | 简单数据流,无固定格式 | | **EventChannel** | 单向 | 原生→Flutter 事件流(如传感器数据) | 原生主动推送,Flutter监听 | --- ### 二、MethodChannel 双向通信实现(推荐) #### 场景:Flutter 调用原生方法获取设备信息,原生主动通知 Flutter 网络状态变化 #### 步骤 1:Flutter 端代码 ```dart // 1. 创建 MethodChannel const _methodChannel = MethodChannel('com.example/app'); // 2. 调用原生方法(Flutter → 原生) Future<String> getDeviceModel() async { try { return await _methodChannel.invokeMethod('getDeviceModel'); } on PlatformException catch (e) { return "Failed: ${e.message}"; } } // 3. 设置原生调用 Flutter 的回调(原生 → Flutter) @override void initState() { super.initState(); _methodChannel.setMethodCallHandler((call) async { switch (call.method) { case 'networkStatusChanged': setState(() => _networkStatus = call.arguments); return true; default: throw MissingPluginException(); } }); } ``` #### 步骤 2:Android 端(Kotlin) ```kotlin class MainActivity : FlutterActivity() { private val CHANNEL = "com.example/app" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // 注册通道 MethodChannel(flutterEngine.dartExecutor, CHANNEL).apply { // 处理 Flutter 调用 setMethodCallHandler { call, result -> when (call.method) { "getDeviceModel" -> { result.success(Build.MODEL) // 返回设备型号 } else -> result.notImplemented() } } // 主动通知 Flutter(模拟网络变化) Handler(Looper.getMainLooper()).postDelayed({ invokeMethod("networkStatusChanged", "WiFi", object : MethodChannel.Result { override fun success(p0: Any?) { /* 成功 */ } override fun error(p0: String, p1: String?, p2: Any?) { /* 失败 */ } override fun notImplemented() {} }) }, 5000) } } } ``` #### 步骤 3:iOS 端(Swift) ```swift @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: "com.example/app", binaryMessenger: controller.binaryMessenger) // 处理 Flutter 调用 channel.setMethodCallHandler { call, result in switch call.method { case "getDeviceModel": result(UIDevice.current.model) // 返回设备型号 default: result(FlutterMethodNotImplemented) } } // 主动通知 Flutter(模拟网络变化) DispatchQueue.main.asyncAfter(deadline: .now() + 5) { channel.invokeMethod("networkStatusChanged", arguments: "4G") } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } ``` --- ### 三、BasicMessageChannel 双向通信实现 #### 场景:实时传递用户输入(适合连续数据流) #### Flutter 端 ```dart // 创建通道(使用 String 编解码器) final _messageChannel = BasicMessageChannel<String>( 'com.example/message', StringCodec(), ); // 发送消息到原生 _messageChannel.send('Hello from Flutter!'); // 监听原生消息 _messageChannel.setMessageHandler((message) async { print('Received: $message'); return null; // 可返回响应 }); ``` #### Android 端(Kotlin) ```kotlin val messageChannel = BasicMessageChannel<String>( flutterEngine.dartExecutor, "com.example/message", StringCodec.INSTANCE ) // 接收 Flutter 消息 messageChannel.setMessageHandler { message, reply -> Log.d("MSG", "Received: $message") reply.reply("Android received: $message") // 回复 } // 主动发送消息 messageChannel.send("Hello from Android") { reply -> Log.d("MSG", "Flutter replied: $reply") } ``` #### iOS 端(Swift) ```swift let messageChannel = FlutterBasicMessageChannel( name: "com.example/message", binaryMessenger: controller.binaryMessenger, codec: FlutterStringCodec.sharedInstance() ) // 接收 Flutter 消息 messageChannel.setMessageHandler { message, reply in print("Received: \(message ?? "")") reply("iOS received: \(message ?? "")") } // 主动发送消息 messageChannel.sendMessage("Hello from iOS") { reply in print("Flutter replied: \(reply ?? "")") } ``` --- ### 四、关键注意事项 1. **通道命名一致性**: - Flutter 原生端的通道名称必须**完全一致**(大小写敏感) - 推荐格式:`com.company.app/channel_name` 2. **线程安全**: - Android 端确保在主线程更新 UI: ```kotlin runOnUiThread { updateUI() } ``` - iOS 使用主队列: ```swift DispatchQueue.main.async { updateUI() } ``` 3. **数据类型支持**: | Dart 类型 | Android 类型 | iOS 类型 | |--------------------|-------------------|------------------| | String | String | NSString | | int/double | Integer/Double | NSNumber | | bool | Boolean | NSNumber(bool) | | List | ArrayList | NSArray | | Map | HashMap | NSDictionary | | ByteData | byte[] | NSData | 4. **混合开发特殊处理**: - 使用 `FlutterFragment` 时传递正确的引擎 ID[^1]: ```java FlutterFragment.withCachedEngine("engine_id").build() ``` - 在 `Application` 中预初始化引擎: ```kotlin FlutterEngine(this).also { it.dartExecutor.executeDartEntrypoint() FlutterEngineCache.getInstance().put("engine_id", it) } ``` --- ### 五、调试技巧 1. **检查通道注册**: ```dart // 打印已注册的通道 debugPrint(WidgetsBinding.instance.defaultBinaryMessenger .checkMessageHandler('com.example/app').toString()); ``` 2. **错误处理**: ```dart try { await channel.invokeMethod('method'); } on PlatformException catch (e) { print("Error: ${e.message}, code: ${e.code}"); } ``` 3. **原生端日志**: - Android:`Log.d("CHANNEL", "Received: ${call.method}")` - iOS:`print("Received method: \(call.method)")` > **提示**:90% 的通信问题源于**通道名不一致**或**原生未注册处理程序**,请优先检查这两点[^2][^5]。 --- ### 六、通信流程图 ```mermaid sequenceDiagram Flutter->>Native: 1. invokeMethod('getDeviceModel') activate Native Native-->>Flutter: 2. 返回设备型号 deactivate Native Native->>Flutter: 3. 主动发送事件(networkStatusChanged) Flutter-->>Native: 4. 处理事件并更新UI ``` 通过以上方法,可实现稳定高效的双向通信。实际开发中推荐使用 **MethodChannel** 处理大多数场景,**BasicMessageChannel** 适用于数据流传输[^2][^4]。 --- ###
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值