做哪个网站好,游戏开发自学,阿里云网站用什么做的,ui设计需要学编程吗前言
文章主要讲解Flutter页面如何使用Android原生View#xff0c;但用到了Flutter 和 Android原生 相互通信知识#xff0c;建议先看完这篇讲解通信的文章
Flutter 与 Android原生 相互通信#xff1a;BasicMessageChannel、MethodChannel、EventChannel-CSDN博客
数据观…前言
文章主要讲解Flutter页面如何使用Android原生View但用到了Flutter 和 Android原生 相互通信知识建议先看完这篇讲解通信的文章
Flutter 与 Android原生 相互通信BasicMessageChannel、MethodChannel、EventChannel-CSDN博客
数据观察监听Flutter使用ValueNotifierAndroid原生使用LiveData在实体数据发生改变时自动刷新。 效果图 图解 1、Android原生端
1.0 PlatformView
AndroidComputeLayoutPlatform.kt
package com.example.flutter_mix_android.ui.flutterplugin.platform;import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.lifecycle.ViewModelProvider
import com.example.flutter_mix_android.R
import com.example.flutter_mix_android.bean.CountBean
import com.example.flutter_mix_android.databinding.LayoutComputeBinding
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.platform.PlatformView/*** 封装成PlatformView*/
class ComputeLayoutPlatform(context: Context,rootContext: Context,messenger: BinaryMessenger,viewId: Int,args: Any?,
) : FrameLayout(context), PlatformView, MethodChannel.MethodCallHandler {private lateinit var mChannel: MethodChannelprivate lateinit var bind: LayoutComputeBindingprivate lateinit var viewModel: CountBeancompanion object {// Android原生View 在Flutter引擎上注册的唯一标识在Flutter端使用时必须一样private const val ANDROID_SEND_FLUTTER_DATA_NOTICE: String androidSendFlutterDataNotice // Android端 向 Flutter端 发送数据private const val ANDROID_GET_FLUTTER_DATA_NOTICE: String androidGetFlutterDataNotice // Android端 获取 Flutter端 数据private const val FLUTTER_SEND_ANDROID_DATA_NOTICE: String flutterSendAndroidDataNotice // Flutter端 向 Android端 发送数据private const val FLUTTER_GET_ANDROID_DATA_NOTICE: String flutterGetAndroidDataNotice // Flutter端 获取 Android端 数据}init {initChannel(messenger, viewId)initView()initData(rootContext, args)}/*** 初始化消息通道*/private fun initChannel(messenger: BinaryMessenger, viewId: Int) {// 创建 Android端和Flutter端的相互通信的通道// 通道名称两端必须一致mChannel MethodChannel(messenger, flutter.mix.android/compute/$viewId)// 监听来自 Flutter端 的消息通道// Flutter端调用了函数这个handler函数就会被触发mChannel.setMethodCallHandler(this)}/*** 初始化视图*/private fun initView() {LayoutInflater.from(context).inflate(R.layout.layout_compute, this, true)bind LayoutComputeBinding.bind(getChildAt(0))bind.add.setOnClickListener {val count: Int viewModel.curNum.value ?: 0viewModel.curNum.value count 1}bind.androidSendFlutterData.setOnClickListener {androidSendFlutterData()}bind.androidGetFlutterData.setOnClickListener {androidGetFlutterData()}}/*** Android端 向 Flutter端 发送数据PUT 操作*/private fun androidSendFlutterData() {val map: MutableMapString, Int mutableMapOfString, Int()map[androidNum] viewModel.curNum.value ?: 0mChannel.invokeMethod(ANDROID_SEND_FLUTTER_DATA_NOTICE,map,object : MethodChannel.Result {override fun success(result: Any?) {Log.d(TAG, success$result)updateFlutterNum((result as? Int) ?: 0)}override fun error(errorCode: String,errorMessage: String?,errorDetails: Any?) {Log.d(TAG,errorCode$errorCode --- errorMessage$errorMessage --- errorDetails$errorDetails)}/*** Flutter端 未实现 Android端 定义的接口方法*/override fun notImplemented() {Log.d(TAG, notImplemented)}})}/*** Android端 获取 Flutter端 数据GET 操作*/private fun androidGetFlutterData() {// 说一个坑不传参数可以写null// 但不能这样写目前它没有这个重载方法invokeMethod第二个参数是Object类型所以编译器不会提示错误// mChannel.invokeMethod(ANDROID_GET_FLUTTER_DATA_NOTICE, object : MethodChannel.Result {// public void invokeMethod(NonNull String method, Nullable Object arguments)mChannel.invokeMethod(ANDROID_GET_FLUTTER_DATA_NOTICE,null,object : MethodChannel.Result {override fun success(result: Any?) {Log.d(TAG, success$result)updateGetFlutterNum((result as? Int) ?: 0)}override fun error(errorCode: String,errorMessage: String?,errorDetails: Any?) {Log.d(TAG,errorCode$errorCode --- errorMessage$errorMessage --- errorDetails$errorDetails)}/*** Flutter端 未实现 Android端 定义的接口方法*/override fun notImplemented() {Log.d(TAG, notImplemented)}})}/*** 初始化数据*/private fun initData(rootContext: Context, args: Any?) {val owner rootContext as FlutterFragmentActivityviewModel ViewModelProvider(owner)[CountBean::class.java]bind.countBean viewModelbind.lifecycleOwner owner// 获取初始化时 Flutter端 向 Android 传递的参数val map: MapString, Int args as MapString, IntviewModel.getFlutterNum.value map[flutterNum]}/*** 监听来自 Flutter端 的消息通道** call Android端 接收到 Flutter端 发来的 数据对象* resultAndroid端 给 Flutter端 执行回调的接口对象*/override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {// 获取调用函数的名称val methodName: String call.methodwhen (methodName) {FLUTTER_SEND_ANDROID_DATA_NOTICE - {// 回调结果对象// 获取Flutter端传过来的数据val flutterCount: Int? call.argumentInt(flutterNum)updateFlutterNum(flutterCount ?: 0)result.success(success)// 回调状态接口对象里面有三个回调方法// result.success(result: Any?)// result.error(errorCode: String, errorMessage: String?, errorDetails: Any?)// result.notImplemented()}FLUTTER_GET_ANDROID_DATA_NOTICE - {result.success(viewModel.curNum.value)}else - {result.notImplemented()}}}fun updateFlutterNum(flutterCount: Int) {viewModel.flutterNum.value flutterCount}fun updateGetFlutterNum(flutterCount: Int) {viewModel.getFlutterNum.value flutterCount}override fun getView(): View? {return this}override fun dispose() {// 解除绑定mChannel.setMethodCallHandler(null)}}1.1 PlatformViewFactory
AndroidComputeLayoutPlatformFactory.kt
package com.example.flutter_mix_android.ui.flutterplugin.factoryimport android.content.Context
import com.example.flutter_mix_android.ui.flutterplugin.platform.ComputeLayoutPlatform
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory/*** 通过PlatformView工厂创建PlatformView*/
class ComputeLayoutPlatformFactory(private val rootContext: Context,private val messenger: BinaryMessenger, // 二进制信使
) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { // 消息编解码器private lateinit var computeLayoutPlatform: ComputeLayoutPlatformoverride fun create(context: Context, viewId: Int, args: Any?): PlatformView {computeLayoutPlatform ComputeLayoutPlatform(context, rootContext, messenger, viewId, args)return computeLayoutPlatform}}
1.2 FlutterPlugin
AndroidFlutterPlugin.kt
package com.example.flutter_mix_android.ui.flutterplugin.plugin;import android.content.Context
import com.example.flutter_mix_android.ui.flutterplugin.factory.ComputeLayoutPlatformFactory
import io.flutter.embedding.engine.plugins.FlutterPlugin/*** 将AndroidView 注册为 Flutter插件** rootContext这个context我是用来作ViewModel观察的setLifecycleOwner*/
class ComputeLayoutPlugin(private val rootContext: Context) : FlutterPlugin {companion object {// Android原生View 在Flutter引擎上注册的唯一标识在Flutter端使用时必须一样private const val viewType: String com.example.flutter_mix_android.ui.flutterplugin.platform/ComputeLayoutPlatform}/*** 连接到flutter引擎时调用*/override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {// 将Android原生View 在Flutter引擎上注册binding.platformViewRegistry.registerViewFactory(viewType,ComputeLayoutPlatformFactory(rootContext, binding.binaryMessenger))}/*** 与flutter引擎分离时调用*/override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}}1.3 注册插件
AndroidMainActivity.kt
Ps建议大家直接使用FlutterFragmentActivity平替掉FlutterActivity因为
FlutterActivity继承于Activity
FlutterFragmentActivity继承于FragmentActivity它实现了 LifecycleOwner 和 ViewModelStoreOwner
package com.example.flutter_mix_android.ui.activityimport com.example.flutter_mix_android.ui.flutterplugin.plugin.ComputeLayoutPlugin
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity: FlutterFragmentActivity() {override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)// 注册为Flutter插件flutterEngine.plugins.add(ComputeLayoutPlugin(this))}}1.4 实体 LiveData
package com.example.flutter_mix_android.beanimport androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelclass CountBean : ViewModel() {var curNum: MutableLiveDataInt MutableLiveDataInt() // Android端点击次数var flutterNum: MutableLiveDataInt MutableLiveDataInt() // Flutter端点击次数接收到的var getFlutterNum: MutableLiveDataInt MutableLiveDataInt() // Flutter端点击次数主动获取的} 2、Flutter端
1.0 页面完整代码
import package:flutter/material.dart;
import package:flutter/services.dart;
import package:flutter_mix_android/bean/count_bean.dart;void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});overrideWidget build(BuildContext context) {return MaterialApp(title: Flutter Demo,debugShowCheckedModeBanner: false,theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),useMaterial3: true,),home: const MyHomePage(title: Flutter Demo Home Page),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;overrideStateMyHomePage createState() _MyHomePageState();
}class _MyHomePageState extends StateMyHomePage {final CountBean countBean CountBean();late MethodChannel channel;// Android原生View 在Flutter引擎上注册的唯一标识在Flutter端使用时必须一样final String viewType com.example.flutter_mix_android.ui.flutterplugin.platform/ComputeLayoutPlatform;static const String FLUTTER_SEND_ANDROID_DATA_NOTICE flutterSendAndroidDataNotice; // Flutter端 向 Android端 发送数据static const String FLUTTER_GET_ANDROID_DATA_NOTICE flutterGetAndroidDataNotice; // Flutter端 获取 Android端 数据static const String ANDROID_SEND_FLUTTER_DATA_NOTICE androidSendFlutterDataNotice; // Android端 向 Flutter端 发送数据static const String ANDROID_GET_FLUTTER_DATA_NOTICE androidGetFlutterDataNotice; // Android端 获取 Flutter端 数据/// 初始化消息通道initChannel(int viewId) {channel MethodChannel(flutter.mix.android/compute/$viewId); // 创建 Flutter端和Android端的相互通信的通道// 监听来自 Android端 的消息通道// Android端调用了函数这个handler函数就会被触发channel.setMethodCallHandler(handler);}/// 监听来自 Android端 的消息通道/// Android端调用了函数这个handler函数就会被触发Futuredynamic handler(MethodCall call) async {// 获取调用函数的名称final String methodName call.method;switch (methodName) {case ANDROID_SEND_FLUTTER_DATA_NOTICE:{int androidCount call.arguments[androidNum];countBean.androidNum.value androidCount;return $ANDROID_SEND_FLUTTER_DATA_NOTICE --- success;}case ANDROID_GET_FLUTTER_DATA_NOTICE:{return countBean.curNum.value ?? 0;}default:{return PlatformException(code: -1, message: 未找到Flutter端具体实现函数, details: 具体描述);}}}/// Flutter端 向 Android端 发送数据PUT 操作flutterSendAndroidData() {MapString, int map {flutterNum: countBean.curNum.value};channel.invokeMethod(FLUTTER_SEND_ANDROID_DATA_NOTICE, map).then((value) {debugPrint($FLUTTER_SEND_ANDROID_DATA_NOTICE --- Result$value);}).catchError((e) {if (e is MissingPluginException) {debugPrint($FLUTTER_SEND_ANDROID_DATA_NOTICE --- ErrornotImplemented --- 未找到Android端具体实现函数);} else {debugPrint($FLUTTER_SEND_ANDROID_DATA_NOTICE --- Error$e);}});}/// Flutter端 获取 Android端 数据GET 操作flutterGetAndroidData() {channel.invokeMethod(FLUTTER_GET_ANDROID_DATA_NOTICE).then((value) {debugPrint($FLUTTER_GET_ANDROID_DATA_NOTICE --- Result$value);countBean.getAndroidNum.value value ?? 0;}).catchError((e) {if (e is MissingPluginException) {debugPrint($FLUTTER_GET_ANDROID_DATA_NOTICE --- ErrornotImplemented --- 未找到Android端具体实现函数);} else {debugPrint($FLUTTER_GET_ANDROID_DATA_NOTICE --- Error$e);}});}/// 累计点击次数computeCount() {countBean.curNum.value 1;}Widget computeWidget() {final ButtonStyle btnStyle ElevatedButton.styleFrom(elevation: 0,padding: const EdgeInsets.symmetric(horizontal: 12),backgroundColor: Colors.white,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(35)));return Padding(padding: const EdgeInsets.all(16),child: Column(crossAxisAlignment: CrossAxisAlignment.start,children: [const Text(Flutter页面,style: TextStyle(color: Color(0xff0066ff),fontSize: 20,fontWeight: FontWeight.bold),),Padding(padding: const EdgeInsets.only(top: 16, bottom: 8),child: Row(children: [ValueListenableBuilderint(valueListenable: countBean.curNum,builder: (context, count, _) {return Text(点击次数$count,style: const TextStyle(fontSize: 16));}),Padding(padding: const EdgeInsets.only(left: 16, right: 8),child: ElevatedButton(style: btnStyle,onPressed: computeCount,child: const Text(1),),),ElevatedButton(style: btnStyle,onPressed: flutterSendAndroidData,child: const Text(发送给Android端),)],),),Padding(padding: const EdgeInsets.only(bottom: 8),child: Row(children: [ValueListenableBuilder(valueListenable: countBean.getAndroidNum,builder: (context, count, _) {return Text(获取Android页面点击次数$count,style: const TextStyle(fontSize: 16));}),Padding(padding: const EdgeInsets.only(left: 16, right: 3),child: ElevatedButton(style: btnStyle,onPressed: flutterGetAndroidData,child: const Text(获取Android端数据),),),],),),ValueListenableBuilder(valueListenable: countBean.androidNum,builder: (context, count, _) {return Text(接收Android端发送的点击次数$count,style: const TextStyle(fontSize: 16));}),],),);}overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: const Color(0xffA4D3EE),body: SizedBox(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: SafeArea(top: true,child: Column(children: [Expanded(flex: 1,child: AndroidView(viewType: viewType, // Android原生View 在Flutter引擎上注册的唯一标识在Flutter端使用时必须一样creationParams: {flutterNum: countBean.curNum.value}, // Flutter端 初始化时 向Android端 传递的参数creationParamsCodec: const StandardMessageCodec(), // 消息编解码器onPlatformViewCreated: (viewId) {initChannel(viewId);// 使用 viewId 构建不同名称的 MethodChannel// 主要应用于 多个相同AndroidView一起使用时避免消息冲突// ListMethodChannel mChannels [];// mChannels.add(MethodChannel(flutter.mix.android/compute/$viewId));// mChannels[0].invokeMethod(method)// mChannels[0].setMethodCallHandler((call) null)},)),Expanded(flex: 1, child: computeWidget()),],),),),);}}1.1 实体 ValueNotifier
import package:flutter/cupertino.dart;class CountBean {ValueNotifierint curNum ValueNotifierint(10); // Flutter端点击次数ValueNotifierint androidNum ValueNotifierint(0); // Android端点击次数接收到的ValueNotifierint getAndroidNum ValueNotifierint(0); // Android端点击次数主动获取的}6、源码地址
https://github.com/LanSeLianMa/flutter_mix_android