前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信

Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信

作者头像
AndroidTraveler
发布2019-04-09 10:24:28
8660
发布2019-04-09 10:24:28
举报
文章被收录于专栏:AndroidTravelerAndroidTraveler
背景

前面我们讲了很多 Flutter 相关的知识点,但是我们并没有介绍怎样实现 Flutter 与原生的通信。

比如我在 Flutter UI 上面点击了一个按钮,我希望原生做一些处理,那么原生怎么知道?

比如我在原生有些变化需要告知 Flutter,Flutter 又如何获知?

本篇我们先解决第一个问题。即 Flutter-> 原生的通信。

路由回顾

之前我们一直在讲 Flutter 相关的知识点,而且基本上都是在 main.dart 文件上面折腾,为了避免很多小伙伴觉得我们跨度过大。

因此我们这里补充一下之前第三篇 Flutter 即学即用系列博客——03 在旧有项目引入 Flutter 的知识点。

在 Flutter Module 的 main.dart 文件里面,对于存在多个页面的情况,我们可以写下面的模板代码:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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 修改如下:

代码语言:javascript
复制
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 里面,我们写出下面代码:

代码语言:javascript
复制
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 里面加入下面变量:

代码语言:javascript
复制
static const MethodChannel methodChannel = MethodChannel('samples.flutter.io/battery');

samples.flutter.io/battery 可以自己指定,一般保证唯一,所以 samples 实际使用可以替换为包名。主要是要跟原生对应即可。

4. Flutter 调用 methodChannel API invokeMethod 调用原生某个方法并获取对应的值。
代码语言:javascript
复制
final int result = await methodChannel.invokeMethod('getBatteryLevel');

比如我们这里要通过原生的 getBatteryLevel 方法获取到对应的电量,并将返回值用 result 保存。

这里的 await 是因为这个操作是异步的。同时 _getBatteryLevel 也要改为对应的异步方法,因此最终方法代码如下:

代码语言:javascript
复制
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
代码语言:javascript
复制
private static final String BATTERY_CHANNEL = "samples.flutter.io/battery";

注意需要跟 Flutter 的一一对应。

6. 原生调用创建 MethodChannel 并通过 MethodCallHandler 接收 Flutter 的方法调用
代码语言:javascript
复制
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 方法即可。

如下:

代码语言:javascript
复制
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 准备工作:

  1. 定义 MethodChannel
  2. 通过异步方法调用 methodChannel 的 invokeMethod 指定这个 methodChannel 具体要调用的方法名

原生准备工作:

  1. 定义 CHANNEL(与 Flutter 对应)
  2. 创建 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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 安卓小煜 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 路由回顾
  • 实际案例
    • 1. 搭建 Flutter 界面
      • 2. 原生调用 Flutter 界面
        • 3. Flutter 定义 MethodChannel
          • 4. Flutter 调用 methodChannel API invokeMethod 调用原生某个方法并获取对应的值。
            • 5. 原生定义 MethodChannel
              • 6. 原生调用创建 MethodChannel 并通过 MethodCallHandler 接收 Flutter 的方法调用
                • 参考链接:
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档