Flutter混編方案
- Flutter 工程作為原生工程共用的子模塊,維持原有的原生工程管理方式不變。這種模式,就是三端分離模式。
- 除了實(shí)現(xiàn)輕量級接入,還可以快速實(shí)現(xiàn) Flutter 功能的“熱插拔”,降低原生工程的改造成本。而 Flutter 工程獨(dú)自管理,無需打開原生工程,可直接進(jìn)行 Dart 代碼和原生代碼的開發(fā)調(diào)試。
-
三端工程分離模式的關(guān)鍵是抽離 Flutter 工程,將不同平臺的構(gòu)建產(chǎn)物依照標(biāo)準(zhǔn)組件化的形式進(jìn)行管理,即 Android 使用 aar、iOS 使用 pod。換句話說,將 Flutter 模塊打包成 aar 和 pod,這樣原生工程就可以像引用其他第三方原生組件庫那樣快速接入 Flutter 了。Flutter混編工程管理方式
平臺通道架構(gòu)設(shè)計(jì)
消息使用platform channels(平臺通道)在客戶端(UI)和宿主(平臺)之間傳遞;

調(diào)用過程大致如下:
1.客戶端(Flutter端)發(fā)送與方法調(diào)用相對應(yīng)的消息;
2.平臺端(iOS、Android端)接收方法,并返回結(jié)果;
- iOS端通過FlutterMethodChannel做出響應(yīng);
- Android端通過MethodChannel做出響應(yīng);
集成Flutter
現(xiàn)有的Flutter是一個(gè)Application項(xiàng)目,現(xiàn)在想把它嵌入原生應(yīng)用中。首先需要將現(xiàn)有Flutter工程轉(zhuǎn)成Module工程,在flutter 工程中的pubspec.yaml 文件中flutter節(jié)點(diǎn)下添加如下配置,然后pug get跑一下
module:
androidX: true
androidPackage: com.ganyuan.flutter_module
iosBundleIdentifier: com.ganyuan.flutterModule

.android 和 .ios 文件夾,這兩個(gè)文件是flutter applicaiton沒有的,在 mac上是兩個(gè) 隱藏文件夾,window 是可見的
Android中集成Flutter module
Flutter 可以作為 Gradle 子項(xiàng)目源碼或者 AAR 嵌入到現(xiàn)有的 Android 應(yīng)用程序中。
請注意
你目前現(xiàn)有的 Android 項(xiàng)目可能支持 mips 或 x86 之類的架構(gòu),然而,F(xiàn)lutter 當(dāng)前僅支持 為 x86_64,armeabi-v7a 和 arm64-v8a 構(gòu)建預(yù)編(AOT)的庫。
可以考慮使用 abiFilters 這個(gè) Android Gradle 插件 API 來指定 APK 中支持的架構(gòu),從而避免 libflutter.so 無法生成而導(dǎo)致應(yīng)用運(yùn)行時(shí)崩潰,具體操作如下:
android {
//...
defaultConfig {
ndk {
// Filter for architectures supported by Flutter.
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
}
Flutter 引擎支持 x86 和 x86_64 的版本,在模擬器以 debug 即時(shí)編譯 (JIT) 模式運(yùn)行時(shí), Flutter 模塊仍可以正常運(yùn)行。
首先在本地安裝Flutter的SDK及環(huán)境配置,FlutterSDK安裝教程
安裝完成后,將我們的Android工程和Flutter工程(默認(rèn)已創(chuàng)建好)放到同一目錄下,如下圖所示

該方式可以使你的 Android 項(xiàng)目和 Flutter 項(xiàng)目能夠同步一鍵式構(gòu)建。當(dāng)你需要同時(shí)在這兩個(gè)項(xiàng)目中進(jìn)行快速迭代時(shí),這種方案非常方便。
- 將 Flutter 模塊作為子項(xiàng)目添加到宿主應(yīng)用的
settings.gradle中:
// Include the host app project.
include ':app' // assumed existing content
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'flutter_module/.android/include_flutter.groovy' // new
)) // new
binding 和 evaluation 腳本可以使 Flutter 模塊將其自身(如 :flutter)和該模塊使用的所有 Flutter 插件(如 :package_info,:video_player 等)都包含在 settings.gradle 的評估的上下文中。
- 在你的應(yīng)用中引入對 Flutter 模塊的依賴:
dependencies {
implementation project(':flutter')
}
- 確保Flutter工程和Android工程編譯的SDK版本保持一致,如下圖在app級
build.gradle文件中修改
flutter工程Android工程 - (可選)盡量保證兩個(gè)工程的gradle版本和jdk版本和引用路徑保持一致,不然可能編譯會報(bào)錯(cuò)設(shè)置Android工程默認(rèn)jdk路徑,在gradle和jdk版本及引用路徑
/File/News Projects Setup/Default Project Structure(根據(jù)Android Studio版本不同可能路徑不同)里設(shè)置設(shè)置默認(rèn)jdk路徑 - (可選)添加
kotlin依賴支持,盡量保證Android和Flutter工程支持的kotlin版本一致
buildscript {
ext.kotlin_version = '1.8.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin-android'
dependencies {
...
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
- 執(zhí)行
gradle sync,這一步建議打開科學(xué)上網(wǎng)
如果報(bào)Failed to apply plugin class ‘FlutterPlugin’的錯(cuò)誤,則需要把settings.gradle中的(RepositoriesMode.FAIL_ON_PROJECT_REPOS)改為(RepositoriesMode.PREFER_PROJECT),如下圖:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
repositories {
google()
mavenCentral()
}
}
如果沒法科學(xué)上網(wǎng)的同學(xué)可以在settings.gradle中添加如下代碼
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}
}
同時(shí)在project的build.gradle中添加
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}
}
再次執(zhí)行gradle sync,build成功后,你的應(yīng)用程序已將 Flutter 模塊添加為依賴項(xiàng)。
7.在 Android 應(yīng)用中添加 Flutter 頁面
這里我們以添加一個(gè)Flutter Fragment為例
首先我們在需要展示Flutter頁面的地方事先預(yù)熱一個(gè)Flutter Engine,以提高Flutter頁面初始加載速度
// Somewhere in your app, before your FlutterFragment is needed,
// like in the Application class ...
// Instantiate a FlutterEngine.
FlutterEngine flutterEngine = new FlutterEngine(this);
// Start executing Dart code in the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
);
// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
然后在FlutterFragment中使用預(yù)熱的 FlutterEngine,可以使用工廠方法 withCachedEngine()實(shí)例化 FlutterFragment。
FlutterFragment.withCachedEngine("my_engine_id").build();
FlutterFragment 內(nèi)部可訪問FlutterEngineCache,并且可以根據(jù)傳遞給 withCachedEngine() 的 ID 獲取預(yù)熱的 FlutterEngine。
MainActivity.java代碼:
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Somewhere in your app, before your FlutterFragment is needed,
// like in the Application class ...
// Instantiate a FlutterEngine.
FlutterEngine flutterEngine = new FlutterEngine(this);
// Start executing Dart code in the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
);
// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController);
}
}
HomeFragment.java代碼:
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment fragment = FlutterFragment.withCachedEngine("my_engine_id").build();
getParentFragmentManager().beginTransaction().add(this.getId(), fragment).commit();
}
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
HomeViewModel homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
// final TextView textView = binding.textHome;
// homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
demo運(yùn)行效果:




