在Flutter中调用Android的代码

news2024/9/25 11:21:14

参考

【Flutter 混合开发】嵌入原生View-Android

默认使用Android studioKotlin

基本配置

创建flutter项目

在终端执行

flutter create batterylevel

添加 Android 平台的实现

打开项目下的android/app/src/main/kotlin 下的 MainActivity.kt 文件。
在这里插入图片描述
我这里编辑器有提示,最好使用其推荐,这样代码会有提示
在这里插入图片描述
使用推荐后会新打开一个窗口,在原窗口里提示的错误也将不会再提示

MainActivity.kt

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine

class MainActivity : FlutterActivity() {
    // 在flutter引擎启动时,将自定义的插件添加到flutter 引擎的插件列表中
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        flutterEngine.plugins.add(MyPlugin())
    }
}

在项目文件下创建MyFlutterView.ktMyFlutterViewFactory.ktMyPlugin.kt 三个文件

  • MyFlutterView.kt编写的是原生安卓代码
  • MyFlutterViewFactory.kt一个自定义的Flutter平台视图工厂,用于创建创建Flutter平台视图并将其返回
  • MyPlugin.kt 创建一个自定义的Flutter插件,将自定义的Flutter平台视图工厂注册到Flutter引擎中,以便在Flutter应用中使用该自定义视图。
    在这里插入图片描述

MyFlutterView.kt

import android.content.Context
import android.view.View
import android.widget.TextView
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.platform.PlatformView

// context 上下文对象
// messenger 用于消息传递,BinaryMessenger二进制消息
// viewId View 生成时会分配一个唯一 ID
// args Flutter 传递的初始化参数
class MyFlutterView(
    context: Context,
    messenger: BinaryMessenger,
    viewId: Int,
    args: Map<String, Any>?
) :
    PlatformView {
    // PlatformView代表一个平台视图,它是Flutter和原生平台之间的桥梁。
    // PlatformView允许在Flutter应用中嵌入原生视图,并提供了与Flutter框架进行交互的方法。

    // 定义一个文本视图
    private val textView: TextView = TextView(context)

    init {
        // 初始化文本视图
        textView.text = "我是Android View"
    }

    // 获取文本视图
    override fun getView(): View {
        return textView
    }

    //  销毁
    override fun dispose() {

    }
}

MyFlutterViewFactory.kt

import android.content.Context
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
class MyFlutterViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(
    StandardMessageCodec.INSTANCE
) {

    // 创建PlatformView
    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
        return MyFlutterView(context, messenger, viewId, args as Map<String, Any>?)
    }

}

MyPlugin.kt

"com.example.batterylevel/custom_platform_view" 是自定义平台视图的标识符,需要保证唯一。
标识符通常由小写字母、数字和斜杠(/)组成,并且不包含空格或特殊字符。
具体如何编写没查到,参考文章应该是 按着包名/视图的描述的格式,感觉挺好的

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.PluginRegistry

class MyPlugin : FlutterPlugin {

    // onAttachedToEngine 在Flutter插件与Flutter引擎绑定时被调用。
    // 在这个方法中,可以执行一些初始化操作,例如注册平台视图工厂、注册方法调用处理器等
    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        // 消息发送器
        val messenger: BinaryMessenger = binding.binaryMessenger
        //
        binding
            .platformViewRegistry
            .registerViewFactory(
                "com.example.batterylevel/custom_platform_view", MyFlutterViewFactory(messenger)
            )
    }

    // 注册视图
    companion object {
        
        fun registerWith(registrar: PluginRegistry.Registrar) {
            registrar
                .platformViewRegistry()
                .registerViewFactory(
                    "com.example.batterylevel/custom_platform_view",
                    MyFlutterViewFactory(registrar.messenger())
                )
        }
    }

    // 用于在插件从 Flutter 引擎中分离时执行清理操作。
    // 当插件不再需要与 Flutter 引擎通信时,例如应用关闭或插件被移除时,onDetachedFromEngine 方法会被调用。
    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {

    }
}

