相關(guān)鏈接侧蘸,如有需要請移步:
Flutter-已有iOS工程中加入Flutter之Cocoapods+Flutter環(huán)境方式集成
Flutter-已有iOS工程中加入Flutter之遠(yuǎn)程Pod產(chǎn)物方式集成
Flutter-遠(yuǎn)程Pod集成Flutter腳本優(yōu)化
一奢讨、環(huán)境
Mac OS:10.14.6
Xcode:11.3
Flutter:1.12.13+hotfix.5 ? channel stable ? Dart 2.7.0
已經(jīng)安裝有Cocoapods,如果沒有安裝請移駕這里
如果集成方式有更新,請看最新官方文檔
Note: 下載好demo后盛杰,請在kk_flutter項目中分別運行flutter pub get
和flutter build ios --debug
后翼闽,再在Xcode上運行項目。否則可能會遇見Command PhaseScriptExecution failed with a nonzero exit code
錯誤笙什,或者Flutter/Flutter.h' file not found
錯誤
Note:應(yīng)用程序?qū)o法在Release模式下運行到模擬器上飘哨,因為Flutter尚不支持Dart代碼的輸出x86預(yù)編譯(AOT)二進(jìn)制文件。 您可以在模擬器或真實設(shè)備上以Debug模式運行琐凭,而在真實設(shè)備上以Release模式運行芽隆。
二、集成方式
官方說有兩種集成方式,分別是:
- 使用CocoaPods依賴性管理器和已安裝的Flutter SDK(官方推薦)胚吁。
- 為Flutter引擎牙躺,已編譯的Dart代碼和所有Flutter插件創(chuàng)建Framework。手動嵌入Framework腕扶,并在Xcode中更新現(xiàn)有應(yīng)用程序的構(gòu)建設(shè)置孽拷。
三、創(chuàng)建Flutter_Module
- 進(jìn)入到和我們iOS項目同級別的文件夾目錄下
KKdeMacBook-Pro:~ kaye$ cd Desktop/NativeFultter/
- 創(chuàng)建flutter_module半抱,這里將flutter_module命名為“kk_flutter”
Note:flutter_module的命名要符合dart package name規(guī)范脓恕,要以小寫單詞和_相連,否則創(chuàng)建不成功
先給一個不規(guī)范的例子:
Last login: Wed Dec 25 09:05:21 on console
KKdeMacBook-Pro:~ kaye$ cd Desktop/NativeFultter/
KKdeMacBook-Pro:NativeFultter kaye$ flutter create -t module KKFlutter
╔════════════════════════════════════════════════════════════════════════════╗
║ Welcome to Flutter! - https://flutter.dev ║
║ ║
║ The Flutter tool uses Google Analytics to anonymously report feature usage ║
║ statistics and basic crash reports. This data is used to help improve ║
║ Flutter tools over time. ║
║ ║
║ Flutter tool analytics are not sent on the very first run. To disable ║
║ reporting, type 'flutter config --no-analytics'. To display the current ║
║ setting, type 'flutter config'. If you opt out of analytics, an opt-out ║
║ event will be sent, and then no further information will be sent by the ║
║ Flutter tool. ║
║ ║
║ By downloading the Flutter SDK, you agree to the Google Terms of Service. ║
║ Note: The Google Privacy Policy describes how data is handled in this ║
║ service. ║
║ ║
║ Moreover, Flutter includes the Dart SDK, which may send usage metrics and ║
║ crash reports to Google. ║
║ ║
║ Read about data we send with crash reports: ║
║ https://github.com/flutter/flutter/wiki/Flutter-CLI-crash-reporting ║
║ ║
║ See Google's privacy policy: ║
║ https://www.google.com/intl/en/policies/privacy/ ║
╚════════════════════════════════════════════════════════════════════════════╝
"KKFlutter" is not a valid Dart package name.
From the [Pubspec format
description](https://www.dartlang.org/tools/pub/pubspec.html):
**DO** use `lowercase_with_underscores` for package names.
Package names should be all lowercase, with underscores to separate words,
`just_like_this`. Use only basic Latin letters and Arabic digits: [a-z0-9_].
Also, make sure the name is a valid Dart identifier -- that it doesn't start
with digits and isn't a reserved word.
然后再來個正常的流程:
KKdeMacBook-Pro:NativeFultter kaye$ flutter create -t module kk_flutter
Creating project kk_flutter... androidx: true
kk_flutter/test/widget_test.dart (created)
kk_flutter/kk_flutter.iml (created)
kk_flutter/.gitignore (created)
kk_flutter/.metadata (created)
kk_flutter/pubspec.yaml (created)
kk_flutter/README.md (created)
kk_flutter/lib/main.dart (created)
kk_flutter/kk_flutter_android.iml (created)
kk_flutter/.idea/libraries/Flutter_for_Android.xml (created)
kk_flutter/.idea/libraries/Dart_SDK.xml (created)
kk_flutter/.idea/modules.xml (created)
kk_flutter/.idea/workspace.xml (created)
Running "flutter pub get" in kk_flutter... 2.9s
Wrote 12 files.
All done!
Your module code is in kk_flutter/lib/main.dart.
KKdeMacBook-Pro:NativeFultter kaye$
這個時候窿侈,我們的目錄結(jié)構(gòu)是這樣的:
進(jìn)入到kk_flutter中炼幔,目錄結(jié)構(gòu)類似于這樣:
kk_flutter/
├─.ios/ #隱藏文件,可以使用Command+shift+. 顯示隱藏文件
│ ├─Runner.xcworkspace
│ └─Flutter/podhelper.rb
├─lib/ #我們的代碼都寫在這個文件夾中
│ └─main.dart
├─test/
└─pubspec.yaml #flutter依賴的庫棉磨,都寫在這里
四江掩、將已經(jīng)創(chuàng)建的flutter_module集成到現(xiàn)有的iOS項目中
4.1 手動導(dǎo)入
如果你想直接手動形式集成Framewok,可以在kk_flutter路徑下乘瓤,使用flutter build ios-framework --output=你想要導(dǎo)出的Framework路徑(比如: /Users/kaye/Desktop/ios_flutter/NativeFlutter_module/Flutterframeworks)
环形,此時在你的指定目錄下,會自動生成三個文件件分別是Debug衙傀、Profile抬吟、Release,三個文件夾中分別有App.framework
统抬、Flutter.framework
和FlutterPluginRegistrant.framework
等多個Framework火本,命令行界面如下:
KKdeMacBook-Pro:kk_flutter kaye$ flutter build ios-framework --output=/Users/kaye/Desktop/ios_flutter/NativeFlutter_module/Flutterframeworks
Building framework for com.example.kkFlutter in debug mode...
├─Populating Flutter.framework... 172ms
├─Add placeholder App.framework for debug... 177ms
├─Assembling Flutter resources for App.framework... 6.0s
├─Building plugins... 3.2s
└─Moving to ../NativeFlutter_module/Flutterframeworks/Debug 0.0s
Building framework for com.example.kkFlutter in profile mode...
├─Populating Flutter.framework... 124ms
├─Building Dart AOT for App.framework... 20.0s
├─Assembling Flutter resources for App.framework... 0.1s
├─Building plugins... 10.0s
└─Moving to ../NativeFlutter_module/Flutterframeworks/Profile 0.0s
Building framework for com.example.kkFlutter in release mode...
├─Populating Flutter.framework... 672ms
├─Building Dart AOT for App.framework... 67.0s
├─Assembling Flutter resources for App.framework... 0.1s
├─Building plugins... 7.9s
└─Moving to ../NativeFlutter_module/Flutterframeworks/Release 0.0s
Frameworks written to /Users/kaye/Desktop/ios_flutter/NativeFlutter_module/Flutterframeworks.
將生成的三種模式下的Framework拖拽到項目中,并修改Xcode如下配置:
然后就可以在Xcode運行了聪建。
4.2 podfile導(dǎo)入
首先钙畔,此方法要求在您的項目上工作的每個開發(fā)人員都必須具有本地安裝的Flutter SDK版本。 只需在Xcode中構(gòu)建應(yīng)用程序即可自動運行腳本以嵌入Dart和插件代碼金麸。 這允許使用Flutter模塊的最新版本進(jìn)行快速迭代擎析,而無需在Xcode之外運行其他命令。
其次挥下,你的原生項目要有Podfile文件揍魂,如果沒有你可以通過命令行,進(jìn)入到原生應(yīng)用目錄中棚瘟,然后創(chuàng)建Podfile:
KKdeMacBook-Pro:~ kaye$ cd Desktop/NativeFultter/
KKdeMacBook-Pro:NativeFultter kaye$ ls
NativeFlutter_module kk_flutter
KKdeMacBook-Pro:NativeFultter kaye$ cd NativeFlutter_module/
KKdeMacBook-Pro:NativeFlutter_module kaye$ ls
NativeFlutter_module NativeFlutter_moduleTests
NativeFlutter_module.xcodeproj NativeFlutter_moduleUITests
KKdeMacBook-Pro:NativeFlutter_module kaye$ pod init
目前我們的現(xiàn)有應(yīng)用程序和flutter_module位于同級目錄中现斋。 如果您使用其他目錄結(jié)構(gòu),則可能需要調(diào)整相對路徑偎蘸。
some/path/
├── kk_flutter/ #flutter_module
│ └── .ios/
│ └── Flutter/
│ └── podhelper.rb
└── NativeFlutter_module/ #原生工程
└── Podfile
- 打開我們剛才在原生項目中創(chuàng)建的
Podfile
文件庄蹋,將下面兩句代碼添加到里面:
flutter_application_path = '../kk_flutter' #這里是我們創(chuàng)建的flutter_module的路徑
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
- 對于需要嵌入Flutter的每個Podfile目標(biāo)瞬内,請調(diào)用
install_all_flutter_pods(flutter_application_path)
。整個Podfile
文件內(nèi)容如下:
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
flutter_application_path = '../kk_flutter' #這里是我們創(chuàng)建的flutter_module的路徑
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'NativeFlutter_module' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for NativeFlutter_module
install_all_flutter_pods(flutter_application_path)
target 'NativeFlutter_moduleTests' do
inherit! :search_paths
# Pods for testing
end
target 'NativeFlutter_moduleUITests' do
inherit! :search_paths
# Pods for testing
end
end
- 運行
pod install
KKdeMacBook-Pro:NativeFlutter_module kaye$ pod install
Analyzing dependencies
Fetching podspec for `Flutter` from `../kk_flutter/.ios/Flutter/engine`
Fetching podspec for `FlutterPluginRegistrant` from `../kk_flutter/.ios/Flutter/FlutterPluginRegistrant`
Fetching podspec for `kk_flutter` from `../kk_flutter/.ios/Flutter`
Downloading dependencies
Installing Flutter (1.0.0)
Installing FlutterPluginRegistrant (0.0.1)
Installing kk_flutter (0.0.1)
Generating Pods project
Integrating client project
[!] Please close any current Xcode sessions and use `NativeFlutter_module.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There are 3 dependencies from the Podfile and 3 total pods installed.
KKdeMacBook-Pro:NativeFlutter_module kaye$
Note:當(dāng)您在kk_flutter/pubspec.yaml
中更改Flutter
插件的依賴性時蔓肯,請在flutter_module
目錄中運flutter pub get
來刷新podhelper.rb
腳本讀取的插件列表遂鹊。 然后振乏,從您的應(yīng)用程序的目錄下需要再次運行pod install
蔗包。
podhelper.rb
腳本將您的插件Flutter.framework
和App.framework
嵌入到您的項目中。
Flutter.framework
是Flutter引擎的捆綁軟件慧邮,而App.framework
是該項目的已編譯Dart代碼调限。
- 打開生成的
.workspace
文件,Command+B误澳,進(jìn)行build一下耻矮,發(fā)現(xiàn)貌似沒有什么問題。
我們再來看一下目前的Xcode目錄:
pod中生成的Frameworks.png
五忆谓、開始使用裆装,啟動FlutterEngine和FlutterViewController
要從iOS應(yīng)用中啟動一個Flutter界面,我們必須先創(chuàng)建FlutterEngine和FlutterViewController倡缠。
FlutterEngine充當(dāng)Dart VM和Flutter運行時的主機(jī)哨免,F(xiàn)lutterViewController依附于FlutterEngine,以將UIKit輸入事件傳遞到Flutter中并顯示FlutterEngine渲染的幀昙沦。
FlutterEngine的生存時間大于等于FlutterViewController的生存時間琢唾。
通常建議為應(yīng)用程序預(yù)先創(chuàng)建一個長生命周期的FlutterEngine,因為:
顯示FlutterViewController時盾饮,第一幀顯示會更快采桃。
您的Flutter和Dart狀態(tài)將超過一個FlutterViewController的生存時間。
在顯示UI之前丘损,您的應(yīng)用程序和插件可以與Flutter和Dart邏輯進(jìn)行交互普办。
5.1 創(chuàng)建FlutterEngine
考慮到上面所說,預(yù)先創(chuàng)建FlutterEngine徘钥,我們先在AppDelegate.h中創(chuàng)建一個引擎衔蹲,并且暴露出來一個屬性。然后在AppDelegate.m中注冊引擎吏饿。
// Appdelegate.h
#import <UIKit/UIKit.h>
@import Flutter; // 導(dǎo)入Flutter
@interface AppDelegate : FlutterAppDelegate //UIResponder <UIApplicationDelegate>
//@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong) FlutterEngine *flutterEngine;
@end
// AppDelegate.m
#import "AppDelegate.h"
#import <FlutterPluginRegistrant/FlutterPluginRegistrant-umbrella.h> //Used to connect plugins.
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"MyFlutterEngine"];
// 使用默認(rèn)的Flutter路由運行默認(rèn)的Dart入口點踪危。
// 當(dāng)在AppDelegate中創(chuàng)建的FlutterEngine上調(diào)用run時,默認(rèn)Dart庫的默認(rèn)main()入口點函數(shù)將運行猪落。
[self.flutterEngine run];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [super application:application didFinishLaunchingWithOptions:launchOptions];;
}
5.2 創(chuàng)建FlutterViewController
進(jìn)入我們原生iOS Demo工程中贞远,打開ViewController.h,寫入如下代碼笨忌,測試我們原生打開FlutterViewController蓝仲。
#import "ViewController.h"
#import "AppDelegate.h"
@import Flutter;
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *flutterButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 120, 40)];
[flutterButton setTitle:@"show flutter" forState:UIControlStateNormal];
[flutterButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[flutterButton addTarget:self action:@selector(flutterButtonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:flutterButton];
[self.navigationController setNavigationBarHidden:YES];
}
- (void)flutterButtonAction {
// 獲取引擎
FlutterEngine *flutterEngine = [(AppDelegate *)[UIApplication sharedApplication].delegate flutterEngine];
// 創(chuàng)建FlutterViewController
FlutterViewController *flutterVC = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
// 跳轉(zhuǎn)
[self.navigationController pushViewController:flutterVC animated:YES];
// 當(dāng)然你也可以使用下面的方法跳轉(zhuǎn)
//[self presentViewController:flutterVC animated:YES completion:nil];
}
至此,我們就已經(jīng)可以完成原生集成Flutter,并且完成跳轉(zhuǎn)的工作了袱结,效果如下:
另外亮隙,官方還給了另外一種使用隱式FlutterEngine的方式創(chuàng)建FlutterViewController,這種方式垢夹,我們不需要預(yù)先創(chuàng)建FlutterEngine溢吻,而去按需創(chuàng)建,尤其是我們Flutter界面很少果元,而且不知道什么時候會啟動Flutter界面的時候促王,會合適些,但是也犧牲了一些而晒,按照官方說的就是在首次顯示FlutterUI的時候蝇狼,會有些延遲,所以官方不是很推薦隱式創(chuàng)建FlutterEngine的方式倡怎。具體代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *flutterButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 120, 40)];
[flutterButton setTitle:@"show flutter" forState:UIControlStateNormal];
[flutterButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[flutterButton addTarget:self action:@selector(flutterButtonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:flutterButton];
UIButton *flutterButton2 = [[UIButton alloc] initWithFrame:CGRectMake(100, 300, 200, 40)];
[flutterButton2 setTitle:@"隱式創(chuàng)建引擎" forState:UIControlStateNormal];
[flutterButton2 setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[flutterButton2 addTarget:self action:@selector(createFlutterViewControllerWithImplicitFlutterEngine) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:flutterButton2];
[self.navigationController setNavigationBarHidden:YES];
}
- (void)createFlutterViewControllerWithImplicitFlutterEngine {
// 創(chuàng)建FlutterViewController
FlutterViewController *flutterVC = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
// 跳轉(zhuǎn)
[self.navigationController pushViewController:flutterVC animated:YES];
}
六迅耘、相關(guān)內(nèi)容的一些說明
6.1 FlutterAppDelegate
有的應(yīng)用可能不能像我們的demo中那樣,直接讓AppDelegate繼承自FlutterAppDelegate监署,這種方法是官方推薦颤专,好處就是可以監(jiān)聽到諸如點擊狀態(tài)欄回到頂部此類操作。
但是這并不是強(qiáng)制的焦匈,我們在不能直接繼承的情況下血公,為了能夠讓我們的Flutter能夠響應(yīng)一部分的App生命周期事件,我們可以在AppDelegate.h
中遵循FlutterAppLifeCycleProvider
,并在AppDelegate.m
中實現(xiàn)代理缓熟,以確保能夠監(jiān)聽到相應(yīng)的事件行為:
// AppDelegate2.h
@import Flutter;
@import UIKit;
@import FlutterPluginRegistrant;
@interface AppDelegate : UIResponder <UIApplicationDelegate, FlutterAppLifeCycleProvider>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
// AppDelegate2.m
#import "AppDelegate2.h"
@interface AppDelegate2 ()
@property (nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
@end
@implementation AppDelegate2
- (instancetype)init {
if (self = [super init]) {
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
}
return self;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"MyFlutterEngine"];
// 使用默認(rèn)的Flutter路由運行默認(rèn)的Dart入口點累魔。
// 當(dāng)在AppDelegate中創(chuàng)建的FlutterEngine上調(diào)用run時,默認(rèn)Dart庫的默認(rèn)main()入口點函數(shù)將運行够滑。
[self.flutterEngine run];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController {
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([viewController isKindOfClass:[FlutterViewController class]]) {
return (FlutterViewController*)viewController;
}
return nil;
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesBegan:touches withEvent:event];
// Pass status bar taps to key window Flutter rootViewController.
if (self.rootFlutterViewController != nil) {
[self.rootFlutterViewController handleStatusBarTouches:event];
}
}
//....
// 這里是生命周期代碼垦写,具體的可以參考Demo中AppDelegate2.m
@end
6.2 Dart 入口(Dart EntryPoint)
FlutterEngine調(diào)用run
方法,默認(rèn)情況下彰触,運行的是lib/main.dart
文件中的main()
入口梯投,我們也可以運行不同的入口,通過使用runWithEntrypoint况毅,傳入一個字符串參數(shù)分蓖,以使用其他的dart入口(入口在lib/main.dart
文件中),但是請注意尔许,除main()
之外的Dart入口點函數(shù)必須使用以下注釋:
// flutter
@pragma('vm:entry-point')
void myOtherEntryPoint() { ... };
// oc
[self.flutterEngine runWithEntrypoint:@"myOtherEntryPoint"];