前言
前一篇文章講解了Android原生工程如何集成Flutter項(xiàng)目的具體過(guò)程,Flutter混合開(kāi)發(fā)(一):Android項(xiàng)目集成Flutter模塊詳細(xì)指南 ,本篇將帶著大家來(lái)一起學(xué)習(xí)原生iOS項(xiàng)目如何集成Flutter。
因?yàn)槊總€(gè)版本的集成邏輯都是有差別的,所以這里交代下本篇文章的集成版本:
Flutter (channel dev,v1.10.16)
dart (v2.7.0)
Xcode (v11.2)
cocoapds (1.8.4)
創(chuàng)建Flutter module
假如iOS項(xiàng)目的路徑是這樣的:flutter/flutter_hybrid/iOS Project,那么我們需要在iOS Project上一層目錄flutter_hybrid中創(chuàng)建Flutter module。
cd flutter/flutter_hybrid/
flutter create -t module flutter_module
輸入后控制臺(tái)打印如下:
$ flutter create -t module flutter_module
Creating project flutter_module...
flutter_module/test/widget_test.dart (created)
flutter_module/flutter_module.iml (created)
flutter_module/.gitignore (created)
flutter_module/.metadata (created)
flutter_module/pubspec.yaml (created)
flutter_module/README.md (created)
flutter_module/lib/main.dart (created)
flutter_module/flutter_module_android.iml (created)
flutter_module/.idea/libraries/Flutter_for_Android.xml (created)
flutter_module/.idea/libraries/Dart_SDK.xml (created)
flutter_module/.idea/modules.xml (created)
flutter_module/.idea/workspace.xml (created)
Running "flutter pub get" in flutter_module... 1.2s
Wrote 12 files.
All done!
Your module code is in flutter_module/lib/main.dart.
看到All done就表示我們項(xiàng)目創(chuàng)建好了。整個(gè)module目錄和原生Flutter基本一樣,主要就是Android、iOS的宿主工程和lib目錄以及pubspec.yaml文件。
添加Flutter module依賴(lài)
為iOS項(xiàng)目添加依賴(lài)需要使用CocoaPods,如果你還沒(méi)有用到CocoaPods,可以參考https://cocoapods.org/上面的說(shuō)明來(lái)安裝CocoaPods。
如果你的項(xiàng)目之前沒(méi)有使用過(guò)cocoapods,那么需要進(jìn)行初始化生成podfile文件,進(jìn)入iOS項(xiàng)目的根目錄執(zhí)行:
pod init
然后打開(kāi)podfile文件,進(jìn)行配置:
# 配置
flutter_application_path = '../flutter_module/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'iOSFlutterHybrid' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for iOSFlutterHybrid
# 配置
install_all_flutter_pods(flutter_application_path)
target 'iOSFlutterHybridTests' do
inherit! :search_paths
# Pods for testing
end
target 'iOSFlutterHybridUITests' do
# Pods for testing
end
配置添加好后在項(xiàng)目根目錄運(yùn)行以下命令進(jìn)行安裝:
pod install
控制臺(tái)輸出:
Analyzing dependencies
Downloading dependencies
Installing Flutter (1.0.0)
Installing FlutterPluginRegistrant (0.0.1)
Installing flutter_module (0.0.1)
Generating Pods project
Integrating client project
[!] Please close any current Xcode sessions and use `iOSFlutterHybrid.xcworkspace` for this project from now on.
Pod installation complete! There are 3 dependencies from the Podfile and 3 total pods installed.
[!] Automatically assigning platform `iOS` with version `13.2` on target `iOSFlutterHybrid` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
這里我們看到有三個(gè)依賴(lài)安裝完成了。并提醒我們關(guān)閉當(dāng)前項(xiàng)目,在根目錄下面使用iOSFlutterHybrid.xcworkspace來(lái)打開(kāi)運(yùn)行項(xiàng)目。這里可能很多人在執(zhí)行命令的時(shí)候會(huì)發(fā)現(xiàn)提示0個(gè)依賴(lài)完成。這里有可能是你的Xcode版本的問(wèn)題。因?yàn)镕lutter要求最低版本是10.2及以上。
當(dāng)在flutter_module/pubspec.yaml中添加一個(gè)Flutter插件時(shí),需要在flutter_module目錄下運(yùn)行:
flutter packages get
來(lái)刷新podhelper.rb腳本中的插件列表,然后在iOS目錄下運(yùn)行:
pod install
這樣podhelper.rb腳本才能確保添加的插件和Flutter.framework能夠添加到iOS項(xiàng)目中。
目前Flutter還不支持Bitcode,所以集成了Flutter的iOS項(xiàng)目需要禁用Bitcode。
在以下路徑下找到Bitcode并禁用:
Build Settings->Build Options->Enable Bitcode

