歡迎光臨
每天分享高質量文章

從零搭建 iOS Native Flutter 混合工程

作者:CaffreySun

鏈接:https://juejin.im/post/5c3ae5ef518825242165c5ca

本文來實現一個靈活、無侵入、低耦合的 iOS Flutter 混合工程。我們希望混合開發至少得保證如下特點:

 

  • 對Native工程無侵入

  • 對Native工程零耦合

  • 不影響Native工程的開發流程與打包流程

  • 易本地除錯

一、Flutter 提供的 Native Flutter 混合工程方式

Flutter 官方提供的混合工程搭建方法:Add Flutter to existing apps文章中介紹瞭如何在現有 App 裡加入Flutter,下麵進行逐步介紹一下

1、創建 Flutter 工程

 

請自行 百度/Google Flutter 安裝教程,安裝Flutter。然後到任意目錄下執行flutter create -t module my_flutter,”my_flutter” 是要創建的 Flutter 工程的名稱。

2、通過 Cocoapods 將 Flutter 引入 現有 Native 工程

 

在Podfile添加以下下代碼

 

flutter_application_path = "xxx/xxx/my_flutter"
eval(File.read(File.join(flutter_application_path, '.ios''Flutter''podhelper.rb')), binding)

然後執行pod install

3、修改 Native 工程

 

打開Xcode工程,選擇要加入 Flutter App 的 target,選擇 Build Phases,點擊頂部的 + 號,選擇 New Run Script Phase,然後輸入以下腳本

 

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

二、分析 Native Flutter 混合工程

按照上面三個步驟進行逐一分析每一步的問題,並提供優化方案。

1、創建 Flutter 工程

 

這一步首先在自己電腦上安裝 Flutter,然後使用flutter create。這裡就存在問題,在團隊開發中每個人安裝的 Flutter 版本可能並不同,這樣會出現Dart層Api兼容性或Flutter虛擬機不一致等問題。在團隊協作中一定要保證 Flutter 工程依賴相同的 Flutter SDK,所有需要一個工具在執行 flutter 指令時可以根據當前 Flutter 工程使用對應版本的 Flutter SDK,目前有一個名叫flutter_wrapper的工具,使用 flutterw 代替 flutter  指令,工具會自動將 Flutter SDK 放在當前 Flutter 工程目錄下,並執行當前工程中的 flutter 命令,這樣就不再依賴本地電腦安裝的 Flutter SDK。

 

flutter_wrapper使用:

 

  1. flutter create創建 Flutter 工程,這裡使用的是本地的 Flutter SDK

  2. 進入 Flutter 工程目錄安裝 ‘flutter_wrapper’,執行 sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"

  3. 此後在當前 Flutter 工程需要使用 flutter  命令的地方都使用 ./flutterw來代替

2、通過 Cocoapods 將 Flutter 引入 現有 Native 工程

 

這一步在 Podfile 里添加里一個 ‘podhelper.rb’ ruby 腳本,腳本會在 pod install/update 時執行,腳本主要做4點事情:

 

  • 解析 ‘Generated.xcconfig’ 檔案,獲取 Flutter 工程配置信息,檔案在’my_flutter/.ios/Flutter/’目錄下,檔案中包含了 Flutter SDK 路徑、Flutter 工程路徑、Flutter 工程入口、編譯目錄等。

  • 將 Flutter SDK 中的 Flutter.framework 通過 pod 添加到 Native 工程。

  • 將 Flutter 工程依賴的插件通過 pod 添加到 Native 工程,因為有些插件有 Native 部分代碼。

  • 使用post_install 這個 pod hooks 來關閉 Native 工程的 bitcode,並將 ‘Generated.xcconfig’ 檔案加入 Native 工程。

 

這一步存在問題是,’podhelper.rb’腳本是通過一個本地 Flutter 工程路徑’flutter_application_path’來讀取,在團隊協作中我們很難保證每個人的本地 Flutter 工程路徑都一樣,在同步代碼時大家可能都要頻繁修改’flutter_application_path’變數,這樣很不友好。

 

解決這個問題的思路就是將 Flutter 工程放在當前 Native 工程的目錄下,我們可以再加入一個 ruby 腳本,在每次執行 pod install/update 時,將 Flutter 工程從 git 上拉取一份放在當前目錄下,這樣 Flutter 工程的路徑就統一了。大致代碼如下:

 

