Flutter混合開發(二):iOS專案整合Flutter模組詳細指南

李四爺發表於2019-11-12

本文首發於微信公眾號「Android開發之旅」,歡迎關注 ,獲取更多技術乾貨

前言

前一篇文章講解了Android原生工程如何整合Flutter專案的具體過程,Flutter混合開發(一):Android專案整合Flutter模組詳細指南 ,本篇將帶著大家來一起學習原生iOS專案如何整合Flutter。

因為每個版本的整合邏輯都是有差別的,所以這裡交代下本篇文章的整合版本:

Flutter (channel dev,v1.10.16)
dart (v2.7.0)
Xcode (v11.2)
cocoapds (1.8.4)
複製程式碼

建立Flutter module

假如iOS專案的路徑是這樣的:flutter/flutter_hybrid/iOS Project,那麼我們需要在iOS Project上一層目錄flutter_hybrid中建立Flutter module。

cd flutter/flutter_hybrid/

flutter create -t module flutter_module
複製程式碼

輸入後控制檯列印如下:

$ 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就表示我們專案建立好了。整個module目錄和原生Flutter基本一樣,主要就是Android、iOS的宿主工程和lib目錄以及pubspec.yaml檔案。

#新增Flutter module依賴

為iOS專案新增依賴需要使用CocoaPods,如果你還沒有用到CocoaPods,可以參考cocoapods.org/上面的說明來安裝CocoaPods。

如果你的專案之前沒有使用過cocoapods,那麼需要進行初始化生成podfile檔案,進入iOS專案的根目錄執行:

pod init
複製程式碼

然後開啟podfile檔案,進行配置:

# 配置
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

複製程式碼

配置新增好後在專案根目錄執行以下命令進行安裝:

pod install
複製程式碼

控制檯輸出:

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`.

複製程式碼

這裡我們看到有三個依賴安裝完成了。並提醒我們關閉當前專案,在根目錄下面使用iOSFlutterHybrid.xcworkspace來開啟執行專案。這裡可能很多人在執行命令的時候會發現提示0個依賴完成。這裡有可能是你的Xcode版本的問題。因為Flutter要求最低版本是10.2及以上。

當在flutter_module/pubspec.yaml中新增一個Flutter外掛時,需要在flutter_module目錄下執行:

flutter packages get
複製程式碼

來重新整理podhelper.rb指令碼中的外掛列表,然後在iOS目錄下執行:

pod install
複製程式碼

這樣podhelper.rb指令碼才能確保新增的外掛和Flutter.framework能夠新增到iOS專案中。

目前Flutter還不支援Bitcode,所以整合了Flutter的iOS專案需要禁用Bitcode。

在以下路徑下找到Bitcode並禁用:

Build Settings->Build Options->Enable Bitcode 
複製程式碼

Flutter混合開發(二):iOS專案整合Flutter模組詳細指南

flutter以前的版本是需要新增build phase以構建Dart程式碼,但是最新的版本已經不需要新增了,可以自動構建。

呼叫Flutter module

Flutter為我們提供了兩種呼叫方式:FlutterViewController和FlutterEngine,FlutterEngine在使用的時候會有一些問題,將在下文進行說明。

FlutterViewController方式:

我們開啟ViewController.m檔案,在裡面新增一個載入flutter頁面的方法並且新增一個按鈕看來呼叫:


#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];
    //設定路由引數
    [flutterViewController setInitialRoute:@"route2"];
    [self presentViewController:flutterViewController animated:false completion:nil];
     
}

@end
複製程式碼

當我們執行專案點選載入Flutetr按鈕時,將會呼叫Flutter頁面。和Android專案整合一樣,這裡的setInitialRoute可以設定一個json陣列來傳遞需要互動的引數。並在Flutter中使用window.defaultRouteName來獲取傳遞的引數。

FlutterEngine方式:

我們需要在AppDelegate中對FlutterEngine進行初始化。開啟AppDelegate.h檔案:

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

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

在開啟AppDelegate.m檔案:

// 如果你需要用到Flutter外掛時
#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外掛時
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
複製程式碼

然後在ViewController.m檔案定義的handleButtonAction中呼叫:

- (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];
     
}
複製程式碼

當我們執行專案點選載入Flutter按鈕時,將會呼叫Flutter頁面。前文講到使用FlutterEngine會有問題,就是我們setInitialRoute傳遞的引數在flutter中永遠獲取到的都是 “/” ,這個是Fltter SDK的一個Bug,所以如果必須依賴setInitialRoute,還是使用FlutterViewController的形式來載入Flutter模組。

熱重啟/重新載入

大家在寫純Flutter應用的時候,知道是有熱重啟/重新載入功能的,但是在做混合開發的過程中,你會發現熱重啟/重新載入功能失效了。那麼如何在混合開發中開啟熱重啟/重新載入功能呢?

  • 首先接入我們的裝置或者模擬器
  • 將我們的App關閉,退出後臺,在terminal中執行 flutter attach命令
$ flutter attach
Waiting for a connection from Flutter on Android SDK built for x86...
複製程式碼

複製程式碼此時就在等待裝置的連線。這裡要注意的是,如果電腦連線了多臺裝置需要使用 -d 命令來指定一臺裝置,引數為裝置的id。

flutter attach -d '你的裝置id'
複製程式碼

然後啟動我們的應用會看到控制檯輸出:

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".
複製程式碼

這樣就表示我們連線成功了。在輸出的日誌中也告訴了我們如何使用熱重啟/重新載入功能。

在Terminal中輸入以下命令​:​

r : 熱載入;
R : 熱重啟;
h : 獲取幫助;
d : 斷開連線;
q : 退出;
複製程式碼

這裡的的 d 和 q 的命令都有退出除錯,區別在於 d 命令只是單純的斷開而 q 命令會將應用退到後臺。

除錯Dart程式碼

同樣在混合開發過程中我們如何除錯dart程式碼呢?

  • 關閉我們的應用
  • 點選Android Studio工具欄上的Flutter Attach按鈕(需要安裝Flutter與Dart外掛)

Flutter混合開發(二):iOS專案整合Flutter模組詳細指南

  • 啟動我們的應用

接下來就可以像除錯普通Flutter專案一樣來除錯混合開發模式下的Dart程式碼了。

總結

本文主要是講解了iOS整合Flutter專案的步驟,其中也遇到了一些問題,由於我的Xcode版本較低,在整合的過程中iOS專案的依賴一直失敗。最後才發現是Xcode的版本問題。這裡花費了很多時間去排查問題,所以大家在整合的過程中有問題可以關注公眾號加我微信,給我留言,我會幫助大家解決問題。

全部Demo原始碼已經上傳到後臺,關注公眾號回覆「混合開發」即可獲得下載連結。

如果你覺得文章還不錯,請大家點贊分享下,你的肯定是對我最大的鼓勵和支援。

推薦閱讀

Flutter開發必備Dart基礎:Dart快速入門

Flutter混合開發(一):Android專案整合Flutter模組詳細指南

掃描下方二維碼關注公眾號,獲取更多技術乾貨。

Flutter混合開發(二):iOS專案整合Flutter模組詳細指南

相關文章