flutter以前的版本是需要添加build phase以構(gòu)建Dart代碼,但是最新的版本已經(jīng)不需要添加了,可以自動(dòng)構(gòu)建。
調(diào)用Flutter module
Flutter為我們提供了兩種調(diào)用方式:FlutterViewController和FlutterEngine,F(xiàn)lutterEngine在使用的時(shí)候會(huì)有一些問(wèn)題,將在下文進(jìn)行說(shuō)明。
FlutterViewController方式:
我們打開(kāi)ViewController.m文件,在里面添加一個(gè)加載flutter頁(yè)面的方法并且添加一個(gè)按鈕看來(lái)調(diào)用:
#import "ViewController.h"
#import <Flutter/Flutter.h>
#import "AppDelegate.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(handleButtonAction) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"加載Flutter" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blueColor]];
button.frame = CGRectMake(100, 100, 160, 60);
[self.view addSubview:button];
}
- (void)handleButtonAction{
FlutterViewController *flutterViewController =[FlutterViewController new];
//設(shè)置路由參數(shù)
[flutterViewController setInitialRoute:@"route2"];
[self presentViewController:flutterViewController animated:false completion:nil];
}
@end
當(dāng)我們運(yùn)行項(xiàng)目點(diǎn)擊加載Flutetr按鈕時(shí),將會(huì)調(diào)用Flutter頁(yè)面。和Android項(xiàng)目集成一樣,這里的setInitialRoute可以設(shè)置一個(gè)json數(shù)組來(lái)傳遞需要交互的參數(shù)。并在Flutter中使用window.defaultRouteName來(lái)獲取傳遞的參數(shù)。
FlutterEngine方式:
我們需要在AppDelegate中對(duì)FlutterEngine進(jìn)行初始化。打開(kāi)AppDelegate.h文件:
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
@interface AppDelegate : FlutterAppDelegate
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
在打開(kāi)AppDelegate.m文件:
// 如果你需要用到Flutter插件時(shí)
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
#include "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
[self.flutterEngine runWithEntrypoint:nil];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine]; //如果你需要用到Flutter插件時(shí)
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
然后在ViewController.m文件定義的handleButtonAction中調(diào)用:
- (void)handleButtonAction{
FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];
FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
[flutterViewController setInitialRoute:@"route2"];
[self presentViewController:flutterViewController animated:false completion:nil];
}
當(dāng)我們運(yùn)行項(xiàng)目點(diǎn)擊加載Flutter按鈕時(shí),將會(huì)調(diào)用Flutter頁(yè)面。前文講到使用FlutterEngine會(huì)有問(wèn)題,就是我們setInitialRoute傳遞的參數(shù)在flutter中永遠(yuǎn)獲取到的都是 “/” ,這個(gè)是Fltter SDK的一個(gè)Bug,所以如果必須依賴(lài)setInitialRoute,還是使用FlutterViewController的形式來(lái)加載Flutter模塊。
熱重啟/重新加載
大家在寫(xiě)純Flutter應(yīng)用的時(shí)候,知道是有熱重啟/重新加載功能的,但是在做混合開(kāi)發(fā)的過(guò)程中,你會(huì)發(fā)現(xiàn)熱重啟/重新加載功能失效了。那么如何在混合開(kāi)發(fā)中開(kāi)啟熱重啟/重新加載功能呢?
- 首先接入我們的設(shè)備或者模擬器
- 將我們的App關(guān)閉,退出后臺(tái),在terminal中運(yùn)行 flutter attach命令
$ flutter attach
Waiting for a connection from Flutter on Android SDK built for x86...
復(fù)制代碼此時(shí)就在等待設(shè)備的連接。這里要注意的是,如果電腦連接了多臺(tái)設(shè)備需要使用 -d 命令來(lái)指定一臺(tái)設(shè)備,參數(shù)為設(shè)備的id。
flutter attach -d '你的設(shè)備id'
然后啟動(dòng)我們的應(yīng)用會(huì)看到控制臺(tái)輸出:
Done.
Syncing files to device Android SDK built for x86... 1,393ms
?? To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on Android SDK built for x86 is available at: http://127.0.0.1:59354/zRsDBfpesrk=/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
這樣就表示我們連接成功了。在輸出的日志中也告訴了我們?nèi)绾问褂脽嶂貑?重新加載功能。
在Terminal中輸入以下命令?:?
r : 熱加載;
R : 熱重啟;
h : 獲取幫助;
d : 斷開(kāi)連接;
q : 退出;
這里的的 d 和 q 的命令都有退出調(diào)試,區(qū)別在于 d 命令只是單純的斷開(kāi)而 q 命令會(huì)將應(yīng)用退到后臺(tái)。
調(diào)試Dart代碼
同樣在混合開(kāi)發(fā)過(guò)程中我們?nèi)绾握{(diào)試dart代碼呢?
- 關(guān)閉我們的應(yīng)用
- 點(diǎn)擊Android Studio工具欄上的Flutter Attach按鈕(需要安裝Flutter與Dart插件)

- 啟動(dòng)我們的應(yīng)用
接下來(lái)就可以像調(diào)試普通Flutter項(xiàng)目一樣來(lái)調(diào)試混合開(kāi)發(fā)模式下的Dart代碼了。
總結(jié)
本文主要是講解了iOS集成Flutter項(xiàng)目的步驟,其中也遇到了一些問(wèn)題,由于我的Xcode版本較低,在集成的過(guò)程中iOS項(xiàng)目的依賴(lài)一直失敗。最后才發(fā)現(xiàn)是Xcode的版本問(wèn)題。