在Android項(xiàng)目中接入Flutter揭保,在Flutter使用安卓布局

開頭

在flutter開發(fā)中肥橙,始終會(huì)有下面兩個(gè)無(wú)法避免的問(wèn)題:

  • 原生項(xiàng)目往flutter遷移,就需要在原生項(xiàng)目中接入flutter
  • flutter項(xiàng)目中要使用到一些比較成熟的應(yīng)用秸侣,就無(wú)法避免去用到原生的各種成熟庫(kù)存筏,比如音視頻之類的

這篇文章,將會(huì)對(duì)上面兩種情況味榛,分別進(jìn)行介紹

在Android中接入flutter界面

在android項(xiàng)目中需要將flutter以module的形式接入

創(chuàng)建flutter module

進(jìn)入當(dāng)前android項(xiàng)目椭坚,在根目錄運(yùn)行如下命令:

flutter create -t module my_flutter

上面表示創(chuàng)建一個(gè)名為 my_flutter 的flutter module

之后運(yùn)行

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

同時(shí),確保你的在你的android項(xiàng)目目錄下 app/build.gradle ,有添加如下代碼:

android {
 compileSdkVersion 28
 defaultConfig {
 ...
 }
 buildTypes {
 ...
 }
 //flutter相關(guān)聲明
 compileOptions {
 sourceCompatibility 1.8
 targetCompatibility 1.8
 }
}

接著搏色,在 android項(xiàng)目 根目錄下的 settings.gradle 中添加如下代碼

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

最后善茎,需要在android項(xiàng)目下的 app/build.gradle 中引入 my_flutter

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

到這里,基本上就可以開始接入flutter的內(nèi)容了

不過(guò)這時(shí)候還有一個(gè)問(wèn)題需要注意频轿,如果你的android項(xiàng)目已經(jīng)遷移到了androidx垂涯,可能你會(huì)遇到下面的這種問(wèn)題

手把手教你在Android項(xiàng)目中接入Flutter,在Flutter使用安卓布局

這種問(wèn)題明顯是因?yàn)閒lutter創(chuàng)建moudle時(shí)航邢,并未做到androidx的轉(zhuǎn)換耕赘,因?yàn)閯?chuàng)建moudle的命令還不支持androidx

下面就開始解決這個(gè)問(wèn)題

解決androidx帶來(lái)的問(wèn)題

首先,如果你原先的android項(xiàng)目已經(jīng)遷移到了androidx膳殷,那么在根目錄下的 grale.properties 一定有如下內(nèi)容

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

下面進(jìn)入到 my_flutter 目錄下操骡,在 你的android項(xiàng)目/my_flutter/.android/Flutter/build.gradle 中對(duì)庫(kù)的依賴部分進(jìn)行修改

如果默認(rèn)的內(nèi)容如下:

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上點(diǎn)擊完 Sync Now 同步之后

再進(jìn)入下面的目錄 你的android項(xiàng)目/my_flutter/.android/Flutter/src/main/java/io/flutter/facade/ 目錄下,對(duì) Flutter.java和 FlutterFragment.java 分別進(jìn)行修改

修改FlutterFragment.java

原本的依賴如下

將報(bào)錯(cuò)部分替換為androidx的版本

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

修改Flutter.java

原本的依賴如下

手把手教你在Android項(xiàng)目中接入Flutter赚窃,在Flutter使用安卓布局

將報(bào)錯(cuò)部分替換為androidx的版本

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

那么現(xiàn)在册招,androidx帶來(lái)的問(wèn)題就解決了,下面就開始準(zhǔn)備正式接入Flutter

在flutter中編輯入口

進(jìn)入 my_flutter 目錄中的lib目錄考榨,可以看到會(huì)有系統(tǒng)自帶的 main.dart 文件跨细,這是一個(gè)默認(rèn)的計(jì)數(shù)器頁(yè)面,我們修改一部分:

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),
 );
 }
}

將入口更換為通過(guò)“route1" 命名進(jìn)入進(jìn)入

接下來(lái)就是在android中進(jìn)行操作了

在android中接入flutter

進(jìn)入到android項(xiàng)目河质,在MainActivity中冀惭,我們做如下操作:

bt_flutter.setOnClickListener {
 val flutterView = Flutter.createView(
 this@MainActivity,
 lifecycle,
 "route1"
 )
 val layout = ConstraintLayout.LayoutParams(
 ViewGroup.LayoutParams.MATCH_PARENT,
 ViewGroup.LayoutParams.MATCH_PARENT
 )
 layout.leftMargin = 0
 layout.bottomMargin = 26
 addContentView(flutterView, layout)
 }

從上面的代碼可以看到,我們通過(guò)一個(gè)按鈕的點(diǎn)擊事件去展示了flutter的計(jì)數(shù)器頁(yè)面掀鹅。實(shí)際效果如下:

image.png

那么android接入flutter就結(jié)束了散休,下面是在flutter中接入android

在Flutter中接入android界面

我們可以新建一個(gè)flutter項(xiàng)目,用于測(cè)試這個(gè)例子

因?yàn)橛玫搅薻otin乐尊,所以使用以下命令

flutter create -a kotlin counter_native

項(xiàng)目創(chuàng)建好之后戚丸,就可以開始了,在開始之前,我們首先可以了解以下如何在flutter中拿到android中的數(shù)據(jù)

獲取android數(shù)據(jù)

關(guān)于如何去獲取數(shù)據(jù)限府,主要還是使用 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 的結(jié)果回調(diào)中夺颤,我們進(jìn)行了篩選,如果方法名是 getCounterData就直接返回100

接下來(lái)在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;
 });
 }

效果如下:

image.png

獲取android的數(shù)據(jù)就說(shuō)到這里胁勺,下面就是去獲取android的頁(yè)面了

獲取android的布局

相較于數(shù)據(jù)而言世澜,拿到android的布局就要復(fù)雜的多

創(chuàng)建android視圖

在android項(xiàng)目里面,創(chuàng)建一個(gè)想要展示在flutter中的布局署穗,這里寥裂,我們結(jié)合xml文件來(lái)創(chuàng)建布局,不過(guò)使用xml的方式案疲,會(huì)出現(xiàn)R文件找不到的情況封恰,這時(shí)候編譯器會(huì)報(bào)錯(cuò),暫時(shí)不用去管:

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 = "當(dāng)前Android的Text數(shù)值是:${counterData.counterData}"
 result.success(counterData.counterData)
 }
 "decreaseNumber" -> {
 counterData.counterData--
 myText.text = "當(dāng)前Android的Text數(shù)值是:${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("出錯(cuò)", "size無(wú)法再小了褐啡!", 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("出錯(cuò)", "size無(wú)法再大了诺舔!", null)
 }
 }
 "setText" -> {
 myText.text = (methodCall.arguments as String)
 result.success(myText.text)
 }
 else -> {
 result.notImplemented();
 }
 }
 }
}

上面的 CounterData 類是用于存儲(chǔ)數(shù)據(jù)創(chuàng)建的一個(gè)類:

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

接下來(lái),我們創(chuàng)建一個(gè) 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)
 }
}

最后創(chuàng)建一個(gè) CounterViewPlugin.kt 文件春贸,它用于注冊(cè)視圖混萝,相當(dāng)于初始化入口

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

創(chuàng)建完成后,在MainActivity中進(jìn)行視圖注冊(cè):

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

接下來(lái)萍恕,就是在flutter中需要做的一些事情了

在flutter中獲取android視圖

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

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

在 onPlatformViewCreated 方法中允粤,我們需要?jiǎng)?chuàng)建 MethodChannel ,用于調(diào)用android中編寫的方法翼岁,我們可以封裝一個(gè)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,
 );
 }
}

效果如下:

image.png

最后給大家分享一份移動(dòng)架構(gòu)大綱类垫,包含了移動(dòng)架構(gòu)師需要掌握的所有的技術(shù)體系,大家可以對(duì)比一下自己不足或者欠缺的地方有方向的去學(xué)習(xí)提升琅坡;
需要高清架構(gòu)圖以及圖中視頻資料的可以加入我的技術(shù)交流群:825106898私聊群主小姐姐免費(fèi)獲取

QQ圖片20190414204449.jpg

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悉患,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子榆俺,更是在濱河造成了極大的恐慌售躁,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茴晋,死亡現(xiàn)場(chǎng)離奇詭異陪捷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诺擅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門市袖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人烁涌,你說(shuō)我怎么就攤上這事苍碟【泼伲” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵微峰,是天一觀的道長(zhǎng)舷丹。 經(jīng)常有香客問(wèn)我,道長(zhǎng)县忌,這世上最難降的妖魔是什么掂榔? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮症杏,結(jié)果婚禮上装获,老公的妹妹穿的比我還像新娘。我一直安慰自己厉颤,他們只是感情好穴豫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逼友,像睡著了一般精肃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帜乞,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天司抱,我揣著相機(jī)與錄音,去河邊找鬼黎烈。 笑死习柠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的照棋。 我是一名探鬼主播资溃,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼烈炭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了符隙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤膏执,失蹤者是張志新(化名)和其女友劉穎驻售,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欺栗,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年迟几,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了消请。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡类腮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蚜枢,到底是詐尸還是另有隱情,我是刑警寧澤厂抽,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布筷凤,位于F島的核電站昭殉,受9級(jí)特大地震影響藐守,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卢厂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巢块。 院中可真熱鬧,春花似錦、人聲如沸姥闭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)铜跑。三九已至门怪,卻和暖如春锅纺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工护锤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酿傍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓氯析,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親掩缓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容