news 2026/6/17 8:36:31

在 Flutter 混合开发中,Android 原生层通知 Dart 界面更新状态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 Flutter 混合开发中,Android 原生层通知 Dart 界面更新状态

在 Flutter 混合开发中,Android 原生层通知 Dart 界面更新状态,主要通过MethodChannelinvokeMethod从原生调用 Dart,或者使用EventChannel建立持续的数据流。


方案一:MethodChannel 单向通知(简单场景)

Android 原生层调用 Dart

// MainActivity.kt 或 Service.ktimportio.flutter.plugin.common.MethodChannelclassMainActivity:FlutterActivity(){privatelateinitvarchannel:MethodChanneloverridefunconfigureFlutterEngine(flutterEngine:FlutterEngine){super.configureFlutterEngine(flutterEngine)channel=MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"flutter_launcher")// Dart 调用原生的处理器channel.setMethodCallHandler{call,result->when(call.method){"getSystemInfo"->result.success(getSystemInfo())else->result.notImplemented()}}}/** * 主动通知 Dart 更新(例如:电池变化、应用安装/卸载) */funnotifyDart(event:String,data:Map<String,Any>){// 在主线程调用runOnUiThread{channel.invokeMethod(event,data)}}/** * 示例:电池变化时通知 Dart */funonBatteryChanged(level:Int){notifyDart("onBatteryChanged",mapOf("level"tolevel))}/** * 示例:应用安装/卸载时通知 Dart */funonAppChanged(action:String,packageName:String){notifyDart("onAppChanged",mapOf("action"toaction,// "installed" | "uninstalled" | "updated""package"topackageName))}}

Dart 层接收通知

// lib/services/desktop_service.dartimport'dart:async';import'package:flutter/services.dart';classDesktopService{staticconstMethodChannel_channel=MethodChannel('flutter_launcher');// 状态流控制器staticfinal_batteryController=StreamController<int>.broadcast();staticfinal_appChangeController=StreamController<Map<String,dynamic>>.broadcast();// 公开的数据流staticStream<int>getbatteryStream=>_batteryController.stream;staticStream<Map<String,dynamic>>getappChangeStream=>_appChangeController.stream;staticvoidinit(){// 设置方法调用处理器,接收原生通知_channel.setMethodCallHandler(_handleMethodCall);}staticFuture<<dynamic>_handleMethodCall(MethodCallcall)async{switch(call.method){case'onBatteryChanged':finallevel=call.arguments['level']asint?;if(level!=null){_batteryController.add(level);}returnnull;case'onAppChanged':finaldata=call.argumentsasMap<<dynamic,dynamic>;_appChangeController.add({'action':data['action'],'package':data['package'],});returnnull;case'onTimeTick':// 每分钟通知returnnull;case'onWifiChanged':finalenabled=call.arguments['enabled']asbool?;// 通知WiFi状态变化returnnull;default:returnnull;}}// 主动调用原生方法staticFuture<Map<String,dynamic>>getSystemInfo()async{finalresult=await_channel.invokeMethod('getSystemInfo');return_toStringMap(result);}staticFuture<void>launchApp(Stringpackage)async{await_channel.invokeMethod('launchApp',{'package':package});}staticMap<String,dynamic>_toStringMap(dynamicdata){if(data==null)return{};finalmap=dataasMap<<dynamic,dynamic>;returnmap.map((k,v)=>MapEntry(k.toString(),v));}staticvoiddispose(){_batteryController.close();_appChangeController.close();}}

UI 层监听状态变化

// lib/widgets/status_bar.dartimport'package:flutter/material.dart';import'../services/desktop_service.dart';classStatusBarextendsStatefulWidget{constStatusBar({super.key});@overrideState<<StatusBar>createState()=>_StatusBarState();}class_StatusBarStateextendsState<<StatusBar>{int _battery=100;bool _wifi=false;String_time='';@overridevoidinitState(){super.initState();// 初始化服务DesktopService.init();// 监听电池变化(来自原生通知)DesktopService.batteryStream.listen((level){setState(()=>_battery=level);});// 定时刷新时间Stream.periodic(constDuration(seconds:1)).listen((_){setState((){_time='${DateTime.now().hour.toString().padLeft(2, '0')}:''${DateTime.now().minute.toString().padLeft(2, '0')}';});});// 初始加载_loadSystemInfo();}Future<void>_loadSystemInfo()async{finalinfo=awaitDesktopService.getSystemInfo();setState((){_battery=info['battery']??100;_wifi=info['wifi']??false;});}@overrideWidgetbuild(BuildContextcontext){returnContainer(height:36,color:Colors.black.withOpacity(0.6),padding:constEdgeInsets.symmetric(horizontal:12),child:Row(children:[Text(_time,style:constTextStyle(color:Colors.white)),constSpacer(),Icon(_wifi?Icons.wifi:Icons.wifi_off,color:Colors.white,size:18),constSizedBox(width:8),Text('$_battery%',style:constTextStyle(color:Colors.white)),],),);}}

方案二:EventChannel 持续数据流(适合高频数据)

Android 原生层

// service/SystemEventChannel.ktpackagecom.example.flutter_launcher.serviceimportio.flutter.plugin.common.EventChannelimportio.flutter.plugin.common.EventChannel.EventSinkclassSystemEventChannel:EventChannel.StreamHandler{privatevareventSink:EventSink?=nulloverridefunonListen(arguments:Any?,events:EventSink?){eventSink=eventsstartListening()}overridefunonCancel(arguments:Any?){eventSink=nullstopListening()}/** * 发送事件到 Dart */funsendEvent(event:String,data:Any){eventSink?.success(mapOf("event"toevent,"data"todata))}privatefunstartListening(){// 注册系统监听器(电池、WiFi、时间等)}privatefunstopListening(){// 注销监听器}}

Dart 层

// lib/services/event_service.dartimport'dart:async';import'package:flutter/services.dart';classEventService{staticconstEventChannel_eventChannel=EventChannel('flutter_launcher/events');staticStream<Map<String,dynamic>>?_eventStream;staticStream<Map<String,dynamic>>geteventStream{_eventStream??=_eventChannel.receiveBroadcastStream().map((event){finalmap=eventasMap<<dynamic,dynamic>;return{'event':map['event'],'data':map['data'],};});return_eventStream!;}}

方案三:BroadcastReceiver + 通知(系统事件)

Android 原生层监听系统广播

// receiver/SystemEventReceiver.ktpackagecom.example.flutter_launcher.receiverimportandroid.content.BroadcastReceiverimportandroid.content.Contextimportandroid.content.Intentimportandroid.content.IntentFilterimportandroid.os.BatteryManagerclassSystemEventReceiver(privatevalonEvent:(String,Map<String,Any>)->Unit):BroadcastReceiver(){funregister(context:Context){valfilter=IntentFilter().apply{addAction(Intent.ACTION_BATTERY_CHANGED)addAction(Intent.ACTION_TIME_TICK)addAction(Intent.ACTION_TIME_CHANGED)addAction(Intent.ACTION_PACKAGE_ADDED)addAction(Intent.ACTION_PACKAGE_REMOVED)addAction(Intent.ACTION_PACKAGE_REPLACED)addAction("android.net.wifi.STATE_CHANGE")}context.registerReceiver(this,filter)}fununregister(context:Context){context.unregisterReceiver(this)}overridefunonReceive(context:Context,intent:Intent){when(intent.action){Intent.ACTION_BATTERY_CHANGED->{vallevel=intent.getIntExtra(BatteryManager.EXTRA_LEVEL,-1)valscale=intent.getIntExtra(BatteryManager.EXTRA_SCALE,-1)valpercent=(level*100/scale).coerceIn(0,100)onEvent("battery",mapOf("level"topercent))}Intent.ACTION_TIME_TICK,Intent.ACTION_TIME_CHANGED->{onEvent("time",mapOf("time"toSystem.currentTimeMillis()))}Intent.ACTION_PACKAGE_ADDED->{valpackageName=intent.data?.schemeSpecificPart?:""onEvent("app_added",mapOf("package"topackageName))}Intent.ACTION_PACKAGE_REMOVED->{valpackageName=intent.data?.schemeSpecificPart?:""onEvent("app_removed",mapOf("package"topackageName))}"android.net.wifi.STATE_CHANGE"->{valwifiManager=context.getSystemService(Context.WIFI_SERVICE)asandroid.net.wifi.WifiManageronEvent("wifi",mapOf("enabled"towifiManager.isWifiEnabled))}}}}

在 Service 或 Activity 中使用

classMainActivity:FlutterActivity(){privatelateinitvareventReceiver:SystemEventReceiverprivatelateinitvarchannel:MethodChanneloverridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)channel=MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger,"flutter_launcher")// 初始化广播接收器eventReceiver=SystemEventReceiver{event,data->// 通知 DartrunOnUiThread{channel.invokeMethod("onSystemEvent",mapOf("event"toevent,"data"todata))}}eventReceiver.register(this)}overridefunonDestroy(){eventReceiver.unregister(this)super.onDestroy()}}

完整状态更新流程图

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Android 系统 │ │ Android 原生层 │ │ Flutter Dart │ │ │ │ │ │ │ │ 电池变化 │────→│ BroadcastReceiver│────→│ MethodChannel │ │ 时间变化 │ │ 或 Service 监听 │ │ setMethodCallHandler │ 应用安装/卸载 │ │ │ │ │ │ WiFi状态变化 │ │ │ │ StreamController │ │ │ │ │ 通知 UI 更新 │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ↑ │ 主动查询 ┌────┴────┐ │ invokeMethod │ getSystemInfo └─────────┘

推荐用法总结

场景方案代码示例
单次状态查询MethodChannel 双向invokeMethod('getSystemInfo')
状态变化通知MethodChannel + StreaminvokeMethod('onBatteryChanged')StreamController
高频数据流EventChannel电池实时变化、传感器数据
系统广播事件BroadcastReceiver + MethodChannel应用安装卸载、时间变化
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 8:33:43

STM32温度传感器选型指南:DS18B20 vs LM335,实战中到底该怎么选?

STM32温度传感器选型指南&#xff1a;DS18B20与LM335的深度技术对决在嵌入式系统开发中&#xff0c;温度测量是仅次于时间显示的第二大高频需求。从智能家居的温控器到工业设备的过热保护&#xff0c;工程师们总需要面对同一个灵魂拷问&#xff1a;到底该选数字传感器还是模拟传…

作者头像 李华
网站建设 2026/6/8 16:52:32

小程序毕设选题推荐:基于springboot+微信小程序的钓鱼论坛小程序基于SpringBoot的同城钓鱼社交APP微信小程序【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/8 16:25:19

破解多组学数据困局:专属数据库如何为科研按下“加速键”

在高通量测序技术日新月异的今天&#xff0c;生命科学研究正以前所未有的速度产生着海量数据。基因组、转录组、蛋白质组、代谢组……不同层面的组学数据如同一块块拼图&#xff0c;散落在全球各异的数据库与文献海洋中。对于广大科研人员而言&#xff0c;如何从这些异构、分散…

作者头像 李华
网站建设 2026/6/8 16:16:43

怎么设置单个项目设置局部的git user.name

⏺ 在项目目录下执行&#xff1a; git config user.name “你的名字” git config user.email “你的邮箱” 不加 --global 就是局部配置&#xff0c;只对当前仓库生效&#xff0c;写入 .git/config。 验证&#xff1a; git config user.name ⏺ --list 显示了全局的 sunnystit…

作者头像 李华