当前位置: 首页 > news >正文

Android 原生与 Flutter 通信完整实现 (Kotlin 版)

1. 项目配置

pubspec.yaml 添加依赖

dependencies:flutter:sdk: flutterprovider: ^6.0.5

2. Flutter 端实现

状态管理类

// settings_provider.dart
import 'package:flutter/foundation.dart';class SettingsProvider with ChangeNotifier {String _themeColor = 'blue';bool _darkMode = false;String get themeColor => _themeColor;bool get darkMode => _darkMode;void updateSettings(String color, bool dark) {_themeColor = color;_darkMode = dark;notifyListeners();}
}

主界面

// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';void main() {runApp(ChangeNotifierProvider(create: (_) => SettingsProvider(),child: const MyApp(),),);
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(home: const MainPage(),);}
}class MainPage extends StatefulWidget {const MainPage({super.key});State<MainPage> createState() => _MainPageState();
}class _MainPageState extends State<MainPage> {static const platform = MethodChannel('com.example.app/settings');void initState() {super.initState();_initMethodChannel();}void _initMethodChannel() {platform.setMethodCallHandler((call) async {if (call.method == 'updateSettings') {final args = call.arguments as Map<dynamic, dynamic>;Provider.of<SettingsProvider>(context, listen: false).updateSettings(args['color'] as String,args['darkMode'] as bool,);}return null;});}Widget build(BuildContext context) {final settings = Provider.of<SettingsProvider>(context);return Scaffold(appBar: AppBar(title: const Text('主界面'),backgroundColor: _getColor(settings.themeColor),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text('当前主题色: ${settings.themeColor}',style: TextStyle(color: settings.darkMode ? Colors.white : Colors.black,),),const SizedBox(height: 20),Text('暗黑模式: ${settings.darkMode ? "开启" : "关闭"}',style: TextStyle(color: settings.darkMode ? Colors.white : Colors.black,),),const SizedBox(height: 40),ElevatedButton(onPressed: _openNativeSettings,child: const Text('打开原生设置页面'),),],),),backgroundColor: settings.darkMode ? Colors.grey[800] : Colors.white,);}Color _getColor(String colorName) {return switch (colorName) {'red' => Colors.red,'green' => Colors.green,_ => Colors.blue,};}Future<void> _openNativeSettings() async {try {final settings = Provider.of<SettingsProvider>(context, listen: false);await platform.invokeMethod('openSettings', {'color': settings.themeColor,'darkMode': settings.darkMode,});} on PlatformException catch (e) {debugPrint("打开设置失败: ${e.message}");}}
}

3. Android 原生端实现 (Kotlin)

MainActivity.kt

package com.example.myappimport android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannelclass MainActivity : FlutterActivity() {private val CHANNEL = "com.example.app/settings"private var latestSettings: Map<String, Any>? = nulloverride fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->when (call.method) {"openSettings" -> {val color = call.argument<String>("color") ?: "blue"val darkMode = call.argument<Boolean>("darkMode") ?: falseIntent(this, SettingsActivity::class.java).apply {putExtra("color", color)putExtra("darkMode", darkMode)startActivityForResult(this, 101)}result.success(null)}else -> result.notImplemented()}}}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == 101 && resultCode == RESULT_OK) {data?.extras?.let { extras ->val settings = mapOf("color" to extras.getString("color", "blue"),"darkMode" to extras.getBoolean("darkMode", false))latestSettings = settingsHandler(Looper.getMainLooper()).post {MethodChannel(flutterEngine?.dartExecutor, CHANNEL).invokeMethod("updateSettings", settings)}}}}
}

SettingsActivity.kt

package com.example.myappimport android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.databinding.ActivitySettingsBindingclass SettingsActivity : AppCompatActivity() {private lateinit var binding: ActivitySettingsBindingprivate var selectedColor = "blue"private var darkMode = falseoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivitySettingsBinding.inflate(layoutInflater)setContentView(binding.root)// 获取初始值selectedColor = intent.getStringExtra("color") ?: "blue"darkMode = intent.getBooleanExtra("darkMode", false)// 初始化UI状态when (selectedColor) {"red" -> binding.colorGroup.check(R.id.red)"green" -> binding.colorGroup.check(R.id.green)else -> binding.colorGroup.check(R.id.blue)}binding.darkModeSwitch.isChecked = darkMode// 保存按钮点击binding.saveButton.setOnClickListener {selectedColor = when (binding.colorGroup.checkedRadioButtonId) {R.id.red -> "red"R.id.green -> "green"else -> "blue"}darkMode = binding.darkModeSwitch.isCheckedIntent().apply {putExtra("color", selectedColor)putExtra("darkMode", darkMode)setResult(RESULT_OK, this)}finish()}}
}