flutter_application_path = __dir__ + "/.flutter/app"
`git clone git://xxxx/my_flutter.git #{flutter_application_path}`
# 如果想要除錯本地的 Flutter 工程,就把下麵這行註釋放開
# flutter_application_path = "xxx/xxx/my_flutter"
eval(File.read(File.join(flutter_application_path, '.ios''Flutter''podhelper.rb')), binding)

上述代碼只是臨時代碼,為了演示將 Flutter 工程放在當前目錄下這個思路,後面會有完整的實現代碼。

3. 修改 Native 工程

 

這裡執行了一個’xcode_backend.sh’腳本的兩個命令build、embed,兩個命令分別的作業是:

  • build: 根據當前 Xcode 工程的 ‘configuration’ 和其他編譯配置編譯 Flutter 工程,’configuration’通常為’debug’或者’release’

  • embed: 將 build 出來的 framework、資源包放入 Xcode 編譯目錄,並簽名 framework

 

這裡存在的問題是:Flutter 工程依賴 Native工程來執行編譯,並影響Native工程的開發流程與打包流程。

 

通常 ‘configuration’ 里不止有 ‘debug’ 或者 ‘release’,可能會有自定義的名稱,如果我們使用自定義的 ‘configuration’ 編譯,那麼 xcode_backend.sh build就會執行失敗。因為Flutter 編譯樣式是通過 ‘configuration’ 獲取的,Flutter 支持 Debug、Profil、Release 三種編譯樣式,而我們自定義的名稱不在這三種之中,Flutter 就不知道該怎麼編譯。

每次 Native 編譯時 Flutter 就需要編譯,其實是產生了相互依賴:Flutter 編譯依賴 Native 編譯環境,Native 依賴 Flutter 編譯通過。

 

我們希望做到:Native 依賴 Flutter 編譯出來的產物,並且保留依賴 Flutter 原始碼進行除錯的能力。

 

實現這個標的我們需要兩部分:

  • 第一部分:Flutter 工程里創建一個打包腳本,可以一鍵產生 Flutter 工程產物;

  • 第二部分:在 Native 工程獲取 FLutter 工程的編譯產物,並通過 pod 添加到工程;並且保留依賴 Flutter 工程原始碼的功能。

三、實現 Native Flutter 混合工程

下麵我們來實現上文提到的兩個部分

第一部分實現“打包腳本”

 

這一部分我們需要實現腳本自動打包 Flutter 工程,拆分一下這個腳本流程,大致分為一下幾個步驟:

 

  1. 檢查 Flutter 環境,拉取 Flutter plugin

  2. 編譯 Flutter 工程產物

  3. 複製 Flutter 插件中的 Native 代碼

  4. 將產物同步到產物發佈的服務器

 

下麵來一步一步的分析並實現每一步:

 

(1) 檢查 Flutter 環境,拉取 Flutter plugin

 

這一步做的工作是檢查是否安裝了 ‘flutter_wrapper’,如果安裝則進行安裝,然後執行 ./flutterw packages get,Shell代碼如下:

 

flutter_get_packages() {
    echo "================================="
    echo "Start get flutter app plugin"

    local flutter_wrapper="./flutterw"
    if [ -e $flutter_wrapper ]; then
        echo 'flutterw installed' >/dev/null
    else
        bash -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"
        if [[ $? -ne 0 ]]; then
            # 上一步腳本執行失敗
            echo "Failed to installed flutter_wrapper."
            exit -1
        fi
    fi

    ${flutter_wrapper} packages get --verbose
    if [[ $? -ne 0 ]]; then
        # 上一步腳本執行失敗
        echo "Failed to install flutter plugins."
        exit -1
    fi

    echo "Finish get flutter app plugin"
}

(2) 編譯 Flutter 工程產物

 

這一步是腳本的核心,主要邏輯和上文中’xcode_backend.sh build’類似,大致代碼如下:

 

# 預設debug編譯樣式
BUILD_MODE="debug"
# 編譯的cpu平臺
ARCHS_ARM="arm64,armv7"
# Flutter SDK 路徑
FLUTTER_ROOT=".flutter"
# 編譯目錄
BUILD_PATH=".build_ios/${BUILD_MODE}"
# 存放產物的目錄
PRODUCT_PATH="${BUILD_PATH}/product"
# 編譯出的flutter framework 存放的目錄
PRODUCT_APP_PATH="${PRODUCT_PATH}/Flutter"

