背景
Flutter已經是大勢所趨,關於Flutter跟原生的種種對比和優劣比較,我這裡就不多贅述。畢竟我也只是一個初學者,只有自己使用下來的感受和總結才最直接,對吧?所以,我們不妨抱著學習的心態來一起嘗試一下這個代表未來的跨端方案。
那麼,既然要嘗試,肯定不能all in,畢竟大家的老專案要是一把推翻重來,這種風險誰都扛不住的,包括阿里、騰訊這種大廠。而新專案又一般都會很趕時間,不允許我們去慢慢踩坑。當然如果公司有一些小專案,週期又不是很趕的,個人認為是可以拿Flutter練練手的,當然前提是你們的領導也對前沿技術感興趣,敢於放手讓你們去玩。那麼排除以上幾點情況,我們大多數的應用場景還是想在現有專案中,在一些深層的頁面嘗試混編,所以業界一般也還是採用這種混編方案,據我瞭解,包括閒魚、哈羅、美團等大廠,從他們的分享資料來看,確實也還只是在嘗試階段,在部分功能採取了混編的方案,這樣一來,對新技術有探索,對現有功能影響也不大。
好了,以上講了那麼多,下面來分享一下這兩天,我搜遍百度、google各種大大小小文章,踩了各種奇奇怪怪、莫名其妙的坑,最後終於集齊七顆龍珠,召喚神龍的故事。???
Flutter整合進現有原生應用,Flutter官方提供了3種方案:
- 方案1:用Flutter官方已經為我們寫好的指令碼,簡單實現整合,我照著嘗試了一遍,很方便,但有一個很明顯的缺點,需要組內人員都具備Flutter環境。而我們想要的是,組內其他成員無感整合。
- 方案2:適用於沒有使用cocoapods的團隊,完全手動整合,手動修改framework的相關屬性。但國內,大多數團隊還是用的cocoapods,所以適用這一條的也不多。
- 方案3:使用cocoapods整合,但是用的是本地倉庫,並不友好,很難進行版本控制。
綜上所述,如果你想自己玩,參考Flutter官方的方案即可,但是如果想要團隊開發,並且實現團隊內部其他成員無感開發,我們就需要利用一下cocoapods的私有庫了。接下來我們進行實戰,同時也附帶一些原理解析或者注意事項。
實戰
一、建立Flutter module
首先第一步,建立Flutter module(這裡請注意,是module,而不是工程,我最最開始的時候沒注意,用工程試了半天,導致我找不到我們後面需要的framework)
cd到自己的目錄,輸入flutter create --template module my_flutter(my_flutter是module名,你可以自己取)
cd some/path/
flutter create --template module my_flutter
複製程式碼
建立完flutter module,我們可以用VSCode或者Android Studio執行起來看看,因為Flutter自帶demo,不像iOS一進來是空白頁面。
二、生成framework
從flutter的整合原理上來說,我們其實只需要幾個檔案。
最簡單的來說,有以上幾個檔案我們團隊其他成員就可以整合Flutter了。這兩個檔案的生成也很簡單,用命令“flutter build ios”即可,生成的framework在你建立的Flutter工程的一個隱藏資料夾(.ios)裡面。
初期驗證,我們只需要這幾個檔案,當然這幾個檔案我們需要把它copy出來放到pod倉庫指定目錄,這裡我們可以用指令碼完成。想參考的同學可以拿去用,build_path自己修改,後面私有pod庫建完之後,可以直接填寫pod庫的路徑。release版本還是debug版本自己選擇,debug用於模擬器除錯,release用於真機除錯或者打包。
#!/bin/sh
#終端輸入 echo $PATH 檢視PATH路徑
# chmod 755 build.sh 獲取許可權
#PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
#export PATH
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
echo "Clean old build"
find . -d -name "build" | xargs rm -rf
#flutter clean
echo "開始獲取 packages 外掛資源"
flutter packages get
echo "開始構建 release for ios"
flutter build ios --debug
echo "構建 release 已完成"
echo "開始 處理framework和資原始檔"
build_path="../FlutterMixDemo/build_flutter_ios"
if [ -d ${build_path} ]; then
find ${build_path} -name \*.framework | xargs rm -rf
#find ${build_path} -name \*.h | xargs rm -rf
#find ${build_path} -name \*.m | xargs rm -rf
else
mkdir ${build_path}
fi
#cp -r build/ios/Release-iphoneos/*/*.framework ${build_path}
cp -r .ios/Flutter/App.framework ${build_path}
cp -r .ios/Flutter/engine/Flutter.framework ${build_path}
cp -r .ios/Flutter/FlutterPluginRegistrant/Classes/GeneratedPluginRegistrant.* ${build_path}
# cd ../FlutterMixDemo
# pod install
echo done
複製程式碼
三、建立Pod私有庫
我們後期是需要把Flutter module放進Pod私有庫內,讓組內成員一鍵整合的,所以我們要把Pod私有庫建立起來,當然,這一Part你要是已經會了可以跳過。我這裡做一個完整版的整合。
參考pod官方文件,注意它的第一步,唉,我這種粗心的人就是這樣踩坑的。
請注意,是spec倉庫,而不是我們的原始碼倉庫,這是私有庫和開源庫的區別,私有庫需要我們自己去管理Spec倉庫。
下面的步驟就簡單了,按照官方文件操作。這裡建議把原始碼倉庫放在gitlab上,不要放在github上(雖然github已經免費私有庫),因為後面的pod repo push或者pod install的速度會讓你昏死過去。我已經浪費了很多寶貴的時間,希望你們不要重蹈我的覆轍。
- 第一步,建立私有庫,pod lib create REPO_NAME,用來放我們的Flutter庫
- 第二步,建立spec庫,用來放我們的podspec檔案
- 第三步,pod repo add REPO_NAME SOURCE_URL(REPO_NAME:庫的名字,SOURCE_URL:spec庫的地址,以.git結尾),舉例:pod repo add flutterspec http://192.168.100.4/zsy/flutterspec.git
- 第四步,檢視~/.cocoapods/repos/,應該多了一個剛剛新增的倉庫,但應該只是一個空的資料夾,我們把遠端spec庫連結上這裡,庫需要提交一下,類似於提交一個README那種,初始化一下。
- 第五步,回到我們的原始碼庫,先pod repo lint --allow-warnings檢查一下,然後pod repo push REPO_NAME SPEC_NAME.podspec,把我們的podspec檔案push到我們的私有庫,成功之後回到我們的~/.cocoapods/repos/,就可以看到那個空資料夾有了具體的版本庫。
- 好,私有庫建立完成。
四、一鍵匯入
platform :ios, '11.0'
source 'http://192.168.100.4/zsy/flutterspec.git'
target 'FlutterIOS' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for FlutterIOS
pod 'flutter_test_sdk', '0.1.4'
end
複製程式碼
這裡要注意的是,因為是私有庫,所以我們pod檔案需要加一個source連結,就是我們的spec倉庫連結,然後就是pod install。
回到我們demo工程裡面,可以看到已經整合進來了。
至於如何使用,就很簡單了,Flutter官方文件也很詳細,我也是copy過來的。
先註冊flutter引擎。
import UIKit
import flutter_test_sdk
@UIApplicationMain
class AppDelegate: FlutterAppDelegate {
lazy var flutterEngine = FlutterEngine(name: "my flutter engine")
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
flutterEngine.run()
GeneratedPluginRegistrant.register(with: self.flutterEngine)
self.window?.backgroundColor = UIColor.white
self.window?.rootViewController = ViewController()
self.window?.makeKeyAndVisible()
return true
}
}
複製程式碼
在需要呼叫Flutter頁面的地方撥出FlutterViewController。
import UIKit
import flutter_test_sdk
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
let flutterVC = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(flutterVC, animated: true, completion: nil)
}
}
複製程式碼
我們看一下demo效果。完美撥出Flutter頁面。不過我們可以看到,我們dismiss之後,重新撥出,其實Flutter頁面並不會初始化的,當然這些都是後話啦。
總結
元件化完成後,後面就是純開發工作了,做Flutter的同學專心攻Flutter,做原生的同學不需要關心Flutter環境。當然這才是萬里長征第一步,後面的踩坑之旅有待我慢慢繼續記錄???。
看過覺得有用的幫忙點個贊吧???,碼字不易,且看且珍惜。