activity_settings.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="主题颜色"android:textSize="18sp"/><RadioGroupandroid:id="@+id/colorGroup"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/red"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="红色"/><RadioButtonandroid:id="@+id/green"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="绿色"android:layout_marginStart="16dp"/><RadioButtonandroid:id="@+id/blue"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="蓝色"android:layout_marginStart="16dp"/></RadioGroup><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_marginTop="24dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="暗黑模式"android:textSize="18sp"/><Switchandroid:id="@+id/darkModeSwitch"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="16dp"/></LinearLayout><Buttonandroid:id="@+id/saveButton"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="保存设置"android:layout_marginTop="32dp"/>
</LinearLayout>

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.myapp"><applicationandroid:label="MyApp"android:icon="@mipmap/ic_launcher"><activityandroid:name=".MainActivity"android:exported="true"android:launchMode="singleTop"android:theme="@style/LaunchTheme"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:hardwareAccelerated="true"android:windowSoftInputMode="adjustResize"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name=".SettingsActivity"android:theme="@style/Theme.AppCompat.Light.DialogWhenLarge"android:exported="false" /></application>
</manifest>

4. 关键点总结

  1. 通信流程:

    • Flutter → 通过 MethodChannel 调用原生方法
    • Android → 通过 startActivityForResult 启动设置页
    • 设置页 → 返回结果通过 MethodChannel 传回Flutter
  2. Kotlin特性利用:

    • 使用 lateinit 延迟初始化绑定
    • 使用 apply 简化对象配置
    • 使用 when 表达式替代 switch-case
  3. 类型安全:

    • 使用Kotlin的空安全操作符 ?
    • 明确指定泛型类型 Map<String, Any>
  4. 线程安全:

    • 通过 Handler(Looper.getMainLooper()) 确保在主线程更新UI
  5. 资源管理:

    • 使用 View Binding (ActivitySettingsBinding)
    • 合理处理 Activity 生命周期

这个实现完整展示了 Flutter 与 Android 原生页面之间的双向通信,所有代码均采用 Kotlin 编写,符合现代 Android 开发最佳实践。

http://www.lryc.cn/news/612904.html

相关文章:

  • JPA 分页查询与条件分页查询
  • 《深入理解 WSGI:解锁 Python Web 应用背后的奥秘》
  • Java+Vue合力开发固定资产条码管理系统,移动端+后台管理,集成资产录入、条码打印、实时盘点等功能,助力高效管理,附全量源码
  • 前端性能优化:从请求到资源的精细调控
  • Event Stream输出优化:Vue3节流函数的正确实现
  • 【大前端】vite忽略指定前缀的静态资源
  • 【插件式微服务架构系统分享】之 解耦至上:gateway 网关与APISIX 网关的不同分工
  • 一文解读“Performance面板”前端性能优化工具基础用法!
  • SpringAI
  • 数据结构---循环队列(补充 应用实例)、哈希表(哈希存储、哈希冲突、解决方法、举例实现)
  • Linux Docker 新手入门:一文学会配置镜像加速器
  • 躺平发育小游戏微信抖音流量主小程序开源
  • 透明矿山:科技重塑矿业未来
  • Numpy科学计算与数据分析:Numpy随机数生成入门
  • 光纤滑环 – 光纤旋转接头(FORJ)- 杭州驰宏科技
  • AutoMQ-Kafka的替代方案实战
  • QML与C++交互的方式
  • Kafka数据生产和发送
  • 基于Spring Cloud Stream与Kafka的事件驱动微服务架构设计与实战指南
  • 【Kafka系列】第二篇| Kafka 的核心概念、架构设计、底层原理
  • MQTT:Dashboard访问授权
  • MQTT:Dashboard黑名单与连接抖动
  • 【LeetCode】set和map相关算法题 前K个高频单词、随机链表的复制、两个数组的交集、环形链表
  • Flink-1.19.0源码详解9-ExecutionGraph生成-后篇
  • VScode使用jupyter notebook,配置内核报错没有torch解决
  • 贪心算法分析与解决指南
  • 1.电动汽车动力电池系统技术介绍与分类
  • 机器视觉系统工业相机的成像原理及如何选型
  • OpenCV图像处理入门实战指南
  • 为什么需要日志收集系统