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.pswp.cn/pingmian/92567.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92567.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字图像处理3

图像线性滤波——目的就是滤去噪声&#xff0c;但是边缘会模糊&#xff0c;整体也模糊线性&#xff1a;邻域平均法&#xff08;4邻域平均和8邻域平均&#xff09;用当前运算点所在邻域的平均值来代替该点的平均值im_for_read"D:\AAAproject\PYproject\EXPERuse\zaosheng.j…

Linux发行版分类与Centos替代品

让centos7气的不轻&#xff0c;这玩意儿太老了&#xff0c;什么都不好配置。 目录Linux 发行版的大致分类1. Red Hat 系列&#xff08;RPM 系&#xff09;2. Debian 系列&#xff08;DEB 系&#xff09;3. Arch 系列4. SUSE 系列CentOS 7 的替代品推荐AlmaLinux 和 Rocky Linux…

大语言模型提示工程与应用:大语言模型对抗性提示安全防御指南

对抗性提示工程 学习目标 理解大语言模型中对抗性提示的风险与防御机制&#xff0c;掌握提示注入、提示泄露和越狱攻击的检测方法&#xff0c;培养安全防护意识。 相关知识点 对抗性攻击类型防御技术 学习内容 1 对抗性攻击类型 1.1 提示注入 提示注入旨在通过使用巧妙…

避不开的数据拷贝(2)

接着上周未完的话题 避不开的数据拷贝。 既然处理器是通用机器&#xff0c;就没有专属数据&#xff0c;所以数据都要从别处调来&#xff0c;这就涉及到了数据搬运&#xff0c;就有了外设的概念。由于不同外设和处理器一起共享数据存储&#xff0c;时间会花在两方面&#xff1a…

娃哈哈经销商“大洗牌”:砍掉年销300万以下经销商

文 | 大力财经据第一财经报道&#xff0c;娃哈哈在宗馥莉“铁腕”策略推动下&#xff0c;正经历经销商体系的重大变革&#xff0c;陆续砍掉年销低于300万元的经销商&#xff0c;方式有时颇为激进&#xff0c;“一刀切”的做法引发诸多争议&#xff0c;部分经销商反馈存在款项未…

drippingblues靶机通关练习笔记

前言 将靶机导入到vmware虚拟机上 靶机下载地址&#xff1a;https://download.vulnhub.com/drippingblues/drippingblues.ova 将网段都设置为nat 信息收集 ip端口扫描 netdiscover -r 192.168.25.1/24 --确定ip nmap -A -p- 192.168.25. kalid的ip&#xff1a;1…

QT第三讲- 机制、宏、类库模块

文章目录 🧩 一、Qt核心机制与类库 🔧 1. 元对象系统(Meta-Object System) ⚡ 2. 信号与槽(Signals & Slots) • 通信机制 📦 3. 属性系统(Property System) 动态属性 例程 类的附加信息 Q_CLASSINFO 例程 🌐 二、全局定义与容器 📝 1. 全局数据类型与函数…

(LeetCode 每日一题) 869. 重新排序得到 2 的幂 (哈希表+枚举)

题目&#xff1a;869. 重新排序得到 2 的幂 思路&#xff1a;哈希表枚举。先预处理出所有的2的幂数&#xff0c;用哈希表来存储。 C版本&#xff1a; class Solution { public:// 哈希表存储所有 2的幂数 按升序排列的形式unordered_set<string> st;// 预处理出所有的2…

WebAssembly技术详解:从浏览器到云原生的高性能革命

引言&#xff1a;WebAssembly的诞生与使命 2015年&#xff0c;当Mozilla、Google、Microsoft和Apple四大浏览器厂商联合发布WebAssembly&#xff08;Wasm&#xff09;技术预览时&#xff0c;业界尚未意识到这将开启Web性能的新纪元。作为继HTML、CSS、JavaScript之后的第四种We…

性能解析案例

异步io是内核fd与应用程序直接的关系io 多路复用1.检测io是否就绪2.read/write消息队列kafka&#xff1a;1.典型应用 &#xff1a;异步处理&#xff0c;系统解耦&#xff0c;流量削峰&#xff0c;日志处理2.核心原理&#xff1a;kafka体系结构以及读写流程3.具体操作&#xff1…

