在Android項目中接入Flutter,在Flutter使用安卓布局 [復制鏈接]

2019-8-14 09:53
hardwork 閱讀:581 評論:0 贊:0
Tag:  Flutter Android

開頭

在flutter開發中,始終會有下面兩個無法避免的問題:

  • 原生項目往flutter遷移,就需要在原生項目中接入flutter
  • flutter項目中要使用到一些比較成熟的應用,就無法避免去用到原生的各種成熟庫,比如音視頻之類的

這篇文章,將會對上面兩種情況,分別進行介紹

在Android中接入flutter界面

在android項目中需要將flutter以module的形式接入

創建flutter module

進入當前android項目,在根目錄運行如下命令:

flutter create -t module my_flutter

上面表示創建一個名為 my_flutter 的flutter module

之后運行

cd my_flutter
cd .android/
./gradlew flutter:assembleDebug

同時,確保你的在你的android項目目錄下 app/build.gradle ,有添加如下代碼:

android {
compileSdkVersion 28
defaultConfig {
...
}
buildTypes {
...
}
//flutter相關聲明
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}

接著,在 android項目 根目錄下的 settings.gradle 中添加如下代碼

include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(
rootDir.path + '/my_flutter/.android/include_flutter.groovy'
))

最后,需要在android項目下的 app/build.gradle 中引入 my_flutter

dependencies {
...
//導入flutter
implementation project(':flutter')
}

到這里,基本上就可以開始接入flutter的內容了

不過這時候還有一個問題需要注意,如果你的android項目已經遷移到了androidx,可能你會遇到下面的這種問題


在Android項目中接入Flutter,在Flutter使用安卓布局


這種問題明顯是因為flutter創建moudle時,并未做到androidx的轉換,因為創建moudle的命令還不支持androidx

下面就開始解決這個問題

解決androidx帶來的問題

首先,如果你原先的android項目已經遷移到了androidx,那么在根目錄下的 grale.properties 一定有如下內容

# 表示使用 androidx
android.useAndroidX=true
# 表示將第三方庫遷移到 androidx
android.enableJetifier=true

下面進入到 my_flutter 目錄下,在 你的android項目/my_flutter/.android/Flutter/build.gradle 中對庫的依賴部分進行修改

如果默認的內容如下:

dependencies {
testImplementation 'junit:junit:4.12'
implementation 'com.android.support:support-v13:27.1.1'
implementation 'com.android.support:support-annotations:27.1.1'
}

將所有依賴修改為androidx的版本:

dependencies {
testImplementation 'junit:junit:4.12'
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'androidx.annotation:annotation:1.0.0'
}

在android studio上點擊完 Sync Now 同步之后

再進入下面的目錄 你的android項目/my_flutter/.android/Flutter/src/main/java/io/flutter/facade/ 目錄下,對 Flutter.java和 FlutterFragment.java 分別進行修改

修改FlutterFragment.java

原本的依賴如下


在Android項目中接入Flutter,在Flutter使用安卓布局


將報錯部分替換為androidx的版本

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

修改Flutter.java

原本的依賴如下


在Android項目中接入Flutter,在Flutter使用安卓布局


將報錯部分替換為androidx的版本

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

那么現在,androidx帶來的問題就解決了,下面就開始準備正式接入Flutter

在flutter中編輯入口

進入 my_flutter 目錄中的lib目錄,可以看到會有系統自帶的 main.dart 文件,這是一個默認的計數器頁面,我們修改一部分:

void main() => runApp(getRouter(window.defaultRouteName));
Widget getRouter(String name) {
switch (name) {
case 'route1':
return MyApp();
default:
return Center(
child: Text('Unknown route: $name', textDirection: TextDirection.ltr),
);
}
}

將入口更換為通過“route1" 命名進入進入

接下來就是在android中進行操作了

在android中接入flutter

進入到android項目,在MainActivity中,我們做如下操作:

bt_flutter.setOnClickListener {
val flutterView = Flutter.createView(
[email protected],
lifecycle,
"route1"
)
val layout = ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
layout.leftMargin = 0
layout.bottomMargin = 26
addContentView(flutterView, layout)
}

從上面的代碼可以看到,我們通過一個按鈕的點擊事件去展示了flutter的計數器頁面。實際效果如下:


在Android項目中接入Flutter,在Flutter使用安卓布局


那么android接入flutter就結束了,下面是在flutter中接入android

在Flutter中接入android界面

我們可以新建一個flutter項目,用于測試這個例子

因為用到了kotin,所以使用以下命令

flutter create -a kotlin counter_native

flutter create -a kotlin counter_native

獲取android數據

關于如何去獲取數據,主要還是使用 MethodChannel

看一下android中MainActivity的代碼

class MainActivity: FlutterActivity() {
private val channelName = "samples.flutter.io/counter_native";
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, channelNameTwo).setMethodCallHandler { methodCall, result ->
when(methodCall.method){
"getCounterData" -> {
result.success(getCounterData())
}
else -> {
result.notImplemented();
}
}
}
}
private fun getCounterData():Int{
return 100;
}
}

在 MethodChannel 的結果回調中,我們進行了篩選,如果方法名是 getCounterData就直接返回100

