想尝试Flutter? 重新开发成本高? 教你Flutter和iOS原生混合开发,Flutter制作成pod导入(一)探索篇

一、Flutter简单介绍

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。

在这里不对Flutter过多介绍。
如果你想安装Flutter或想了解更多Flutter相关信息可关注以下网址:
Flutter官方 https://flutter.io/
Flutter中文网 https://flutterchina.club/


二、分析Flutter Xcode项目

1、创建Flutter项目

当安装完成Flutter环境后,我们在终端下使用"flutter create flutter_app"命令来创建一个名为"flutter_app"的项目来一起分析一下flutter的目录

2、分析iOS运行所需内容

1.我们首先运行一下看看项目有没有问题。首先打开iOS模拟器,终端进入flutter_app目录下,我们执行"flutter run"命令运行项目,可以看到一个计数器的项目启动起来了。我们直接打开flutter_app目录下的ios文件夹里面是一个xcode项目,直接打开Runner.xcworkspace项目结构如下:

xcode目录

2.Flutter目录
Flutter目录中主要用到的是flutter_assets、App.framework、Flutter.framework这三个文件。

目录 说明
flutter_assets 项目资源文件(图片、字体等)都会存放在这个目录下
App.framework 所有Flutter代码都会编译进这个库文件
Flutter.framework Flutter运行所需库文件

注:App.framework和Flutter.framework在debug、release模式下会有所不同,后面我们需要在debug和release下分别取出相应的库文件

3.分析AppDelegate

