Flutter-boot 你集成成功了嗎?

flutter-boot

首先區分兩個概念'flutter-boot''flutter_boost'都是alibba開源的flutter工具,可是是兩個徹底不一樣的東西。node

FlutterBoost

新一代Flutter-Native混合解決方案。 FlutterBoost是一個Flutter插件,它能夠輕鬆地爲現有原生應用程序提供Flutter混合集成方案。FlutterBoost的理念是將Flutter像Webview那樣來使用。在現有應用程序中同時管理Native頁面和Flutter頁面並不是易事。 FlutterBoost幫你處理頁面的映射和跳轉,你只需關心頁面的名字和參數便可(一般能夠是URL)。android

Flutter-boot

這是一個幫助你在已有原生應用的狀況下,搭建flutter混合開發環境的工具。 咱們提供了標準的混合工程結構,同時支持混合棧(一套原生和flutter以前頁面通訊和過渡的方案)的快速接入。ios

以上是摘抄GitHub上flutter-boot 和 flutter_boost的官方介紹,也由於官方文檔上的集成步驟太過簡略,在集成的過程當中才過了很多的坑,因此都記錄下來而且分享出來,但願能夠給看文章的你,帶去一些解決方案和思路。git

本文主要記錄一次flutter-boot的集成過程,對於flutter_boost的使用放在後續的文章中再介紹。github

Native工程環境

已有一個維護迭代過多個版本的Native工程,而且已經作過組件化,拆分了多個組件(就是常見的組合組件的方法,經過CocoaPods的方式添加安裝各個組件,製做CocoaPods遠程私有庫,將其發不到公司的gitlab或GitHub,使工程可以Pod下載下來),本次集成flutter-boot的目的是要把其中一個組件,使用flutter重寫。shell

這裏強調作過組件化,是由於不少問題都是由於組件化了才引發的若是隻是一個單純的工程集成的話,會簡單很多,並且後續若是隻是某個組件須要依賴flutter,還須要有一些特殊的操做。npm

Flutter-boot集成步驟和遇到的問題

這裏能夠跟着我一塊兒來操做,下面的步驟我都寫得儘可能詳細,遇到的問題都有截圖或者文字描述,以及解決方案跟在後面。ruby

  1. $ cd somepath/my_repo

這裏能夠是你的Native路徑 , 也能夠是一個新建立的文件夾,這裏我是用了一個新的文件夾路徑,目的是保證以後的路徑和官方文檔上的目錄結構一致。bash

somepath/my_repo
└──my_android
└──my_ios
└──my_flutter
    └──.git
    └──.gitignore
    └──android_shell
    └──ios_shell
    └──android
    └──ios
複製代碼
  1. $ flutter-boot init

問題一:link 失敗,這裏忘記截圖了。。。特別抱歉app

解決方案:

  • 出現這個問題的緣由,是由於flutter-boot的版本支持問題,官方文檔上還要求裝1.5.0的版本(其實已經支持到1.9了),可是通常咱們電腦上的版本都比這個高,因此,要先下降版本,切換到master分支,而後執行init命令
擁有^1.5.0的flutter環境
複製代碼
  • 找到npm包中flutter-boot的源碼,修改一下判斷版本號的代碼,不過。。。不推薦這種方式(可是親測有效,畢竟來回切換版本號太浪費時間了)
  1. $ 請輸入flutter工程名稱: my_flutter_module (能夠回車跳過,會自動生成一個叫my_flutter_module的module)
  2. $ 請輸入flutter倉庫地址,回車跳過
  3. $ ? 是否存在iOS工程? Yes
  4. $ ? iOS工程本地地址 somepath/my_repo/my_ios 這裏輸入你的native工程目錄

問題二:podfile內容copy出錯,你也可能不會遇到這個問題,這裏是flutter-boot自己的bug,只去匹配了字符串'end',恰好咱們使用了這個日曆的庫,就到這裏把podfile文件中的內容給截斷了

pod 'FSCalendar', '2.8.0' # 日曆
複製代碼

解決方案:

  • 在執行flutter-boot init 以前檢查一下你的podfile文件,看是否也存在這種字符中含有'end',能夠先刪除掉,init命令執行完以後再手動添加,注意原有target和新生成的Runner target中都要添加
  • 查看從那裏開始copy截斷,手動把後面的補齊
  • 注意 只copy pod 引用 不須要copy post_install中的內容(若是你的podfile中有post_install的話)
post_install do |installer_representation|
    installer_representation.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            ...
        end
    end
end
複製代碼

問題三:

這裏是你工程的post_install和init命令生成的fbpodhelper.rb中的post_install衝突致使,

解決方案:

  • 仍是由於下降了flutter版本致使的,使用flutter 1.9版本就不會有這個問題,可是1.5會報這個錯誤
  • 還有同事反饋,pod版本升級到最新也不會有這個問題,個人是1.6.1,目前最新已是1.8以上了,我尚未試這個-。-
  • 修改fbpodhelper.rb中以下代碼:
flutter_application_path = fbFlutterPath
require File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
複製代碼

改成:

# ============= Flutter config begin ============== #
  
  flutter_application_path = './fpf_flutter/'
  podhelper_path = File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
  
  podhelper_content = File.read(podhelper_path);
  podhelper_post_isntall = "post_install do |installer|";
  # 當post_install重複時須要去重以免發生pod install錯誤:multiple post_install hooks is unsupported
  if podhelper_content.scan(/(#{podhelper_post_isntall})/).length > 0 then
    
    podhelper_buffer = podhelper_content.gsub(podhelper_post_isntall, "def update_configs(installer, framework_dir)")
    eval(podhelper_buffer, binding)
    
    else
    eval(File.read(podhelper_path), binding)
  end
  
  # ============= Flutter config end ============== #
複製代碼
  1. 混合工程初始化完成
info [init] 你能夠在建立Android工程後調用 flutter-boot link來關聯flutter
info [init] 混合工程初始化完成
複製代碼
  1. 運行flutter

你覺得到這裏就真的完成了嗎,run一下~~ 記得要選擇 Runner target,好像確實能夠成功。可是flutter-boot的做用是你能夠有兩個開發視角,flutter視角下,不須要關心native,native視角,甚至能夠不用安裝flutter環境。那麼,去flutter視角看一下,把flutter module拖入VSCode打開,而後fn+f5,運行起來

那麼在flutter視角運行起來了嗎

問題四: Run Flutter Build Script 中的腳本./my_flutter_module/.ios/Flutter/flutter_export_environment.sh 路徑找不到,一個很奇怪的現象,pod install成功後查看路徑,沒問題,可是flutter run以後,這裏的路徑就改變了,相對路徑錯誤,確實找不到這個腳本

解決方案:

  • $ cd ./my_flutter_module/.ios/Flutter/podhelper.rb
  • 打開這個文件,其實就是一個ruby腳本,找到設置flutter_export_environment.sh路徑的代碼,作一下修改,改成:
flutter_export_environment_path = File.join(project_directory_pathname, relative, 'flutter_export_environment.sh');
複製代碼
  1. AppDelegate 中註冊flutter 引擎

In AppDelegate.h:

@import UIKit;
@import Flutter;

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

In AppDelegate.m:

@import FlutterPluginRegistrant; // Only if you have Flutter Plugins

#import "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
複製代碼

In ViewController.m

#import "AppDelegate.h"
#import "ViewController.h"
@import Flutter;

@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
複製代碼

至此,flutter-boot 環境集成成功!!! 喜大普奔啊,終於不報錯了~~

可是,回到咱們最初集成flutter-boot的目的,是要使用flutter重寫某一個業務線。由於是組件化的工程,因此flutter引擎的註冊也不但願暴露在APPdelegate中,而是放在一個管理各個業務線分發的平臺組件中,那麼就涉及到,某一個組件中要使用flutter。

用唄,主工程都能用,組件同樣能用,而後,當你在組件代碼中添加以下代碼以後:

#import <Flutter/Flutter.h>
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
複製代碼

找不着,各類找不着啊。。。好像各個業務線組件根本就不能引用flutter以及flutter_module

解決方案:

  • 各業務線的podspec文件中添加依賴:
  • $ s.dependency 'Flutter'
  • $ s.dependency 'FlutterPluginRegistrant'

到這裏集成flutter-boot 纔算取得了階段性的勝利✌️,但願這篇文章,能夠給廣大敢於嘗試flutter-boot的開發人員,帶去一些福利(主要是節省一些時間,都不是什麼難解決的問題,就是得不斷的嘗試)。

在這個過程當中,也去看了GitHub上他們全部開放和關閉的issues,而且嘗試了N次,過程比較坎坷,不過成功了仍是很可的事😃。

2019.12.11 補充 flutter-boot 推到遠端以後,其餘同事拉代碼,裝flutter-boot以後遇到的一些問題記錄:

問題一:

產生緣由: 曾用 root 用戶進行了局部安裝npm包,留下所屬權爲 root 的文件,致使普通用戶 沒法訪問 root的文件內容。

解決方案:

  • 找到報無權限文件夾:/usr/ local/lib/node_ modules/ flutter -boot/node_ modules/core-js
  • 查看無權限文件夾的權限:ls -la /usr/ local/lib/node_ modules/ flutter -boot/node_ modules/core-js
    發現權限擁有者是root,但應該是咱們本機用戶
  • 更改權限擁有者(後面是用戶名和文件夾名): sudo chown -R 用戶名 /usr/ local/lib/node_ modules/ flutter -boot/node_ modules/core-js
  • 再次查看文件夾權限就改爲用戶了,npm包就能夠正常下載啦。

問題二:my_flutter_module中數據缺失

就是別的同事拉下flutter_module中的代碼裏面,沒有engine這個文件夾

解決方案:

  • .gitignore中去掉對這個文件夾的忽略,而後再push一次

問題三: native視角如何使用

其實這個也不能算是個問題啦,應該歸到上面的使用步驟裏面,畢竟這個flutter-boot的環境搭建起來,是要給你們一塊兒用的,又不是指給本身用😀

其實切換到native視角,就會以爲這個工具,和我最開始選擇用flutter-boot的初衷有一些偏離。最初,咱們是但願有一套徹底不影響原生開發的框架,去接入flutter,徹底不影響是什麼意思呢,領導但願,native開發,不須要安裝flutter環境,不須要,不須要,不須要,重要的地方強調一下,可是經過這幾天對flutter-boot的研究,發現根本作不到這一點。

首先別人拉下native代碼和flutter代碼以後,須要執行link命令去作軟鏈接,可是自己flutter-boot就是依賴flutter庫的,可能說依賴不太準確,可是link命令中就有判斷flutter版本的邏輯,若是沒有flutter的開發環境,這一步就過不去,那你的native和flutter如何連接上呢??

解決方案:

  • 這個問題其實沒有解決,只是有一個思路
  • flutter的全部產物,都是一個framework ,咱們能夠手動把這些framework拖入到工程中,不用pod引入
  • 其實大部分依賴的framework 都只須要拖進去一次(這裏不考慮更新)
  • 只有App.framework 須要頻繁的修改,這裏能夠本身進入一些腳本,重定向他的產物
  • native工程中以前的依賴和各類path 都須要修改
  • 這個方案,理論上可行,可是須要改動的地方太大,還有各類維護的問題,成本都很大

若是看這篇文章的小夥伴,有更好的方式,歡迎交流!!!

相關文章
相關標籤/搜索