build_flutter_app() {
    echo "================================="
    echo "Start Build flutter app"
    # 創建目錄
    mkdir -p -- "${PRODUCT_APP_PATH}"
    # flutter 工程入口 dart檔案
    local target_path="lib/main.dart"
    # flutter sdk 目錄解析
    local artifact_variant="unknown"
    case "$BUILD_MODE" in
    release*)
        artifact_variant="ios-release"
        ;;
    profile*)
        artifact_variant="ios-profile"
        ;;
    debug*)
        artifact_variant="ios"
        ;;
    *)
        echo "ERROR: Unknown FLUTTER_BUILD_MODE: ${BUILD_MODE}."
        exit -1
        ;;
    esac

    if [[ "${BUILD_MODE}" != "debug" ]]; then
        # 非debug編譯樣式
        # build fLutter app,output App.framework
        ${FLUTTER_ROOT}/bin/flutter --suppress-analytics 
            --verbose 
            build aot 
            --output-dir="${BUILD_PATH}" 
            --target-platform=ios 
            --target="${target_path}" 
            --${BUILD_MODE} 
            --ios-arch="${ARCHS_ARM}"

        if [[ $? -ne 0 ]]; then
            echo "Failed to build flutter app"
            exit -1
        fi
    else
        # debug編譯樣式直接使用編譯好的App.framework,
        # 因為在 debug 樣式下 flutter 代碼並沒有編譯成二進制機器碼,而是在後續build bundle時被打包進資源包,
        # 在'xcode_backend.sh'腳本里,這一步這裡只是編譯成一個App.framework空殼。
        # 提前編譯好的原因是'xcode_backend.sh'腳本執行和Xcode一起執行,所以執行時能獲取到Xcode設置的編譯配置,能正確的編譯出'App.framework',
        # 而本腳本不依賴Xcode執行,即便把'xcode_backend.sh'對應的代碼拷貝出來也不能正確的編譯出'App.framework',除非我們能正確的配置編譯環境。
        
        # 而我又不想那麼麻煩,選擇另闢蹊徑:
        # 隨便創建了一個 Flutter 工程,
        # 在debug樣式下,先在模擬器編譯運行一下,得到x86_64的App.framework,
        # 再到真機運行一下,得到arm64/armv7的App.framework,
        # 最後使用lipo命令將兩個App.framework合併,得到x86_64/arm64/armv7的App.framework,
        # 這樣最後得到的App.framework在模擬器和真機都可以用
        # 因為debug樣式下App.framework就是占位的空殼,所以其他flutter工程一樣用
        local app_framework_debug="iOSApp/Debug/App.framework"
        cp -r -- "${app_framework_debug}" "${BUILD_PATH}"
    fi

    # copy info.plist to App.framework
    app_plist_path=".ios/Flutter/AppFrameworkInfo.plist"
    cp -- "${app_plist_path}" "${BUILD_PATH}/App.framework/Info.plist"

    local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}"
    local flutter_framework="${framework_path}/Flutter.framework"
    local flutter_podspec="${framework_path}/Flutter.podspec"

    # copy framework to PRODUCT_APP_PATH
    cp -r -- "${BUILD_PATH}/App.framework" "${PRODUCT_APP_PATH}"
    cp -r -- "${flutter_framework}" "${PRODUCT_APP_PATH}"
    cp -r -- "${flutter_podspec}" "${PRODUCT_APP_PATH}"

    local precompilation_flag=""
    if [[ "$BUILD_MODE" != "debug" ]]; then
        precompilation_flag="--precompiled"
    fi

    # build bundle
    ${FLUTTER_ROOT}/bin/flutter --suppress-analytics 
        --verbose 
        build bundle 
        --target-platform=ios 
        --target="${target_path}" 
        --${BUILD_MODE} 
        --depfile="${BUILD_PATH}/snapshot_blob.bin.d" 
        --asset-dir="${BUILD_PATH}/flutter_assets" 
        ${precompilation_flag}

    if [[ $? -ne 0 ]]; then
        echo "Failed to build flutter assets"
        exit -1
    fi

    # copy Assets
    cp -rf -- "${BUILD_PATH}/flutter_assets" "${PRODUCT_APP_PATH}/App.framework"

    # setting podspec
    # replace:
    # 'Flutter.framework'
    # to:
    # 'Flutter.framework', 'App.framework'
    sed -i '' -e $'s/'Flutter.framework'/'Flutter.framework', 'App.framework'/g' ${PRODUCT_APP_PATH}/Flutter.podspec

    echo "Finish build flutter app"
}

