前面咱們講了不少 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 妙用及 .. 使用