前面咱们讲了不少 Flutter 相关的知识点,可是咱们并无介绍怎样实现 Flutter 与原生的通讯。java
好比我在 Flutter UI 上面点击了一个按钮,我但愿原生作一些处理,那么原生怎么知道?android
好比我在原生有些变化须要告知 Flutter,Flutter 又如何获知?git
本篇咱们先解决第一个问题。即 Flutter-> 原生的通讯。github
以前咱们一直在讲 Flutter 相关的知识点,并且基本上都是在 main.dart 文件上面折腾,为了不不少小伙伴以为咱们跨度过大。api
所以咱们这里补充一下以前第三篇 Flutter 即学即用系列博客——03 在旧有项目引入 Flutter 的知识点。app
在 Flutter Module 的 main.dart 文件里面,对于存在多个页面的状况,咱们能够写下面的模板代码:less
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 那一块代码。这里会根据不一样的路由,返回不一样的页面。异步
下面咱们会用到这种写法。async
接下来咱们经过实际案例来讲明如何实现 Flutter 向原生发送消息?ide
咱们的案例是假设我要获取 Android 设备的当前电量,我但愿点击按钮以后电量会显示出来。
固然这里的按钮和显示电量的文本都是 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 界面。
在 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 方法。所以咱们要在这里作一些修改。
咱们在 _BatteryWidgetState 里面加入下面变量:
static const MethodChannel methodChannel = MethodChannel('samples.flutter.io/battery');
samples.flutter.io/battery 能够本身指定,通常保证惟一,因此 samples 实际使用能够替换为包名。主要是要跟原生对应便可。
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 方法更新界面。
private static final String BATTERY_CHANNEL = "samples.flutter.io/battery";
注意须要跟 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 妙用及 .. 使用