本文首發於公衆號「後廠村碼農」html
ReactNative入門系列
React Native組件
Flutter基礎系列前端
原本這篇文章應該講一下Flutter的插件開發,可是在插件開發的基礎是PlatformChannel,也就是Flutter與Android/iOS Native的通訊,理解了這一個知識點,Flutter的插件開發也就不在話下。java
Flutter不能完成全部Native的功能,所以須要Flutter與Native的通訊,Flutter提供了一套Platform Channel的機制,來知足Flutter與Native通訊的需求。 下面是PlatformChannel架構。 android
Flutter定義了三種不一樣類型的PlatformChannel,它們分別是:程序員
這幾個PlatformChannel的用法都不難,本文會以比較經常使用的MethodChannel來進行舉例。在此以前咱們先要了解BinaryMessenger、Codec、Handler的概念。json
BinaryMessenger BinaryMessenger是PlatformChannel與Flutter端的通訊的工具,其通訊使用的消息格式爲二進制格式數據,BinaryMessenger在Android中是一個接口,它的實現類爲FlutterNativeView。網絡
Codec Codec是消息編解碼器,主要用於將二進制格式的數據轉化爲Handler可以識別的數據,Flutter定義了兩種Codec:MessageCodec和MethodCodec。MessageCodec用於二進制格式數據與基礎數據之間的編解碼,BasicMessageChannel所使用的編解碼器是MessageCodec。MethodChannel和EventChannel所使用的編解碼均爲MethodCodec。架構
Handler Flutter定義了三種類型的Handler,它們與PlatformChannel類型一一對應,分別是MessageHandler、MethodHandler、StreamHandler。在使用PlatformChannel時,會爲它註冊一個Handler,PlatformChannel會將該二進制數據經過Codec解碼爲轉化爲Handler可以識別的數據,並交給Handler處理。當Handler處理完消息以後,會經過回調函數返回result,將result經過編解碼器編碼爲二進制格式數據,經過BinaryMessenger發送回Flutter端。app
MethodChannel能夠實現Flutter調用Android,也能夠實現Android調用Flutter,這裏分別來進行舉例。less
這裏實現一個Android的簡單的功能:彈出一個AlertDialog,而後在Flutter中調用這一功能。
Android端實現 先在MainActivity中實現功能,以下所示。
package com.example.platform_channel;
import android.app.AlertDialog;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);//1
MethodChannel methodChannel = new MethodChannel(getFlutterView(), "com.example.platform_channel/dialog");//2
methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {//3
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if ("dialog".equals(methodCall.method)) {
if (methodCall.hasArgument("content")) {
showAlertDialog();
result.success("彈出成功");
} else {
result.error("error", "彈出失敗", "content is null");
}
} else {
result.notImplemented();
}
}
private void showAlertDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setPositiveButton("肯定", null);
builder.setTitle("Flutter調用Android");
builder.show();
}
});
}
}
複製代碼
註釋1處用於註冊插件,這個是建立Flutter工程時MainActivity自帶的。 註釋2處建立一個MethodChannel,它有兩個參數,一個是getFlutterView方法,用於獲取FlutterView,FlutterView實現了BinaryMessenger接口。一個是MethodChannel的Name,這個Name要保證是惟一的,後面Flutter端實現中會用到這個Name。 註釋3處爲methodChannel註冊一個MethodCallHandler,用於監聽回調的數據。 onMethodCall方法中的result是Flutter端傳來的數據,咱們須要對數據進行判斷,而後向Flutter端發送數據。 向Flutter端發送數據有如下方法:
result.success(Object result) 結果成功,將result返回給Flutter端。
result.error(String errorCode,String errorMsg,Object errorDetails) 結果失敗,將errorCode、errorMsg、errorDetails返回給Flutter端。
result.notImplemented() Android端沒有實現Flutter端須要的方法,會將notImplemented返回給Flutter端。
Flutter端實現 在main.dart中加入以下代碼。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const platformChannel =
const MethodChannel('com.example.platform_channel/dialog');//1
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
home: Scaffold(
appBar: AppBar(
title: Text("Flutter調用Android"),
),
body: Padding(
padding: EdgeInsets.all(40.0),
child: RaisedButton(
child: Text("調用Dialog"),
onPressed: () {
showDialog("Flutter調用AlertDialog");
},
),
),
),
);
}
void showDialog(String content) async {
var arguments = Map();
arguments['content'] = content;
try {
String result = await platformChannel.invokeMethod('dialog', arguments);//2
print('showDialog ' + result);
} on PlatformException catch (e) {
print('showDialog ' + e.code + e.message + e.details);
} on MissingPluginException catch (e) {
print('showDialog ' + e.message);
}
}
}
複製代碼
註釋1處建立了MethodChannel,它須要傳入MethodChannel的Name,這個Name要保證和Android端設置的Name是同樣的。當點擊按鈕時會觸發showDialog方法。註釋2處用於調用Android中的方法,第一個參數是方法的名稱,第二個參數arguments只能是Map或者JSON類型的,是咱們須要傳遞給Android端的數據。 運行程序,當咱們點擊"調用Dialog"按鈕時,效果以下所示。
有的時候Flutter調用Android後,Android還會將結果返回給Flutter,雖然有時能夠用result來實現,但Android端的處理多是異步的,result對象也不能長期的持有,這時就須要Android來調用Flutter。 由於頁面UI是Flutter端繪製的,咱們很難在頁面中控制Android端,要實現Android調用Flutter,能夠利用Android的Activty的生命週期,若是將應用切到後臺再切回前臺,這樣Activty的onResume方法就會被調用,咱們在onResume方法中實現調用Flutter的功能就能夠了。
Android端的實現
package com.example.platform_channel;
import android.os.Bundle;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
public static final String MAIN_ACTIVITY = "MainActivity";
MethodChannel methodChannel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
methodChannel = new MethodChannel(getFlutterView(),"com.example.platform_channel/text");//1
}
@Override
protected void onResume() {
super.onResume();
Map map = new HashMap();
map.put("content","Android進階三部曲");
methodChannel.invokeMethod("showText", map, new MethodChannel.Result() {//2
@Override
public void success(Object o) {
Log.d(MAIN_ACTIVITY,(String)o);
}
@Override
public void error(String errorCode, String errorMsg, Object errorDetail) {
Log.d(MAIN_ACTIVITY,"errorCode:"+errorCode+" errorMsg:"+errorMsg+" errorDetail:"+(String)errorDetail);
}
@Override
public void notImplemented() {
Log.d(MAIN_ACTIVITY,"notImplemented");
}
});
}
}
複製代碼
和Flutter調用Android的代碼是相似的,在註釋1處建立MethodChannel,而後在註釋2處調用Flutter端的showText方法,並將數據以Map的形式傳遞過去。MethodChannel.Result() 的回調裏有三個方法,經過這三個方法能夠獲得Android調用Flutter的結果。
Flutter端的實現
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
static const platformChannel =
const MethodChannel('com.example.platform_channel/text');
String textContent = 'Flutter端初始文字';
@override
void initState() {
// TODO: implement initState
super.initState();
platformChannel.setMethodCallHandler((methodCall) async {
switch (methodCall.method) {
case 'showText':
String content = await methodCall.arguments['content'];
if (content != null && content.isNotEmpty) {
setState(() {
textContent = content;
});
return 'success';
} else {
throw PlatformException(
code: 'error', message: '失敗', details: 'content is null');
}
break;
default:
throw MissingPluginException();
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
home: Scaffold(
appBar: AppBar(
title: Text('Android調用Flutter'),
),
body: Padding(
padding: EdgeInsets.all(40.0),
child: Text(textContent),
),
),
);
}
}
複製代碼
由於要實現Flutter頁面的改變,就須要在initState方法中爲MethodChannel添加回調,若是Android端傳遞過來的方法名稱爲showText,就獲取Android端傳來的content的值,賦值給Text來改變頁面的狀態。 運行程序後,將程序切到後臺再切回前臺,效果以下圖所示。
Flutter基礎系列
Flutter基礎(一)移動開發的跨平臺技術演進
Flutter基礎(二)Flutter開發環境搭建和Hello World
Flutter基礎(三)Dart快速入門
Flutter基礎(四)開發Flutter應用前須要掌握的Basic Widget
Flutter基礎(五)Material組件之MaterialApp、Scaffold、AppBar
Flutter基礎(六)Material組件之BottomNavigationBar、TabBar、Drawer
Flutter基礎(七)Scrolling Widget之ListView、GridView、PageView
Flutter基礎(八)手勢相關Widget:GestureDetector和Dismissible
Flutter基礎(九)資源和圖片
Flutter基礎(十)佈局Widget快速入門
Flutter基礎(十一)網絡請求(Dio)與JSON數據解析
Flutter基礎(十二)路由(頁面跳轉)與數據傳遞
Flutter基礎(十三)Flutter與Android的相互通訊