青龙峡拔韭菜

我们一年四季&#xff0c;除了冬天不往山里进&#xff0c;其余季节&#xff0c;只要天气允许&#xff0c;我们都会进山。在山里拔韭菜&#xff0c;是我们百做不烦的一件事。今年大旱&#xff0c;从五月份上山找韭菜&#xff0c;没有如愿。直到入秋后&#xff0c;我们再次去青龙…

5、docker镜像管理命令

1、命令总览命令&#xff08;含关键参数&#xff09;作用出现频率备注docker buildx build --platform … -t … --push .一次构建并推送多平台镜像高频需先 docker buildx create --usedocker buildx build -o typedocker,destxxx.tar .构建后离线导出 tar 包中频只导出单平台…

阿里云ECS云服务器临时升级带宽方法

阿里云ECS云服务器临时升级带宽方法一、背景与需求二、原理三、操作步骤步骤 0: 准备工作步骤 1: 创建弹性网卡 (ENI)步骤 2: 创建并绑定弹性公网IP (EIP)步骤 3: SSH登录ECS并切换到高速通道 (eth1)步骤 4: 执行你的高带宽任务步骤 5: 任务完成&#xff0c;切回默认网卡 (eth0…

Java语言简介

一.Java语言的起源 Java语言的前身是Oka语言,是美国Sun Microsystems公司于1991年推出的,仅限于公司内部使用的语言。1995年,Sun公司将Oak语言更名为Java语言,并正式向公众推出。这之后,Java语言不断更新,其类库越来越丰富,性能逐步提升,应用领域也显著拓展,已成为当今…

VUE+SPRINGBOOT从0-1打造前后端-前后台系统-视频列表与视频播放

在现代Web开发中&#xff0c;视频播放功能已成为许多网站的基本需求。本文将基于Vue.js框架&#xff0c;详细讲解如何实现一个视频列表与播放器交互的功能模块。这个组件可以让用户点击列表中的视频项来播放对应的视频&#xff0c;并支持再次点击关闭播放器。功能概述我们实现的…

详解 Seaborn:让数据可视化更简单高效的 Python 库

在数据科学领域&#xff0c;可视化是理解数据、挖掘规律的重要手段。今天要为大家介绍的 Seaborn 库&#xff0c;正是数据可视化领域的一把 “利器”。它基于 Matplotlib 开发&#xff0c;却凭借更简洁的接口和更美观的默认样式&#xff0c;成为众多数据分析师的首选工具。下面…

Cesium1.95中如何高效管理 1500 个高频实体

一、建议&#xff1a;不要频繁创建/销毁&#xff0c;而是复用对象&#xff1b;​​​​使用 CallbackProperty更新位置而不是删了重建&#xff1b;​​​​对大量 Billboard / Polyline / Label&#xff0c;优先使用对应的 *Collection&#xff0c;然后批量更新&#xff1b;​​…

全面了解机器语言之kmeans

深入理解 KMeans 聚类算法&#xff1a;原理、实现与应用在机器学习领域&#xff0c;聚类算法作为无监督学习的核心技术之一&#xff0c;一直以来都是数据挖掘和模式识别的重要工具。其中&#xff0c;KMeans 算法以其简洁的原理、高效的计算性能和广泛的适用性&#xff0c;成为最…

纳米陶瓷与光子集成:猎板PCB定义下一代VR硬件的技术蓝图

虚拟现实&#xff08;VR&#xff09;设备正从“视觉沉浸”向“多感官无感交互”演进&#xff0c;其底层PCB技术面临带宽、算力密度与动态可靠性的三重挑战。作为国内高端PCB技术的引领者&#xff0c;​​猎板PCB​​以材料革新、光电子融合与智能响应为核心&#xff0c;构建了适…

Linux ssh-keygen系列命令与ssh命令的使用

关联文章 Linux ssh 免密登录配置&#x1f44d;对日开发 TeraTerm 批量向各台服务器传输文件SSH 教程&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d; 目录一. ssh-keygen相关命令1.1 简介1.2 生成密钥1.3 ssh-copy-id 上传公钥到指定的服务…