基本内容编写完成后回到flutter项目的部分,进行运行
main.dart

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '嵌入原生View-Android',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const PlatformViewDemo(),
    );
  }
}

class PlatformViewDemo extends StatelessWidget {
  const PlatformViewDemo({super.key});

  
  Widget build(BuildContext context) {
    platformView() {
      // 如果是安卓平台,则返回一个AndroidView对象
      // AndroidView 是 Flutter 提供的一个小部件,用于在 Flutter 中嵌入原生 Android 视图
      if (defaultTargetPlatform == TargetPlatform.android) {
        // 标识符要保持一致
        return const AndroidView(
          viewType: 'com.example.batterylevel/custom_platform_view',
        );
      }
    }

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: platformView(),
      ),
    );
  }
}

在这里插入图片描述

flutter与原生之间的通信

关于通信可以看一下这篇文章:Flutter与原生如何进行通信

简单的参数传递

flutter中的修改,main.dart

class PlatformViewDemoState extends State<PlatformViewDemo> {
  
  Widget build(BuildContext context) {
    platformView() {
      // 如果是安卓平台,则返回一个AndroidView对象
      // AndroidView 是 Flutter 提供的一个小部件,用于在 Flutter 中嵌入原生 Android 视图
      if (defaultTargetPlatform == TargetPlatform.android) {
        // 标识符要保持一致
        return const AndroidView(
          viewType: 'com.example.batterylevel/custom_platform_view',
          creationParams: {'text': 'AndroidTextView122'},
          creationParamsCodec: StandardMessageCodec(),
        );
      }
    }

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: platformView(),
      ),
    );
  }
}
  • creationParams :传递的参数,插件可以将此参数传递给 AndroidView 的构造函数。
  • creationParamsCodec :将 creationParams 编码后再发送给平台侧,它应该与传递给构造函数的编解码器匹配。值的范围:
    • StandardMessageCodec,这是 Flutter 默认的编解码器,用于序列化和反序列化标准数据类型,例如字符串、数字、布尔值、列表和映射
    • JSONMessageCodec,使用 JSON 格式进行序列化和反序列化。它可以用于传递复杂的数据结构,例如嵌套的映射和列表。
    • StringCodec,用于将字符串编码为字节流,并在需要时进行解码。它适用于只传递字符串类型的创建参数。
    • BinaryCodec,用于直接传递字节流类型的创建参数,不进行额外的编码或解码

修改 MyFlutterView.kt

class MyFlutterView(
    context: Context,
    messenger: BinaryMessenger,
    viewId: Int,
    args: Map<String, Any>?
) :
    PlatformView {
    // PlatformView代表一个平台视图,它是Flutter和原生平台之间的桥梁。
    // PlatformView允许在Flutter应用中嵌入原生视图,并提供了与Flutter框架进行交互的方法。

    // 定义一个文本视图
    private val textView: TextView = TextView(context)

    init {
        // 初始化文本视图
        // 没学过Kotlin,大体意思是参数存在则赋值
           args?.also { map ->
            println(map["text"])
            println("参数")
            textView.text = map["text"] as String
        }
    }

    // 获取文本视图
    override fun getView(): View {
        return textView
    }

    //  销毁
    override fun dispose() {

    }
}

在这里插入图片描述

使用MethodChannel 通信

main.dart

class PlatformViewDemoState extends State<PlatformViewDemo> {
  // 用于通信的通道
  static const platform = MethodChannel('com.flutter.guide.MyFlutterView');
  
  Widget build(BuildContext context) {
    platformView() {
      // 如果是安卓平台,则返回一个AndroidView对象
      // AndroidView 是 Flutter 提供的一个小部件,用于在 Flutter 中嵌入原生 Android 视图
      if (defaultTargetPlatform == TargetPlatform.android) {
        // 标识符要保持一致
        return const AndroidView(
          viewType: 'com.example.batterylevel/custom_platform_view',
          creationParams: {'text': '向安卓传参数'},
          creationParamsCodec: StandardMessageCodec(),
        );
      }
    }

    return Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  // invokeMethod 的作用是在特定的上下文中调用指定的方法。它接受两个参数:方法名称和一个包含参数的对象
                  platform.invokeMethod('setText', {'name': '张三', 'age': 18});
                },
                child: const Text("使用MethodChannel通信")),
            Expanded(child: platformView()!)
          ],
        ));
  }
}