1.AppDelegate.h 引入 #import <Flutter/Flutter.h> 并继承 FlutterAppDelegate
2.AppDelegate.m 引入 #include "GeneratedPluginRegistrant.h" 文件并在didFinishLaunchingWithOptions方法中调用了
[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
3.GeneratedPluginRegistrant文件, 这个文件是用来注册用到的Flutter插件的,因为这里是新建项目并没有引入任何插件,所以registerWithRegistry方法为空。如果Flutter项目引入其他插件会在flutter_app目录下生成一个.flutter-plugins的隐藏文件里面会包括插件名称和对应路径,GeneratedPluginRegistrant.m文件也会有所变化。如下图

flutter-plugins

GeneratedPluginRegistrant.m

4.rootViewController

我们打开Main.storyboard文件发现是有一个叫做FlutterViewController的viewController。通过查阅文档,可以通过以下代码跳转

FlutterViewController *flutterViewController = [[FlutterViewController alloc] init];
//设置路由页面
//[flutterViewController setInitialRoute:@"route1"];
[self presentViewController:flutterViewController animated:false completion:nil];

三、总结步骤

通过步骤二,我们需要flutter_assets、App.framework、Flutter.framework还要将插件引入并继承FlutterAppDelegate然后使用FlutterViewController跳转


四、编写shell编译并获取iOS运行所需文件

1、在flutter_app目录下执行"flutter build ios debug"、"flutter build ios release"可以编译并得到debug/release相对应的App.framework、Flutter.framework,然后我们利用.flutter-plugins引入所有插件,导入flutter_assets文件,最后制作一个Flutter.podspec文件以便引入pod中

2、实现代码

1.将Flutter.podspec、flutter-ios.sh放入flutter_app目录下,运行flutter-ios.sh脚本便会flutter_app/build/flutter-ios/下生成debug和release对应的文件,使用pod。
2.将flutter-ios目录拷贝到项目目录下使用Podfile引入

flutter_app/flutter-ios

xcode/flutter-ios

3.1AppDelegate继承FlutterAppDelegate
3.2导入GeneratedPluginRegistrant并注册插件
3.3使用FlutterViewController显示Flutter

flutter

Flutter.podspec

#将代码保存到flutter_app目录下命名为Flutter.podspec
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
#

Pod::Spec.new do |s|
  s.name             = 'Flutter'
  s.version          = '1.0.0'
  s.summary          = 'High-performance, high-fidelity mobile apps.'
  s.description      = <<-DESC
Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
                       DESC
  s.homepage         = 'https://flutter.io'
  s.license          = { :type => 'MIT' }
  s.author           = { 'Flutter Dev Team' => 'meng1711@sina.com' }
  s.source           = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
  s.ios.deployment_target = '7.0'
  s.vendored_frameworks = 'Flutter.framework', 'App.framework'
  s.resources = 'flutter_assets'
  s.source_files = '*.{h,m}'
  s.public_header_files = '*.h'
end

flutter-ios.sh

#!/bin/sh
#将代码保存到flutter_app目录下命名为flutter-ios.sh

function echoError() {
    echo '\033[31mError:'$1'\033[0m'
}

function echoGreen() {
echo '\033[32m'$1'\033[0m'
}

#debug/release
function buildFlutter() {

    mode='release'
    if [ $# = 1 ] ; then
        mode=$1
    fi

    buildRoot='build/flutter-ios/'
    buildPath=$buildRoot$mode'-iphoneos/'
    buildRootPlugins=$buildRoot'plugins/'

    rm -rf $buildPath
    mkdir -p $buildPath

    #编译flutter
    echoGreen 'flutter build ios --'$mode
    flutter build ios --$mode

    #加载Flutter.framework
    flutterFramework='ios/Flutter/Flutter.framework'
    if [ ! -d $flutterFramework ]; then
        echoError 'Flutter.framework不存在'
        exit 0
    else
        echoGreen '加载Flutter.framework'
        cp -r $flutterFramework $buildPath
    fi

    #加载App.framework
    appFramework='ios/Flutter/App.framework'
    if [ ! -d $appFramework ]; then
        echoError 'App.framework不存在'
        exit 0
    else
        echoGreen '加载App.framework'
        cp -r $appFramework $buildPath
    fi

########################################

    #加载plugins
    if [ ! -d $buildRootPlugins ]; then
        mkdir -p $buildRootPlugins
        flutterPlugins='.flutter-plugins'
        if [ -f $flutterPlugins ]; then
            while read line 
            do
                array=(${line//=/ }) 
                pluginPath=$buildRootPlugins${array[0]} 
                mkdir -p $pluginPath
                cp -r ${array[1]}'/ios/.' $pluginPath
            done < $flutterPlugins
        fi
    fi
    echoGreen '加载plugins'
    ln -fs '../plugins' $buildPath'plugins'
    #注册文件
    if [ ! -f $buildRoot'GeneratedPluginRegistrant.h' ]; then
        flutterPluginsRegistrant='ios/Runner/GeneratedPluginRegistrant'
        if [ -f $flutterPluginsRegistrant'.h' ]; then
            cp -r $flutterPluginsRegistrant'.h' $buildRoot
            cp -r $flutterPluginsRegistrant'.m' $buildRoot
        fi
    fi
    ln -fs '../GeneratedPluginRegistrant.h' $buildPath'GeneratedPluginRegistrant.h'
    ln -fs '../GeneratedPluginRegistrant.m' $buildPath'GeneratedPluginRegistrant.m'

    #加载flutter_assets
    if [ ! -d $buildRoot'flutter_assets' ]; then
        flutterAssets='ios/Flutter/flutter_assets'
        if [ -d $flutterAssets ]; then
            cp -r $flutterAssets $buildRoot
        fi
    fi
    echoGreen '加载flutter_assets'
    ln -fs '../flutter_assets' $buildPath'flutter_assets'
    

    #复制Flutter.podspec
    if [ ! -f $buildRoot'Flutter.podspec' ]; then
        flutterPodspec='Flutter.podspec'
        if [ -f $flutterPodspec ]; then
            cp -r $flutterPodspec $buildRoot
        fi
    fi
    echoGreen '加载Flutter.podspec'
    ln -fs '../Flutter.podspec' $buildPath'Flutter.podspec'
}

start=$(date +%s)

#编译
rm -rf 'build/flutter-ios/'
mkdir -p 'build/flutter-ios/'
buildFlutter debug
buildFlutter release

end=$(date +%s)
echo '^v^制作完成,耗时:'$(( $end - $start ))'s'

Podfile

# Uncomment the next line to define a global platform for your project
# platform :ios, ‘9.0’

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.

#Flutter model: debug/release
def podFlutter(mode)
    
    flutterPath = 'flutter-ios/' + mode + '-iphoneos/'
    pod 'Flutter', :path => flutterPath
    
    pluginsPath = 'flutter-ios/' + mode + '-iphoneos/plugins/'
    if File.directory?(pluginsPath)
        Dir.foreach(pluginsPath) do |fileName|
            if fileName != '.' and fileName != '..' and fileName != '.DS_Store'
                pod fileName, :path => pluginsPath + fileName
            end
        end
    end
end

target 'demo' do
    podFlutter('debug')
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

本文只是探索了一种使用pod库来导入Flutter项目代码的方法,如果大家有更换好方法或建议欢迎留言
项目有些大就不传了,有不明白的可以留言或私信
晚安~