https://github.com/fengxing1234/Flutter
常用命令
- 創(chuàng)建module
flutter create -t module my_flutter
創(chuàng)建項(xiàng)目
使用Android Studio 在 flutter目錄中創(chuàng)建5個(gè)項(xiàng)目:
Native 項(xiàng)目
Android 原生項(xiàng)目flutter_app項(xiàng)目
標(biāo)準(zhǔn)的Flutter App工程,包含標(biāo)準(zhǔn)的Dart層與Native平臺(tái)層flutter_module項(xiàng)目
Flutter組件工程,僅包含Dart層實(shí)現(xiàn)啃匿,Native平臺(tái)層子工程為通過Flutter自動(dòng)生成的隱藏工程flutter_package項(xiàng)目
Flutter純Dart插件工程,僅包含Dart層的實(shí)現(xiàn)冀值,往往定義一些公共Widgetflutter_plugin項(xiàng)目
Flutter平臺(tái)插件工程也物,包含Dart層與Native平臺(tái)層的實(shí)現(xiàn)
native集成flutter_module
官網(wǎng)方式集成
https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps
官方建議使用flutter_module形式集成native宫屠,這里做個(gè)實(shí)驗(yàn),看看flutter_app形式能否成功滑蚯。
native項(xiàng)目中:
-
build.gradle
中
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
不添加會(huì)報(bào)錯(cuò)的浪蹂。
-
dependencies
中
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
implementation project(':flutter')
}
-
setting gradle
中
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
同步代碼
mainActivity 中添加代碼
setContentView(R.layout.activity_main);
container = (FrameLayout) findViewById(R.id.flutter_container);
findViewById(R.id.btn_flutter).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View flutterView = Flutter.createView(
MainActivity.this,
getLifecycle(),
"route1"
);
// FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
// layout.leftMargin = 100;
// layout.topMargin = 200;
// addContentView(flutterView, layout);
container.addView(flutterView);
}
});
運(yùn)行,查看結(jié)果告材,完畢坤次。
aar 方式集成
flutter_module集成
首先我們注釋掉一些代碼
- setting 中
include ':app'
//setBinding(new Binding([gradle: this])) // new
//evaluate(new File( // new
// settingsDir.parentFile, // new
// 'flutter_module/.android/include_flutter.groovy' // new
//))
- dependencies 中
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
// implementation project(':flutter')
}
- mainActivity
// View flutterView = Flutter.createView(
// MainActivity.this,
// getLifecycle(),
// "route1"
// );
//// FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
//// layout.leftMargin = 100;
//// layout.topMargin = 200;
//// addContentView(flutterView, layout);
// container.addView(flutterView);
同步代碼 運(yùn)行。
首先打出aar包
在flutter_module目錄下斥赋,執(zhí)行:
flutter build apk
會(huì)在flutter_module/.android/Flutter/build/outputs/aar/
生成aar文件
如果代碼沒有改變缰猴,在此使用命令不會(huì)打出新的aar。
使用
./gradlew assembleDebug
./gradlew assembleRelease
這兩個(gè)命令也可以打出aar包疤剑。
使用aar集成方式滑绒,適用flutter_app和flutter_module開發(fā)闷堡。
- Native中:
把這個(gè)aar放在libs目錄
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
自動(dòng)依賴aar
- java代碼
在開啟Flutter前
FlutterMain.startInitialization(this);
可以放在Application中,這里我就放在了MainActivity 中了
- MainActivity
package com.picc.anative;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.FrameLayout;
import io.flutter.view.FlutterMain;
public class MainActivity extends AppCompatActivity {
private FrameLayout container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FlutterMain.startInitialization(this);
container = (FrameLayout) findViewById(R.id.flutter_container);
findViewById(R.id.btn_flutter).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, MyFlutterActivity.class));
// View flutterView = Flutter.createView(
// MainActivity.this,
// getLifecycle(),
// "route1"
// );
//// FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
//// layout.leftMargin = 100;
//// layout.topMargin = 200;
//// addContentView(flutterView, layout);
// container.addView(flutterView);
}
});
}
}
- MyFlutterActivity
package com.picc.anative;
import android.os.Bundle;
import android.support.annotation.Nullable;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MyFlutterActivity extends FlutterActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
這里注意:FlutterActivity 導(dǎo)報(bào)時(shí)候注意 別倒錯(cuò)了疑故。
現(xiàn)在同步下代碼杠览,然后運(yùn)行,完事纵势。
目前這樣做會(huì)有問題踱阿,如果flutter-module集成了native插件,會(huì)報(bào)錯(cuò)钦铁。
稍等在寫一個(gè)native插件模擬報(bào)錯(cuò)软舌。
flutter-app 集成進(jìn)native
純flutter-app項(xiàng)目,需要做一些修改才能打aar包育瓜。
-
flutter-app/android/app/build.gradle
修改如下
def isLib = true
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
if(isLib) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
if(!isLib) {
applicationId "com.zhyen.flutter_app"
}
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
if(isLib) {
ndk {
//設(shè)置支持的SO庫(kù)架構(gòu)
abiFilters 'armeabi', 'armeabi-v7a', 'x86'
}
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
sourceSets {
main {
if (isLib) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
如果想要打包aar文件 就使用apply plugin: 'com.android.library'
打包aar葫隙,application是打apk包的。
打包aar躏仇,不需要 applicationId
恋脚。
為了避免AndroidManifest文件沖突,在不同模式下焰手,選擇不同文件糟描。
sourceSets {
main {
if (isLib) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
創(chuàng)建`module/AndroidManifest.xml,上git上找去。
現(xiàn)在開始打包
cd flutter_app
cd android
./gradlew assembleDebug
在flutter_app/build/app/outputs/aar/
目錄在會(huì)生辰aar文件书妻。
現(xiàn)在包有了船响,和上面module集成方式就一樣了。
我這里直接替換aar包躲履,就可以運(yùn)行了见间。
小試了一下, 因?yàn)槲掖虻陌莇ebug工猜,在native進(jìn)入flutter頁(yè)面時(shí)米诉,會(huì)出現(xiàn)黑屏,改成release就好了篷帅。
現(xiàn)在 純flutter 和 module 模式 都可以使用 aar方式集成了
當(dāng)然還有很多坑沒踩史侣,現(xiàn)在就來(lái)踩坑完。
編寫flutter-plugin 讓現(xiàn)在的集成方式報(bào)錯(cuò)魏身。
flutter_plugin
創(chuàng)建插件時(shí)惊橱,自動(dòng)生成一個(gè)native方法,我們還是在創(chuàng)建一個(gè)方法把箭昵,就做個(gè)toast吧税朴。
嗯。。正林。 不寫了 茧跋。費(fèi)事。就用自動(dòng)帶的方法吧卓囚,如果調(diào)用成功瘾杭,會(huì)顯示手機(jī)版本,如果失敗哪亿,Unknown粥烁。
flutter_app依賴flutter-plugin
首先更改開發(fā)模式
def isLib = false
- 依賴插件
這里使用本地依賴
pubspec.yaml
文件
flutter_plugin:
path: ../flutter_plugin/
- 修改代碼
按照plugin-示例代碼使用
運(yùn)行 ,可以 蝇棉,完畢讨阻。
修改模式
def isLib = true
打包aar。
- native
替換aar
運(yùn)行篡殷,報(bào)錯(cuò)钝吮。
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/zhyen/flutter_plugin/FlutterPlugin;
at io.flutter.plugins.GeneratedPluginRegistrant.registerWith(GeneratedPluginRegistrant.java:14)
at com.picc.anative.MyFlutterActivity.onCreate(MyFlutterActivity.java:14)
at android.app.Activity.performCreate(Activity.java:7436)
at android.app.Activity.performCreate(Activity.java:7426)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1286)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3279)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3484)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:86)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2123)
at android.os.Handler.dispatchMessage(Handler.java:109)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:7470)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
找不到插件,看來(lái)打包aar板辽,只能打包源碼奇瘦,不能打包依賴。
Flutter 帶有原生代碼的插件劲弦,在插件安裝后耳标,也是會(huì)以本地 Module Project 的形式引入 。
在插件安裝之后邑跪,所有帶原生代碼的插件次坡,都會(huì)以路徑和插件名的key=value 形式 存在 .flutter-plugins 文件中。
而在 android 工程的 settings.gradle 里画畅,會(huì)通過讀取該文件將 .flutter-plugins 文件中的項(xiàng)目一個(gè)個(gè) include 到主工程里砸琅。
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
之后就是主工程里的 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
腳本的引入了,這個(gè)腳本一般在于 flutterSDK/packages/flutter_tools/gradle/
目錄下轴踱,其中最關(guān)鍵的部分同樣是 讀取.flutter-plugins
文件中的項(xiàng)目症脂,然后一個(gè)一個(gè)再implementation
到主工程里完成依賴。
自此所有原生代碼的 Flutter 插件寇僧,都被作為本地 Module Project 的形式引入主工程了 摊腋,最后腳本會(huì)自動(dòng)生成一個(gè) GeneratedPluginRegistrant.java 文件沸版,實(shí)現(xiàn)原生代碼的引用注冊(cè)嘁傀, 而這個(gè)過程對(duì)你完全是無(wú)感的。
解決辦法 一 使用 fat-aar
-修改flutter-app端代碼
-
android/gradle
文件
添加fat-aar
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.kezong:fat-aar:1.1.10'
}
-
android/app/gradle
文件
if(isLib) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
if(isLib) {
apply plugin: 'com.kezong.fat-aar'
}
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
///為庫(kù)的方式才添加本地倉(cāng)庫(kù)依賴视粮,這個(gè)本地倉(cāng)庫(kù)目前是從 include 那里讀取的细办。
if(isLib) {
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, _ ->
println name
embed project(path: ":$name", configuration: 'default')
}
}
打aar包。可以使用了笑撞,完畢岛啸。
方案二
方案二我們使用 flutter_module來(lái)做,一人一個(gè)茴肥,誰(shuí)也不偏向坚踩。
flutter_module 依賴和app一樣的。
lib代碼一樣的瓤狐。
修改一下標(biāo)題區(qū)分module還是app瞬铸。
編譯,生成aar础锐,替換aar嗓节,運(yùn)行,報(bào)錯(cuò)皆警。
還是一樣的錯(cuò)誤拦宣,我們來(lái)解決一下。
- flutter-plugin 項(xiàng)目
生成aar包
或者直接在flutter-module中找到plugin的aar
找到aar后信姓,直接把a(bǔ)ar文件放在libs目錄下鸵隧。
運(yùn)行,完畢意推,可用掰派。