修改 MyFlutterView.kt

class MyFlutterView(
    context: Context,
    messenger: BinaryMessenger,
    viewId: Int,
    args: Map<String, Any>?
) :
    PlatformView, MethodChannel.MethodCallHandler {
    // PlatformView代表一个平台视图,它是Flutter和原生平台之间的桥梁。
    // PlatformView允许在Flutter应用中嵌入原生视图,并提供了与Flutter框架进行交互的方法。

    // 定义一个文本视图
    private val textView: TextView = TextView(context)

    private var methodChannel: MethodChannel

    init {

        // 初始化文本视图
        // 没学过Kotlin,大体意思是参数存在则赋值
        args?.also { map ->
            textView.text = map["text"] as String
        }
        // com.flutter.guide.MyFlutterView 标识保证唯一,与flutter中的保持一致
        methodChannel = MethodChannel(messenger, "com.flutter.guide.MyFlutterView")
        methodChannel.setMethodCallHandler(this)
    }

    // 获取文本视图
    override fun getView(): View {
        return textView
    }

    //  销毁
    override fun dispose() {
        methodChannel.setMethodCallHandler(null)
    }

    // 方法调用处理器
    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        if (call.method == "setText") {
            val name = call.argument("name") as String?
            val age = call.argument("age") as Int?

            textView.text = "hello,$name,年龄:$age"
        } else {
            result.notImplemented()
        }
    }
}

注:

methodChannel = MethodChannel(messenger, "com.flutter.guide.MyFlutterView")
methodChannel.setMethodCallHandler(this)

必须放在

 args?.also { map ->
            textView.text = map["text"] as String
        }

后面,好像存在执行顺序的问题。如果放在前面, textView.text = map["text"] as String不会执行
在这里插入图片描述

flutter接收来自原生的消息
在flutter中使用invokeMethod调用原生的方法。在原生中处理对应方法时,如果要向flutter中返回消息可以使用

textView.text = "hello,$name,年龄:$age"
result.success("收到了来自flutter的调用")

在flutter中正常接收即可

ElevatedButton(
 onPressed: () async{
   // invokeMethod 的作用是在特定的上下文中调用指定的方法。它接受两个参数:方法名称和一个包含参数的对象
 var res = await  platform.invokeMethod('setText', {'name': '张三', 'age': 18});
 print(res);
 },
 child: const Text("使用MethodChannel通信"))

在这里插入图片描述

解决多个原生View通信冲突问题

这个看原文章就好了,主要是保证每一个通道都是唯一的。

原生 View 生成时,系统会为其生成唯一id:viewId,使用 viewId 构建不同名称的 MethodChannel。

class MyFlutterView(context: Context, messenger: BinaryMessenger, viewId: Int, args: Map<String, Any>?) : PlatformView, MethodChannel.MethodCallHandler {

    val textView: TextView = TextView(context)
    private var methodChannel: MethodChannel

    init {
        args?.also {
            textView.text = it["text"] as String
        }
        methodChannel = MethodChannel(messenger, "com.flutter.guide.MyFlutterView_$viewId")
        methodChannel.setMethodCallHandler(this)
    }
  ...
}

Flutter 端为每一个原生 View 创建不同的MethodChannel

var platforms = [];

AndroidView(
  viewType: 'plugins.flutter.io/custom_platform_view',
  onPlatformViewCreated: (viewId) {
    print('viewId:$viewId');
    platforms
        .add(MethodChannel('com.flutter.guide.MyFlutterView_$viewId'));
  },
  creationParams: {'text': 'Flutter传给AndroidTextView的参数'},
  creationParamsCodec: StandardMessageCodec(),
)

