Flutter混合開發(fā)項目搭建

官方提供的集成步驟詳見:
https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps
目前混合開發(fā)的集成方案還是preview版本,所以實際使用過程中,容易碰到各種問題,還不夠穩(wěn)定。
本文配置基于:
1.7.8-hotfix-4
官方文檔只能用于master分支,所以會有一些細節(jié)差異。

創(chuàng)建Flutter工程

假設(shè)當前目錄結(jié)構(gòu)如下,ios和android工程在同一目錄下:

some/path/
  - android_project/
  - ios_project/

進入some/path/目錄,創(chuàng)建Flutter module:

flutter create -t module --org com.example my_flutter

執(zhí)行完后,目錄結(jié)構(gòu)如下:

some/path/
  - android_project/
  - ios_project/
  - my_flutter

my_flutter會生成隱藏的.android和.ios文件夾,注意盡量不要在這兩個隱藏文件夾里面添加自己的代碼,在pubspec.yaml更新時,這兩個文件夾會被重新覆蓋,導(dǎo)致后來添加的文件丟失。

配置Android工程

修改原生工程android_project下的app module中的build.gradle,在android{}配置中添加一下內(nèi)容

android {
  //...
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}

依賴Flutter Module工程的方式有兩種:依賴aar與直接依賴源碼

依賴aar

$ cd some/path/my_flutter
$ flutter build aar

注意:build aar命令到1.7.8-hotfix-4還不支持,master分支已經(jīng)更新。
在flutter module工程,使用flutter build命令構(gòu)建出一個aar,然后android_project就去配置依賴這個aar,這樣做的優(yōu)點是android_project項目編譯的時候不需要去依賴Flutter SDK,
直接引用aar即可,缺點是開發(fā)的時候,沒法一鍵運行。

依賴源碼

依賴源碼,可以一鍵運行,不過android_project項目編譯的時候需要去依賴Flutter SDK。
android_project項目在setting.gradle添加以下代碼

include ':app'                                     // assumed existing content
setBinding(new Binding([gradle: this]))                                 // new
evaluate(new File(                                                      // new
  settingsDir.parentFile,                                               // new
  'my_flutter/.android/include_flutter.groovy'                          // new
))     

目錄結(jié)構(gòu)如下:


0bc3eb99-face-4ab8-af0b-de3f3a4028e3.png

Flutter工程以及在pubspec.yaml引用的library,就能作為一個project被引入,最后在build.gradle添加依賴Flutter工程:

dependencies {
  implementation project(':flutter')
}

調(diào)起Flutter界面

Android端

1、使用FlutterView

// MyApp/app/src/main/java/some/package/MainActivity.java
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);

2、使用FlutterFragment

// MyApp/app/src/main/java/some/package/MainActivity.java
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.someContainer, Flutter.createFragment("route1"));
tx.commit();

3、使用FlutterActivity

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
  }
}

FlutterActivity可以通過intent指定routeName

intent.putExtra("route", "initRoute");

Flutter端

import 'dart:ui';
import 'package:flutter/material.dart';

void main() => runApp(_widgetForRoute(window.defaultRouteName));

Widget _widgetForRoute(String route) {
  switch (route) {
    case 'route1':
      return SomeWidget(...);
    case 'route2':
      return SomeOtherWidget(...);
    default:
      return Center(
        child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
      );
  }
}

創(chuàng)建FlutterView或者FlutterFragment時,傳入了一個initialRoute,也就是window.defaultRouteName的值,這樣我們就能夠根據(jù)routeName來決定跳轉(zhuǎn)到哪個Flutter頁面。

配置ios工程

集成Flutter module工程到flutter_host_ios工程需要Cocoapods依賴項管理器,請確保本地安裝了cocoapods,如果未安裝,可以參考:cocoapods.org/
1、如果ios_project工程中已經(jīng)使用了cocoapods,將下列配置添加到工程的Podfile文件中:

flutter_application_path = 'path/to/my_flutter/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

2、找到項目TARGETS的Build Phases,點擊左上角+號選擇New Run Script Phase添加Run Script,在Shell字段下添加下面兩行腳本

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

這段跟官方文檔有差異,如果是master分支,參考官方文檔。

2、執(zhí)行pod install
每次pubspec.yaml依賴有更新時,都需要重新執(zhí)行pod install

3、因為Flutter現(xiàn)在不支持bitcode,需要禁用項目TARGETS的Build Settings-> Build Options-> Enable Bitcode部分中的ENABLE_BITCODE標志。

調(diào)起Flutter界面

在ios_project中,將AppDelegate改為繼承FlutterAppDelegate。
AppDelegate.h

#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>AppDelegate.m

@interface AppDelegate : FlutterAppDelegate
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end

AppDelegate.m

#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Only if you have Flutter Plugins

#include "AppDelegate.h"

@implementation AppDelegate

// This override can be omitted if you do not have any Flutter Plugins.
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
  [self.flutterEngine runWithEntrypoint:nil];
  [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

ViewController.m

#import <Flutter/Flutter.h>
#import "AppDelegate.h"
#import "ViewController.h"

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self
               action:@selector(handleButtonAction)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"Press me" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor blueColor]];
    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
    [self.view addSubview:button];
}

- (void)handleButtonAction {
    FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];
    FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
    [self presentViewController:flutterViewController animated:false completion:nil];
}
@end

與Android一樣,F(xiàn)lutterViewController也可以設(shè)置routeName

[flutterViewController setInitialRoute:@"route1"];

但是,這個api目前基本是無法派上用場,上述例子共用了FlutterEngine,在runWithEntrypoint執(zhí)行之后,main.dart中runApp代碼已經(jīng)執(zhí)行,后面再去setInitialRoute已經(jīng)沒有效果,F(xiàn)lutterViewController使用initWithNibName等其他方式初始化,不共用FlutterEngine,main.dart還沒執(zhí)行,這種方式setInitialRoute能夠起作用,不過跳轉(zhuǎn)到Flutter頁面時,會展示launch頁面,體驗不好。

熱重載

在宿主app進程運行以后,進入my_flutter,執(zhí)行:

$ cd some/path/my_flutter
$ flutter attach
Waiting for a connection from Flutter on Nexus 5X...

attach成功之后,就可以隨時修改flutter代碼,進行熱重載更新。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容