接下來在flutter中編寫下面的代碼:

static const platform =
const MethodChannel('samples.flutter.io/counter_native');
void getCounterData() async {
int data;
try {
final int result = await platform.invokeMethod('getCounterData');
data = result;
} on PlatformException catch (e) {
data = -999;
}
setState(() {
counterData = data;
});
}

效果如下:



獲取android的數據就說到這里,下面就是去獲取android的頁面了

獲取android的布局

相較于數據而言,拿到android的布局就要復雜的多

創建android視圖

在android項目里面,創建一個想要展示在flutter中的布局,這里,我們結合xml文件來創建布局,不過使用xml的方式,會出現R文件找不到的情況,這時候編譯器會報錯,暫時不用去管:

class CounterView(context: Context, messenger: BinaryMessenger, id: Int)
: PlatformView, MethodChannel.MethodCallHandler {
private var methodChannel: MethodChannel =
MethodChannel(messenger, "samples.flutter.io/counter_view_$id")
private var counterData: CounterData = CounterData()
private var view: View = LayoutInflater.from(context).inflate(R.layout.test_layout, null);
private var myText: TextView
init {
methodChannel.setMethodCallHandler(this)
myText = view.findViewById(R.id.tv_counter)
}
override fun getView(): View {
return view
}
override fun dispose() {
}
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
when (methodCall.method) {
"increaseNumber" -> {
counterData.counterData++
myText.text = "當前Android的Text數值是:${counterData.counterData}"
result.success(counterData.counterData)
}
"decreaseNumber" -> {
counterData.counterData--
myText.text = "當前Android的Text數值是:${counterData.counterData}"
result.success(counterData.counterData)
}
"decreaseSize" -> {
if(myText.textSize > 0){
val size = myText.textSize
myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size-1)
result.success(myText.textSize)
} else{
result.error("出錯", "size無法再小了!", null)
}
}
"increaseSize" -> {
if(myText.textSize < 100){
val size = myText.textSize
myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size+1)
result.success(myText.textSize)
} else{
result.error("出錯", "size無法再大了!", null)
}
}
"setText" -> {
myText.text = (methodCall.arguments as String)
result.success(myText.text)
}
else -> {
result.notImplemented();
}
}
}
}

上面的 CounterData 類是用于存儲數據創建的一個類:

class CounterData(var counterData: Int = 0) {
}

接下來,我們創建一個 CounterViewFactory 類用于獲取到布局:

class CounterViewFactory(private val messenger: BinaryMessenger)
: PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, id: Int, o: Any?): PlatformView {
return CounterView(context, messenger, id)
}
}

最后創建一個 CounterViewPlugin.kt 文件,它用于注冊視圖,相當于初始化入口

class CounterViewPlugin{
fun registerWith(registrar: Registrar) {
registrar.platformViewRegistry().registerViewFactory("samples.flutter.io/counter_view", CounterViewFactory(registrar.messenger()))
}
}

創建完成后,在MainActivity中進行視圖注冊:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CounterViewPlugin().registerWith(flutterView.pluginRegistry.registrarFor("CounterViewPlugin"))
...
}

接下來,就是在flutter中需要做的一些事情了

在flutter中獲取android視圖

在flutter里面,想要拿到android的視圖,需要通過 AndroidView 去獲取

Widget build(BuildContext context) {
if (Platform.isAndroid) {
return AndroidView(
viewType: 'samples.flutter.io/counter_view',
onPlatformViewCreated: _onPlatformViewCreated,
);
}
return Text(
'$defaultTargetPlatform 還不支持這個布局');
}

在 onPlatformViewCreated 方法中,我們需要創建 MethodChannel ,用于調用android中編寫的方法,我們可以封裝一個Controller去處理這些邏輯:

final CounterController counterController;
void _onPlatformViewCreated(int id) {
if (widget.counterController == null) {
return;
}
widget.counterController.setId(id);
}

下面是 CounterController

typedef void CounterViewCreatedCallBack(CounterController controller);
class CounterController {
MethodChannel _channel;
void setId(int id){
_channel = new MethodChannel('samples.flutter.io/counter_view_$id');
print("id");
}
Future increaseNumber() async {
final int result = await _channel.invokeMethod(
'increaseNumber',
);
print("result:${result}");
}
Future decreaseNumber() async {
final int result = await _channel.invokeMethod(
'decreaseNumber',
);
}
Future increaseSize() async {
final result = await _channel.invokeMethod(
'increaseSize',
);
}
Future decreaseSize() async {
final result = await _channel.invokeMethod(
'decreaseSize',
);
}
Future setText(String text) async {
final result = await _channel.invokeMethod(
'setText',text,
);
}
}

效果如下:


在Android項目中接入Flutter,在Flutter使用安卓布局

我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

重庆时时彩开奖模拟器 快乐飞艇是国家发行的吗 老11选5玩法技巧 做传媒什么赚钱吗 pk10牛牛是官方开奖吗 什么软件可以赚钱 玩玩就可以 腾讯分分彩计划精准版下载 什么手工赚钱女人到底该做什么 极速快3技巧赚钱方法 股票分析师招聘 中彩票图像 棋牌游戏888棋牌 11月14日福彩开奖结果 百度