给第一个发送消息:

platforms[0]
    .invokeMethod('setText', {'name': 'laomeng', 'age': 18});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1430102.html

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

相关文章

开源的三维算法库有哪些

PCL,VTK,VCG,CGAL&#xff0c;Open CASCADE&#xff08;opencascade&#xff09;&#xff0c;OpenSceneGraph (OSG)&#xff0c;Easy3D 点云网格处理算法&#xff1a;openmesh, meshlab三维算法库&#xff0c;Eigen 网格简化&#xff0c;网格平滑&#xff0c;网格参数化 无序…

北朝隋唐文物展亮相广西,文物预防性保护网关保驾护航

一、霸府名都——太原博物馆收藏北朝隋朝文物展 2月1日&#xff0c;广西民族博物馆与太原博物馆携手&#xff0c;盛大开启“霸府名都——太原博物馆北朝隋文物展”。此次新春展览精选了北朝隋唐时期150多件晋阳文物珍品。依据“巍巍雄镇”“惊世古冢”“锦绣名都”三个单元&am…

Java swing —— 创建一个窗口

swing组件分类&#xff1a; 顶层容器&#xff1a;JFrame、JApplet、JDialog、JWindow 中间容器&#xff1a;JPanel、JScrollPane、JSplitPane、JToolBar 基本控件&#xff1a; ImageIcon&#xff08;图标&#xff09;&#xff0c;JLabel&#xff08;标签&#xff09;&#xff…

【数据结构】分治策略

现场保护和现场恢复 文章目录 分治策略分治法解决问题有以下四个特征&#xff1a;分治法步骤: 递归&#xff1a;解决以下问题&#xff1a;倒序输出整数求最大公约数&#xff08;递归和非递归&#xff09;菲波那切数列 不要尝试间接 要使用直接递归&#xff08;自己调用自己&am…

代码随想录算法训练营第二十四天|● 理论基础 ● 77. 组合

仅做学习笔记&#xff0c;详细请访问代码随想录 ● 理论基础 ● 77. 组合 ● 理论基础 回溯法解决的问题 回溯法&#xff0c;一般可以解决如下几种问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合 切割问题&#xff1a;一个字符串按一定规则有几…

SpringBoot统一功能处理,拦截器,统一数据格式,捕捉异常

目录 拦截器:是Spring框架提供的核心功能之一&#xff0c;主要用来拦截用户的请求&#xff0c;在指定方法前后&#xff0c;根据业务需要执行预先设定的代码: 自定义拦截器 统一数据格式&#xff0c;要包含状态码&#xff0c;错误信息​编辑 出现针对String类型的错误​​​…

MySQL查询缓存

MySQL查询缓存 MySQL在查询的时候首先会查询缓存&#xff0c;如果缓存命中的话就直接返回结果&#xff0c;不需要解析sql语句&#xff0c;也不会生成执行计划&#xff0c;更不会执行&#xff1b;如果没有命中缓存&#xff0c;则再进行SQL解析以及进行查询&#xff0c;并将结果返…

CodeFuse成功支持通义千问算法大赛,评测方案已开源

前段时间&#xff0c; 首届通义千问AI挑战赛成功举办&#xff0c;CodeFuse 为大赛提供技术支持&#xff0c;模型微调框架 MFTCoder 和 CodeFuseEval 评测框架为大赛保驾护航&#xff0c;助力大赛圆满完成。我们基于leetcode 阿里和蚂蚁最新面试题库建设了“模型赛马”在线打榜的…

NC、NC65、NCC富客户端附件在线预览插件

NC附件目前只支持下载&#xff0c;不支持在线查看 通过二开实现NC的附件可以在线预览 支持的格式包含&#xff1a;doc, docx, xls, xlsx, ppt, pptx, pdf和txt等。

金和OA jc6 UploadFileBlock 任意文件上传漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