(3) 複製 Flutter 插件中的 Native 代碼

 

Flutter 使用的各種插件可能會包含 Native 代碼,並且這些代碼已經提供了podspec,可以使用 pod 直接引入。我們要做的就是把插件的 Native 代碼拷貝到產物目錄。Flutter 創建了一個給 Native 註冊插件的 pod 庫 ‘FlutterPluginRegistrant’,這個也需要拷貝出來,在 Flutter 工程根目錄下有一個 .flutter-plugins 檔案,檔案內部記錄了插件的名字和插件的路徑,格式為 pugin_name=/xx/xx/xx,解析這個檔案就可以得到插件信息,代碼如下:

 

flutter_copy_packages() {
    echo "================================="
    echo "Start copy flutter app plugin"

    local flutter_plugin_registrant="FlutterPluginRegistrant"
    local flutter_plugin_registrant_path=".ios/Flutter/${flutter_plugin_registrant}"
    echo "copy 'flutter_plugin_registrant' from '${flutter_plugin_registrant_path}' to '${PRODUCT_PATH}/${flutter_plugin_registrant}'"
    cp -rf -- "${flutter_plugin_registrant_path}" "${PRODUCT_PATH}/${flutter_plugin_registrant}"

    local flutter_plugin=".flutter-plugins"
    if [ -e $flutter_plugin ]; then
        OLD_IFS="$IFS"
        IFS="="
        cat ${flutter_plugin} | while read plugin; do
            local plugin_info=($plugin)
            local plugin_name=${plugin_info[0]}
            local plugin_path=${plugin_info[1]}

            if [ -e ${plugin_path} ]; then
                local plugin_path_ios="${plugin_path}ios"
                if [ -e ${plugin_path_ios} ]; then
                    if [ -s ${plugin_path_ios} ]; then
                        echo "copy plugin 'plugin_name' from '${plugin_path_ios}' to '${PRODUCT_PATH}/${plugin_name}'"
                        cp -rf ${plugin_path_ios} "${PRODUCT_PATH}/${plugin_name}"
                    fi
                fi
            fi
        done
        IFS="$OLD_IFS"
    fi

    echo "Finish copy flutter app plugin"
}

 

(4) 將產物同步到保留產物的服務器

 

經過上面的幾個步驟後會生成一個產物目錄,這個目錄下會有幾個二級目錄,每個二級目錄里都包含一個 podspec 檔案。

 

也就是說這個產物目錄里存放的就是 cocoapods 庫,將目錄拷貝到 Native 工程,然後用 pod 'pod_name', :path=>'xx/xxx' 的形式取用就可以了。

 

有了產物後我們需要一個存放產物的地方, 大家可以去這個地方下載,這一步比較靈活,可以選擇將產物放在git倉庫、http服務器、ftp服務器等。我最終選擇將產物壓縮成 zip 上傳到 Maven 上,原因是為了和 Android Flutter 產物放在一個地方,並且 Maven 已成做好的產物版本管理。

 

Maven上傳代碼比較簡單,這裡不再贅述,有興趣可以到文末的github倉庫查看代碼。

 

Flutter 工程版本設置是在工程目錄下的 ‘pubspec.yaml’ 檔案,打包腳本讀取這個檔案來確定產物的版本。

 

最後這個腳本使用方式為 ./build_ios.h -m debug ./build_ios.h -m release,上文中沒有提到的一點是只有 release 樣式編譯的包才會上傳的服務器,debug 只是編譯到產物目錄。

第二步 Native 依賴 Flutter 產物

 

這部分我們需要實現獲取指定版本 Flutter 工程 release 產物,並集成到 Native 專案,並保留可以除錯 Flutter 工程的能力。

 

也是來拆分一下腳本流程:

  • 獲取 Flutter 工程產物

    • 獲取 release 產物

    • 獲取 debug 產物

  • 通過 pod 引入 Flutter 工程產物

 

(1) 獲取 Flutter 工程產物

 

上文說到只有 release 產物放在了產物服務器上,debug 只是編譯到產物目錄。不上傳 debug 的原因是,debug 階段就是開發階段,舉個不太恰當的例子:哪有開發階段就把包上傳 app store 的?也就代表這 release 的產物和 debug 的產物獲取邏輯不一樣,並且我們的腳本支持兩種方式的切換,所以在 Podfile 添加如下代碼:

 

# 設置要引入的 flutter app 的版本
FLUTTER_APP_VERSION="1.1.1"

