在Android原生項目中添加flutter
官方文檔
1.創(chuàng)建flutter module,有兩種方法:
用Android studio創(chuàng)建flutter module;
用命令創(chuàng)建flutter module
cd some/path/(原生項目的根目錄)
flutter create -t module --org com.example my_flutter
2.設(shè)置 settings.gradle 添加下列代碼
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'my_flutter/.android/include_flutter.groovy' // 必須是flutter module的真實路徑
))
- 設(shè)置app build.gradle
dependencies {
implementation project(':flutter')
}
- 注意事項
//Java 版本號
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
minSdkVersion 16(最低)
- Android 代碼加載 flutter 界面
Flutter提供兩種引入方法:一個是View,會默認生成一個View,一個是FlutterFragment方式。
package com.example.administrator.shadowapplication.flutter
import android.os.Bundle
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.EventChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
/**
* native項目加載flutter頁面
將flutter頁面構(gòu)建成View,通過addView來顯示flutter頁面
將flutter頁面構(gòu)建成Fragment,通過對fragment的操作來顯示flutter頁面
*/
class FlutterActivity : FragmentActivity() {
companion object {
private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
private var flutterFragment: FlutterFragment? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//方法1
/* setContentView(R.layout.activity_flutter)
val fragmentManager: FragmentManager = supportFragmentManager
flutterFragment = fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?
if (flutterFragment == null) {
val newFlutterFragment = Flutter.createFragment("這里是flutter頁面")
flutterFragment = newFlutterFragment
fragmentManager.beginTransaction()
.add(R.id.flutterContent, newFlutterFragment, TAG_FLUTTER_FRAGMENT)
.commit()
}*/
//方法2
val flutterView = Flutter.createView(this, lifecycle, "route1")
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.topMargin = 20
addContentView(flutterView, layoutParams)
}
}
- flutter 代碼
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp(
initParams: window.defaultRouteName,
));
class MyApp extends StatelessWidget {
final String initParams;
MyApp({Key key, @required this.initParams}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 混合開發(fā)',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: _getHomePage(),
);
}
Widget _getHomePage() {
switch (initParams) {
case "route1":
return MyHomePage(
title: 'flutter 混合開發(fā)',
initParams: "hello shadow!!",
);
break;
}
}
}
class MyHomePage extends StatefulWidget {
final String initParams;
MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState(initParams);
}
class _MyHomePageState extends State<MyHomePage> {
final String initParams;
int _counter = 0;
_MyHomePageState(this.initParams);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'initParams:$initParams',
style: TextStyle(color: Colors.red),
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
flutter 和Android 平臺通信
平臺通道簡介
所謂“平臺特定”或“特定平臺”,平臺指的就是指Flutter運?的平臺,如Android或IOS,可以認為就是應(yīng)?的原?部分。 所以,平臺通道正是Flutter和原?之間通信的橋梁,它也是Flutter插件的底層基礎(chǔ)設(shè)施。
Flutter使?了?個靈活的系統(tǒng),允許您調(diào)?特定平臺的API,?論在Android上的Java或Kotlin代碼中,還是iOS上的 ObjectiveC或Swift代碼中均可?。
Flutter與原?之間的通信依賴靈活的消息傳遞?式:
應(yīng)?的Flutter部分通過平臺通道(platform channel)將消息發(fā)送到其應(yīng)?程序的所在的宿主(iOS或Android)應(yīng) ?(原?應(yīng)?)。 宿主監(jiān)聽平臺通道,并接收該消息。然后它會調(diào)?該平臺的API,并將響應(yīng)發(fā)送回客戶端,即應(yīng)?程序的Flutter部 分。
平臺通道
使?平臺通道在Flutter(client)和原?(host)之間傳遞消息

圖中可以看到,Flutter 是 Client 端,Native 是 Host,Client 和 host 通信是通過 PlatformChannel,Client 通過 PlatformChannel 向 Host 發(fā)送消息,Host 監(jiān)聽 PlatformChannel 并接收消息,然后將響應(yīng)結(jié)果發(fā)送給 Client。
消息和響應(yīng)以異步方式傳遞,以確保 UI 不阻塞。另外,PlatformChannel 是雙工的,這意味著 Flutter 和 Native 可以交替做 Client 和 Host。
Flutter調(diào)用原生主要使用插件方式,通過MethodChannel的方式。
Flutter 定義了三種不同類型的 PlatformChannel,它們分別是:
- MethodChannel:用于傳遞方法調(diào)用,是比較常用的 PlatformChannel。
- EventChannel: 用于傳遞事件。
- BasicMessageChannel:用于傳遞數(shù)據(jù)。
BinaryMessenger
BinaryMessenger 是 PlatformChannel 與 Flutter 端的通信的工具,其通信使用的消息格式為二進制格式數(shù)據(jù),BinaryMessenger 在 Android 中是一個接口,它的實現(xiàn)類為 FlutterNativeView。
Codec
Codec 是消息編解碼器,主要用于將二進制格式的數(shù)據(jù)轉(zhuǎn)化為 Handler 能夠識別的數(shù)據(jù),F(xiàn)lutter 定義了兩種 Codec:MessageCodec 和 MethodCodec。MessageCodec 用于二進制格式數(shù)據(jù)與基礎(chǔ)數(shù)據(jù)之間的編解碼,BasicMessageChannel 所使用的編解碼器是 MessageCodec。MethodChannel 和 EventChannel 所使用的編解碼均為 MethodCodec。
Handler
Flutter 定義了三種類型的 Handler,它們與 PlatformChannel 類型一一對應(yīng),分別是 MessageHandler、MethodHandler、StreamHandler。
在使用 PlatformChannel 時,會為它注冊一個 Handler,PlatformChannel 會將該二進制數(shù)據(jù)通過 Codec 解碼為轉(zhuǎn)化為 Handler 能夠識別的數(shù)據(jù),并交給 Handler 處理。
當(dāng) Handler 處理完消息之后,會通過回調(diào)函數(shù)返回 result,將 result 通過編解碼器編碼為二進制格式數(shù)據(jù),通過 BinaryMessenger 發(fā)送回 Flutter 端。
MethodChannel 可以實現(xiàn) Flutter 調(diào)用 Android,也可以實現(xiàn) Android 調(diào)用 Flutter,這里分別來進行舉例。
MethodChannel 實現(xiàn)通信
Android端 代碼 實現(xiàn)
package com.example.administrator.shadowapplication.flutter
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
/**
* native項目加載flutter頁面
將flutter頁面構(gòu)建成View,通過addView來顯示flutter頁面
將flutter頁面構(gòu)建成Fragment,通過對fragment的操作來顯示flutter頁面
*/
class FlutterActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val flutterView = Flutter.createView(this, lifecycle, "route1")
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.topMargin = 20
addContentView(flutterView, layoutParams)
//生成plateForm,注冊 handle
MethodChannelPlugin(flutterView)
}
/**
* 獲取當(dāng)前面 電量
*/
fun getBatteryLevel(): Int {
val batteryLevel: Int
batteryLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
}
//======================== 封裝了 MethodChannelPlugin====
package com.example.administrator.shadowapplication.flutter.channel
import android.app.Activity
import com.example.administrator.shadowapplication.flutter.FlutterActivity
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView
/**
* @author 付影影
* @desc 用于數(shù)據(jù)流 event streams的通信,持續(xù)通信,通常用于native向dart的通信
* 如手機電量變化,網(wǎng)絡(luò)連接變化,陀螺儀,傳感器等
* @date 2020/1/9
*/
class MethodChannelPlugin constructor(flutterView: FlutterView) : MethodChannel.MethodCallHandler {
var activity: Activity = flutterView.context as Activity
private val CHANNEL_NAME = "MethodChannelPlugin"
init {
MethodChannel(flutterView, CHANNEL_NAME).setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = (activity as FlutterActivity).getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
}
else -> {
result.notImplemented()
}
}
}
}
Dart 代碼 實現(xiàn)
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp(
initParams: window.defaultRouteName,
));
class MyApp extends StatelessWidget {
final String initParams;
MyApp({Key key, @required this.initParams}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 混合開發(fā)',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: _getHomePage(),
);
}
///根據(jù) native 傳遞的參數(shù),選擇要展示的界面
Widget _getHomePage() {
switch (initParams) {
case "route1":
return MyHomePage(
title: 'flutter 混合開發(fā)',
initParams: "hello shadow!!",
);
break;
}
}
}
class MyHomePage extends StatefulWidget {
final String initParams;
MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState(initParams);
}
class _MyHomePageState extends State<MyHomePage> {
final String initParams;
_MyHomePageState(this.initParams);
///調(diào)?通道上的?法,指定通過字符串標識符調(diào)??法 getBatteryLevel 。
///該調(diào)?可能失敗(平臺不?持平臺 API,例如在模擬器中運?時),所以我們將invokeMethod調(diào)?包裝在try-catch語句中。
///我們使?返回的結(jié)果,在 setState 中來更新?戶界?狀態(tài) batteryLevel
String _batteryLevel = "unknow battery level";
MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
///獲取 當(dāng)前 電量
Future<Null> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await _methodChannel.invokeMethod("getBatteryLevel");
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
_getBatteryLevel();
},
child: Text(
'FlutterActivity 傳遞數(shù)據(jù):$initParams',
style: TextStyle(color: Colors.red),
),
),
Text(
'$_batteryLevel',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
實現(xiàn) 結(jié)果

MethodChannel 實現(xiàn) 在Android 中調(diào)用 flutter 方法
有的時候 Flutter 調(diào)用 Android 后,Android 還會將結(jié)果返回給 Flutter,雖然有時可以用 result 來實現(xiàn),但 Android 端的處理可能是異步的,result 對象也不能長期的持有,這時就需要 Android 來調(diào)用 Flutter。
因為頁面 UI 是 Flutter 端繪制的,我們很難在頁面中控制 Android 端,要實現(xiàn) Android 調(diào)用 Flutter,可以利用 Android 的 Activty 的生命周期,如果將應(yīng)用切到后臺再切回前臺,這樣 Activty 的 onResume 方法就會被調(diào)用,我們在 onResume 方法中實現(xiàn)調(diào)用 Flutter 的功能就可以了。
Android 端代碼實現(xiàn)
package com.example.administrator.shadowapplication.flutter
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView
import java.util.*
/**
* native項目加載flutter頁面
將flutter頁面構(gòu)建成View,通過addView來顯示flutter頁面
將flutter頁面構(gòu)建成Fragment,通過對fragment的操作來顯示flutter頁面
*/
private lateinit var flutterView: FlutterView;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
flutterView = Flutter.createView(this, lifecycle, "route1")
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.topMargin = 20
addContentView(flutterView, layoutParams)
}
override fun onResume() {
super.onResume()
val map = HashMap<String, String>()
map["content"] = "Hello 付小影子,我是Android"
val methodChannel = MethodChannel(flutterView, "MethodChannelPlugin")
methodChannel.invokeMethod("showText", map, object : MethodChannel.Result {
//2
override fun success(o: Any?) {
Log.d("hh", o as String?)
}
override fun error(errorCode: String, errorMsg: String?, errorDetail: Any?) {
Log.d("hh", "errorCode:" + errorCode + " errorMsg:" + errorMsg + " errorDetail:" + errorDetail as String?)
}
override fun notImplemented() {
Log.d("hh", "notImplemented")
}
})
}
}
Dart 端 代碼實現(xiàn)
class _MyHomePageState extends State<MyHomePage> {
final String initParams;
_MyHomePageState(this.initParams);
//通信方式2
MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
String textContent = " Flutter 初始數(shù)據(jù)";
@override
void initState() {
super.initState();
_methodChannel.setMethodCallHandler((MethodCall call) async {
switch (call.method) {
case "showText":
String content = await call.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 Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
child: Text(
'FlutterActivity 傳遞數(shù)據(jù):$initParams',
style: TextStyle(color: Colors.red),
),
),
Text(
'$_batteryLevel',
style: Theme.of(context).textTheme.display1,
),
Text(" Android端數(shù)據(jù) -- $textContent")
],
),
),
);
}
}
BasicMessageChannel 實現(xiàn) 通信
Android 代碼實現(xiàn)
package com.example.administrator.shadowapplication.flutter.channel
import android.app.Activity
import com.example.administrator.shadowapplication.crash_log.ToastUtil
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StringCodec
import io.flutter.view.FlutterView
/**
* @author 付影影
* @desc BasicMessageChannel (主要是傳遞字符串和一些半結(jié)構(gòu)體的數(shù)據(jù))
* BasicMessageChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec)
* @date 2020/1/9
*/
class BasicMessageChannelPlugin constructor(flutterView: FlutterView) : BasicMessageChannel.MessageHandler<String> {
lateinit var activity: Activity;
lateinit var messageChannel: BasicMessageChannel<String>;
val CHANNEL_NAME = "BasicMessageChannelPlugin"
/**
* BasicMessageChannel的初始化,入?yún)⒂腥齻€,
* 第一個是BinaryMessage,
* 第二個參數(shù)是定義的channel name,
* 第三個參數(shù)是MessageCodec解碼器
* 官方提供了四種消息編碼器,最終自動轉(zhuǎn)為ByteBuffer
*/
init {
activity = flutterView.context as Activity
messageChannel = BasicMessageChannel<String>(flutterView, CHANNEL_NAME, StringCodec.INSTANCE);
//設(shè)置消息處理器,處理來自dart的消息
messageChannel.setMessageHandler(this)
}
/**
* 第二種 使用:dart 主動發(fā)起,Android端為接收方(收到dart的消息,并且發(fā)送回復(fù))
*/
override fun onMessage(message: String?, reply: BasicMessageChannel.Reply<String>) {
reply.reply("BasicMessageChannel收到:$message") //可以通過reply進行回復(fù)
ToastUtil.showMsg(message) //接收到的消息
}
/**
* 第一種 使用:發(fā)送消息到Dart ,并且 接收到 dart的回復(fù)
*/
fun send(message: String, callBack: BasicMessageChannel.Reply<String>) {
messageChannel.send(message, callBack)
}
}
Dart 端代碼實現(xiàn)
class BasicMessageChannelPage extends StatefulWidget {
@override
_BasicMessageChannelPageState createState() =>
_BasicMessageChannelPageState();
}
class _BasicMessageChannelPageState extends State<BasicMessageChannelPage> {
String _receiveData ="flutter 默認數(shù)據(jù)";
var _messageChannel = BasicMessageChannel<String>("BasicMessageChannelPlugin", StringCodec());
//發(fā)送消息 到 Android,并且收到Android的回復(fù)
Future<String> sendMessage() async {
String reply = await _messageChannel.send("hello shadow,我是 dart");
print(reply);
return reply;
}
//從Android 端接收消息,并且發(fā)送回復(fù)
void receiveMessage() {
_messageChannel.setMessageHandler((message) async {
print("接收 Android 端發(fā)送的消息,$message");
return "哈哈哈哈,這是我給你的回復(fù)";
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Text("EventChanne 接收的Android數(shù)據(jù):$_receiveData"),
);
}
}
EventChannel 實現(xiàn)通信
Android 代碼實現(xiàn)
package com.example.administrator.shadowapplication.flutter.channel
import io.flutter.plugin.common.EventChannel
import io.flutter.view.FlutterView
/**
* @author 付影影
* @desc 用于數(shù)據(jù)流 event streams的通信,持續(xù)通信,通常用于native向dart的通信
* 如手機電量變化,網(wǎng)絡(luò)連接變化,陀螺儀,傳感器等
* @date 2020/1/9
*/
class EventChannelPlugin constructor(flutterView: FlutterView) : EventChannel.StreamHandler {
private val CHANNEL_NAME = "EventChannelPlugin"
init {
EventChannel(flutterView, CHANNEL_NAME).setStreamHandler(this)
}
/**
* onCancel代表對面不再接收,這里我們應(yīng)該做一些clean up的事情。
* onListen則代表通道已經(jīng)建好,Native可以發(fā)送數(shù)據(jù)了。
* 注意onListen里帶的EventSink這個參數(shù),后續(xù)Native發(fā)送數(shù)據(jù)都是經(jīng)過EventSink的
*/
var events: EventChannel.EventSink? = null
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
this.events = events
}
override fun onCancel(arguments: Any?) {
events = null
}
/**
* 給 dart端發(fā)送消息
*/
fun send(params: Any) {
if (events != null) {
events!!.success(params)
}
}
}
Dart 代碼實現(xiàn)
class EventChannelPage extends StatefulWidget {
@override
_EventChannelPageState createState() => _EventChannelPageState();
}
class _EventChannelPageState extends State<EventChannelPage> {
String _receiveData = "Flutter 默認數(shù)據(jù)";
var CHANNEL_NAME = "EventChannelPlugin";
var __eventChannel = EventChannel("CHANNEL_NAME");
@override
void initState() {
// TODO: implement initState
super.initState();
__eventChannel.receiveBroadcastStream().listen(eventListener);
}
@override
Widget build(BuildContext context) {
return Center(
child: Text("EventChanne 接收的Android數(shù)據(jù):$_receiveData"),
);
}
void eventListener(event) {
print("收到的數(shù)據(jù):$event");
setState(() {
_receiveData = event;
});
}
}