使用JDBC连接mysql

JDBC:Java DataBase Connectivity,Java数据库连接。 使用Java语言操作关系型数据库的一套API。 原理&#xff1a;官方&#xff08;sun公司&#xff09;定义出一套操作所有关系型数据库的规则&#xff0c;即接口&#xff1b;所有的数据库厂商去实现这套接口&#xff0c;提供数据…

Kubernetes k8s

Kubernetes k8s 一个开源的容器编排引擎&#xff0c;用来对容器化应用进行自动化部署、 扩缩和管理。 从架构设计层面&#xff0c;k8s能很好的解决可用性&#xff0c;伸缩性&#xff1b;从部署运维层面&#xff0c;服务部署&#xff0c;服务监控&#xff0c;应用扩容和故障处…

大数据信用报告查询费用一般要多少钱?

一些不少朋友在申贷的时候被拒贷之后&#xff0c;得到的原因就是因为大数据不良被拒&#xff0c;这时候很多人都反过来查询自己的大数据信用报告&#xff0c;而查询的价格也是不少朋友都比较关注的&#xff0c;那大数据信用报告查询费用一般要多少钱呢?下面本文就为你介绍一下…

C# 读取文件中的配置信息

文章目录 定义使用文件格式代码 C#读取文件并处理&#xff1b;C# 读取文件中的配置信息。 在有的程序中&#xff0c;需要从本地文件中读取配置信息&#xff0c;以进行初始化。 定义 定义一个静态函数来获取文件信息。StreamReader 类。 /// <summary> /// 读取参数文件…

获取真实 IP 地址(一):判断是否使用 CDN(附链接)

一、介绍 CDN&#xff0c;全称为内容分发网络&#xff08;Content Delivery Network&#xff09;&#xff0c;是一种网络架构&#xff0c;旨在提高用户对于网络上内容的访问速度和性能。CDN通过在全球各地部署分布式服务器节点来存储和分发静态和动态内容&#xff0c;从而减少…

MySQL全表扫描:性能杀手的隐患与优化策略

MySQL全表扫描&#xff1a;性能杀手的隐患与优化策略 MySQL数据库作为常用的关系型数据库管理系统之一&#xff0c;全表扫描问题一直困扰着开发者。本文将深入剖析MySQL全表扫描的原理、其对性能的严重影响&#xff0c;同时提供一系列优化策略&#xff0c;助您高效应对MySQL性能…

聊聊比特币----比特币地址

⽐特币地址是⼀个标识符&#xff08;帐号&#xff09;&#xff0c;包含27-34个字母数字拉丁字符&#xff08;0&#xff0c;O&#xff0c;I除外&#xff09;。地址可以以QR码形式表⽰&#xff0c;是匿名的&#xff0c;不包含关于所有者的信息。 地址⽰例&#xff1a;14qViLJfdG…

Linux|Grep 命令的 12 个实用示例

您是否曾经遇到过在文件中查找特定字符串或模式的任务&#xff0c;但不知道从哪里开始查找&#xff1f;那么&#xff0c;grep 命令可以拯救你&#xff01; grep 是一个功能强大的文件模式搜索器&#xff0c;每个 Linux 发行版都配备了它。如果出于某种原因&#xff0c;它没有安…

华为机考入门python3--(8)牛客8-合并表记录

分类&#xff1a;字典排序 知识点&#xff1a; 将输入转成int的列表 my_list list(map(int, input().strip().split( ))) 将列表转为元组 tuple(my_list) 访问元素为元组的列表 for first, second, third in my_list: 对字典进行排序 sorted(my_dict.items())…

如何计算两个指定日期相差几年几月几日

一、题目要求 假定给出两个日期&#xff0c;让你计算两个日期之间相差多少年&#xff0c;多少月&#xff0c;多少天&#xff0c;应该如何操作呢&#xff1f; 本文提供网页、ChatGPT法、VBA法和Python法等四种不同的解法。 二、解决办法 1. 网页计算法 这种方法是利用网站给…