# 是否進行除錯 flutter app,
# 為true時FLUTTER_APP_VERSION配置失效,下麵的三項配置生效
# 為false時FLUTTER_APP_VERSION配置生效,下麵的三項配置失效
FLUTTER_DEBUG_APP=false

# Flutter App git地址,從git拉取的內容放在當前工程目錄下的.flutter/app目錄
# 如果指定了FLUTTER_APP_PATH,則此配置失效
FLUTTER_APP_URL="git:/xxxx.git"
# flutter git 分支,預設為master
# 如果指定了FLUTTER_APP_PATH,則此配置失效
FLUTTER_APP_BRANCH="master"

# flutter本地工程目錄,絕對路徑或者相對路徑,如果有值則git相關的配置無效
FLUTTER_APP_PATH="../my_flutter"

eval(File.read(File.join(__dir__, 'flutterhelper.rb')), binding)

Podfile 其實就是 Ruby 代碼,上面幾個由大寫字母組成的變數是全域性變數,最後一句代碼的作用為讀取’flutterhelper.rb’里的代碼並執行,在’flutterhelper.rb’里可以獲取到上面定義的全域性變數,根據這幾個變數做不同的操作,其中選擇使用 release 還是 debug 的代碼如下:

 

if FLUTTER_DEBUG_APP.nil|| FLUTTER_DEBUG_APP == false
    # 使用 flutter release 樣式
    puts "開始安裝 release mode flutter app"
    install_release_flutter_app()
else
    # 存在debug配置,使用 flutter debug 樣式
    puts "開始安裝 debug mode flutter app"
    install_debug_flutter_app()
end

install_release_flutter_app為操作 release 產物的函式,install_debug_flutter_app為操作 debug 產物的函式。

 

處理 release 樣式主要就是獲取 release 產物,代碼如下:

 

# 安裝正式環境環境app
def install_release_flutter_app
    if FLUTTER_APP_VERSION.nil?
        raise "Error: 請在 Podfile 里設置要安裝的 Flutter app 版本 ,例如:FLUTTER_APP_VERSION='1.0.0'"
    else
        puts "當前安裝的 flutter app 版本為 #{FLUTTER_APP_VERSION}"
    end

    # 存放產物的目錄
    flutter_release_path = File.expand_path('.flutter_release')
    # 是否已經存在當前版本的產物
    has_version_file = true
    if !File.exist? flutter_release_path
        FileUtils.mkdir_p(flutter_release_path)
        has_version_file = false
    end

    # 存放當前版本產物的目錄
    flutter_release_version_path = File.join(flutter_release_path, FLUTTER_APP_VERSION)
    if !File.exist? flutter_release_version_path
        FileUtils.mkdir_p(flutter_release_version_path)
        has_version_file = false
    end

    # 產物包
    flutter_package = "flutter.zip"
    flutter_release_zip_file =  File.join(flutter_release_version_path, flutter_package)
    if !File.exist? flutter_release_zip_file
        has_version_file = false
    end

    # 產物包下載完成標誌
    flutter_package_downloaded = File.join(flutter_release_version_path, "download.ok")
    if !File.exist? flutter_package_downloaded
        has_version_file = false
    end

    if has_version_file == true
        # 解壓
        flutter_package_path = unzip_release_flutter_app(flutter_release_version_path, flutter_release_zip_file)
        # 開始安裝
        install_release_flutter_app_pod(flutter_package_path)
    else
        # 刪除老檔案
        FileUtils.rm_rf(flutter_release_zip_file)
        # 刪除標誌物
        FileUtils.rm_rf(flutter_package_downloaded)

        # 下載
        download_release_flutter_app(FLUTTER_APP_VERSION, flutter_release_zip_file, flutter_package_downloaded)
        # 解壓
        flutter_package_path = unzip_release_flutter_app(flutter_release_version_path, flutter_release_zip_file)
        # 開始安裝
        install_release_flutter_app_pod(flutter_package_path)
    end
end

unzip_release_flutter_app為解壓zip格式產物的函式,download_release_flutter_app為從 Maven 下載產物的函式,這兩個比較簡單,詳細代碼請看文末 github 倉庫。install_release_flutter_app_pod為通過 pod 將產物添加到 Native 的函式,後面會詳細介紹。

 

處理 debug 樣式的操作為,獲取 Flutter 工程原始碼,執行 build_ios.sh -m debug 進行打包,然後得到 debug 產物目錄,詳細代碼如下:

 

