Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信
背景
前面我们讲了很多 Flutter 相关的知识点,但是我们并没有介绍怎样实现 Flutter 与原生的通信。
比如我在 Flutter UI 上面点击了一个按钮,我希望原生做一些处理,那么原生怎么知道?
比如我在原生有些变化需要告知 Flutter,Flutter 又如何获知?
本篇我们先解决第一个问题。即 Flutter-> 原生的通信。
路由回顾
之前我们一直在讲 Flutter 相关的知识点,而且基本上都是在 main.dart 文件上面折腾,为了避免很多小伙伴觉得我们跨度过大。
因此我们这里补充一下之前第三篇 Flutter 即学即用系列博客——03 在旧有项目引入 Flutter 的知识点。
在 Flutter Module 的 main.dart 文件里面,对于存在多个页面的情况,我们可以写下面的模板代码:
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return SomeWidget(...);
case 'route2':
return SomeOtherWidget(...);
default:
return Center(
child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}
这段代码我们可以重点关注 switch 那一块代码。这里会根据不同的路由,返回不同的页面。
下面我们会用到这种写法。
实际案例
接下来我们通过实际案例来说明如何实现 Flutter 向原生发送消息?
我们的案例是假设我要获取 Android 设备的当前电量,我希望点击按钮之后电量会显示出来。
当然这里的按钮和显示电量的文本都是 Flutter 界面的。
那么步骤是怎样的呢?
1. 搭建 Flutter 界面
我们将界面写成一个单独的 battery_widget.dart 文件:
import 'package:flutter/material.dart';
class BatteryWidget extends StatefulWidget {
@override
_BatteryWidgetState createState() => _BatteryWidgetState();
}
class _BatteryWidgetState extends State<BatteryWidget> {
String _batteryLevel = 'Battery level: unknown.';
void _getBatteryLevel() {}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_batteryLevel),
RaisedButton(
child: const Text('Refresh'),
onPressed: _getBatteryLevel,
),
],
),
);
}
}
很简单的界面,就是一个文本和一个按钮,排成一列。
然后我们 main.dart 修改如下:
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:my_flutter/battery_widget.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'battery':
return MaterialApp(
home: Scaffold(
body: BatteryWidget(),
),
);
default:
return MaterialApp(
home: Scaffold(
body: Container(),
),
);
}
}
这里的关键点是指定 route 名字为 battery 时,返回我们刚刚新建的 battery_widget 界面。
2. 原生调用 Flutter 界面
在 MainActivity.java 里面,我们写出下面代码:
package com.nesger.flutterdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import io.flutter.facade.Flutter;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View flutterView = Flutter.createView(
MainActivity.this,
getLifecycle(),
"battery"
);
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addContentView(flutterView, layout);
}
}
可以看到 battery 指定了要加载的 Flutter 界面。
运行后效果如下:
接下来就是关键的在点击按钮的时候如何获取原生设备电量。
根据上面的代码,我们知道点击按钮会执行 _getBatteryLevel 方法。因此我们要在这里做一些修改。
3. Flutter 定义 MethodChannel
我们在 _BatteryWidgetState 里面加入下面变量:
static const MethodChannel methodChannel = MethodChannel('samples.flutter.io/battery');
samples.flutter.io/battery 可以自己指定,一般保证唯一,所以 samples 实际使用可以替换为包名。主要是要跟原生对应即可。
4. Flutter 调用 methodChannel API invokeMethod 调用原生某个方法并获取对应的值。
final int result = await methodChannel.invokeMethod('getBatteryLevel');
比如我们这里要通过原生的 getBatteryLevel 方法获取到对应的电量,并将返回值用 result 保存。
这里的 await 是因为这个操作是异步的。同时 _getBatteryLevel 也要改为对应的异步方法,因此最终方法代码如下:
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await methodChannel.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level: $result%.';
} on PlatformException {
batteryLevel = 'Failed to get battery level.';
}
setState(() {
_batteryLevel = batteryLevel;
});
}
可以看到通过异步方法获取到电量之后通过 setState 方法更新界面。
5. 原生定义 MethodChannel
private static final String BATTERY_CHANNEL = "samples.flutter.io/battery";
注意需要跟 Flutter 的一一对应。
6. 原生调用创建 MethodChannel 并通过 MethodCallHandler 接收 Flutter 的方法调用
new MethodChannel((FlutterView)flutterView, BATTERY_CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
}
);
可以看到我们是通过 call.method 来区分 Flutter 的不同方法调用。
这里 result.success 返回成功回调。 result.error 返回错误回调。result.notImplemented 表明没有对应实现。
最后我们实现原生 getBatteryLevel 方法即可。
如下:
private int getBatteryLevel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
return (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
}
运行点击按钮,效果如下:
到此我们 Flutter 调用原生并获取返回值的方法就介绍完了。
这里我们总结如下:
Flutter 准备工作:
- 定义 MethodChannel
- 通过异步方法调用 methodChannel 的 invokeMethod 指定这个 methodChannel 具体要调用的方法名
原生准备工作:
- 定义 CHANNEL(与 Flutter 对应)
- 创建 MethodChannel 并通过 setMethodCallHandler 方法来区分 Flutter 的不同调用方法名和返回对应的回调
源码位置:
https://github.com/nesger/FlutterSample/tree/feature/method_channel
参考链接:
https://flutter.dev/docs/development/platform-integration/platform-channels
https://github.com/flutter/flutter/tree/master/examples/platform_channel
更多阅读:
Flutter 即学即用系列博客
Flutter 即学即用系列博客——01 环境搭建
Flutter 即学即用系列博客——02 一个纯 Flutter Demo 说明
Flutter 即学即用系列博客——03 在旧有项目引入 Flutter
Flutter 即学即用系列博客——04 Flutter UI 初窥
Flutter 即学即用系列博客——05 StatelessWidget vs StatefulWidget
Flutter 即学即用系列博客——06 超实用 Widget 集锦
Flutter 即学即用系列博客——07 RenderFlex overflowed 引发的思考
Flutter & dart
dart 如何优雅的避空
Flutter map 妙用及 .. 使用
Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信的更多相关文章
- Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)
前言 上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信. 并且也看到了 Flutter 内部 EventChannel 源码也是对 Meth ...
- Flutter 即学即用系列博客——02 一个纯 Flutter Demo 说明
前言 上一篇文章我们搭建好了 Flutter 的开发环境. Flutter 即学即用--01 环境搭建 这一篇我们通过 Flutter 的一个 Demo 来了解下 Flutter. 开发系统:MAC ...
- Flutter 即学即用系列博客——09 EventChannel 实现原生与 Flutter 通信(一)
前言 紧接着上一篇,这一篇我们讲一下原生怎么给 Flutter 发信号,即原生-> Flutter 还是通过 Flutter 官网的 Example 来讲解. 案例 接着上一次,这一次我们让原生 ...
- Flutter 即学即用系列博客总结篇
前言 迟到的总结篇,其实大家看我之前发的系列博客最后一篇,发文时间是 3 月 29 日.距离现在快两个月了. 主要是因为有很多事情在忙,所以这篇就耽搁了. 今天终于可以跟大家会面了. 系列博客背景 F ...
- Flutter 即学即用系列博客——05 StatelessWidget vs StatefulWidget
前言 上一篇我们对 Flutter UI 有了一个基本的了解. 这一篇我们通过自定义 Widget 来了解下如何写一个 Widget? 然而 Widget 有两个,StatelessWidget 和 ...
- Flutter 即学即用系列博客——04 Flutter UI 初窥
前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...
- Flutter 即学即用系列博客——06 超实用 Widget 集锦
本篇文章我们来讲讲一些比较常用的 Widget. 大家验证的时候使用下面的代码替换 main.dart 代码,然后在 //TODO 语句返回下面常用 Widget 示例的代码. import 'pac ...
- Flutter 即学即用系列博客——03 在旧有项目引入 Flutter
前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...
- Flutter 即学即用系列博客——10 混淆
前言 之前的博客我们都是在 debug 的模式下进行开发的. 实际发布到市场或者给到用户的都是 release 包. 而对于 Android 来说,release 包一个重要的步骤就是混淆. Andr ...
随机推荐
- 重构:以Java POI 导出EXCEL为例2
前言 上一篇博文已经将一些对象抽象成成员变量以及将一些代码块提炼成函数.这一节将会继续重构原有的代码,将一些函数抽象成类,增加成员变量,将传入的参数合成类等等. 上一篇博文地址:http://www. ...
- linux yum命令 使用
yum -y install 包名(支持*) :自动选择y,全自动 yum install 包名(支持*) :手动选择y or n yum remove 包名(不支持*) rpm -ivh 包名(支持 ...
- 【移动端web】软键盘兼容问题
软键盘收放事件 这周几天遇到了好几个关于web移动端兼容性的问题.并花了很长时间去研究如何处理这几种兼容问题. 这次我们来说说关于移动端软键盘的js处理吧. 一般情况下,前端是无法监控软键盘到底是弹出 ...
- ALS交替最小二乘法总结
摘要: 1.算法概述 2.算法推导 3.算法特性及优缺点 4.注意事项 5.实现和具体例子 6.适用场合 内容: 1.算法概述 ALS是alternating least squares的缩写 , 意 ...
- redis与CPU、内存
任何一个后端应用,包括代码都要考虑对于CPU和内存的影响.redis本质上类似于nodejs,单进程.单线程,事件驱动,但不同的是redis是CPU密集型的.这里列出了redis与内存CPU的相关考虑 ...
- Unity 用ml-agents机器学习造个游戏AI吧(1)(Windows环境配置)
前言:以前觉得机器学习要应用于游戏AI,还远得很. 最近看到一些资料后,突发兴致试着玩了玩Unity机器学习,才发觉机器学习占领游戏AI的可能性和趋势. Unity训练可爱柯基犬Puppo 机器学习训 ...
- Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 上一篇基于哈希表实现HashMap核心源码彻底分析 分析了HashMap的源码,主要分析了扩容机制,如果感兴趣的可以去看看,扩容机制那几行最难懂的 ...
- 泛微关于js设计的一些小技巧
1.关于泛微流程的js设计 泛微oa可以插入javascript可以diy自己想要的表单页面前端功能.如果有前端开发经验,或者熟练使用jQuery的话,这将变得非常容易!同时泛微OA内部有很多库,包括 ...
- ASP.NET MVC默认配置如有跳转到指定的Area区域中的对应程序中
今天在搭建一个基于MVC的项目,因为项目涉及到了手机和pc端,为了方便和减少二者之间的耦合我在区域(Areas)中建立了两个 程序空间,那么问题来了我想让程序默认跳转到我所指定的areas中的对应项目 ...
- Vue(day8)
继续上一篇文章的内容,本文主要内容为项目中新闻资讯模块的实现. 新闻资讯页面主要是当我们点击这个按钮时跳转到新闻列表界面. 一.新闻资讯的路由设计 将新闻资讯的标签改为路由:(a标签改为router- ...