# 安裝開發環境app
def install_debug_flutter_app

    puts "如果是第一次運行開發環境Flutter專案,此過程可能會較慢"
    puts "請耐心等️待☕️️️️️☕️☕️
"

    # 預設Flutter App 目錄
    flutter_application_path = __dir__ + "/.flutter/app"
    flutter_application_url = ""
    flutter_application_branch = 'master'

    # 指定了FLUTTER_APP_PATH就用本地代碼,複製從git拉取
    if FLUTTER_APP_PATH != nil
        File.expand_path(FLUTTER_APP_PATH)
        if File.exist?(FLUTTER_APP_PATH) 
            flutter_application_path = FLUTTER_APP_PATH
        else
            flutter_application_path = File.expand_path(FLUTTER_APP_PATH)
            if !File.exist?(flutter_application_path) 
                raise "Error: #{FLUTTER_APP_PATH} 地址不存在!"
            end
        end

        puts "
Flutter App路徑: "+flutter_application_path
    else
        if FLUTTER_APP_URL != nil
            flutter_application_url = FLUTTER_APP_URL
            if FLUTTER_APP_BRANCH != nil
                flutter_application_branch = FLUTTER_APP_BRANCH
            end
        else
            raise "Error: 請在'Podfile'里增加Flutter App git地址配置,配置格式請查看'flutterhelper.rb'檔案"
        end
        puts "
拉取 Flutter App 代碼"
        puts "Flutter App路徑: "+flutter_application_path
        update_flutter_app(flutter_application_path, flutter_application_url, flutter_application_branch)
    end

    puts "
編譯 Flutter App"
    # PUB_HOSTED_URL FLUTTER_STORAGE_BASE_URL 為了加快速度,使用國內鏡像地址
    `export PUB_HOSTED_URL=https://pub.flutter-io.cn && 
    export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn && 
    cd #{flutter_application_path} && 
    #{flutter_application_path}/build_ios.sh -m debug`

    if $?.to_i == 0
        flutter_package_path = "#{flutter_application_path}/.build_ios/debug/product"
        # 開始安裝
        install_release_flutter_app_pod(flutter_package_path)
    else
        raise "Error: 編譯 Flutter App失敗"
    end
end

update_flutter_app為從 git 拉取代碼的函式也不贅述,詳情見文末 github 倉庫。

 

(2) 通過 pod 引入 Flutter 工程產物

 

上文兩個函式執行完成後,就得到了產物的存放目錄,下麵只需要引入到 Native 倉庫就可以了,也就是install_release_flutter_app_pod函式,從代碼如下:

 

# 將 Flutter app 通過 pod 安裝
def install_release_flutter_app_pod(product_path)
    if product_path.nil?
        raise "Error: 無效的 flutter app 目錄"
    end

    puts "將 flutter app 通過 pod 匯入到 工程"

    Dir.foreach product_path do |sub|
        if sub.eql?('.'|| sub.eql?('..') 
            next
        end

        sub_abs_path = File.join(product_path, sub)
        pod sub, :path=>sub_abs_path
    end

    post_install do |installer|
        installer.pods_project.targets.each do |target|
            target.build_configurations.each do |config|
                config.build_settings['ENABLE_BITCODE'] = 'NO'
            end
        end
    end
end 

如果要修改 release 產物版本,則設置FLUTTER_APP_VERSION。如果想要 debug flutter,則設置 FLUTTER_DEBUG_APP=true,如果除錯本地代碼則設置 FLUTTER_APP_PATH="../my_flutter",負責將 FLUTTER_APP_PATH註釋掉,配置 FLUTTER_APP_URL FLUTTER_APP_BRANCH

四、總結

對照上文中提到的對混合工程的要求,總結一下:

 

  • Flutter 工程完全不依賴 Native 工程,而是通過 ‘build_ios.sh’ 腳本進行編譯打包;

  • 通過 pod 引入 Flutter 工程對 Native 也沒有浸入,不要在 Native 工程里增加 Flutter 打包腳本;

  • Native 開發工程師只需要執行 pod install 所有的 Flutter 依賴就都加入進工程,不需要工程師配置 Flutter 開發環境;也不影響 Native 打包;

  • 也保留了本地除錯 Flutter 工程的功能;

 

Github 倉庫:https://github.com/CaffreySun/iOS_Flutter_Hybrid_Project

赞(0)

分享創造快樂