Flutter踩坑日記:接入現有iOS項目

以前搞的Flutter版工具鏈已經弄完了,感興趣的朋友能夠圍觀下,Android版本dio庫(v2.0.14)發送網絡請求總是報錯,去官方提了issue還沒回,因而今天搞一下把Flutter模塊接入到已有項目中。android

首先Flutter官方已經出了wiki,教你們怎麼接入。懶得看官方原文的能夠看看這個簡易版快速集成,先來看看效果。ios

1、建立Flutter模塊

通常用Flutter或者React Native的團隊大部分緣由都是由於須要跨平臺,因此在已有項目目錄的上一級建立Flutter模塊,譬如你的工做目錄在some/path/MyApp,那麼你須要在some/path下執行,這樣Flutter能夠單獨交給一個虛擬團隊維護,各自獨立。git

$ cd some/path/

$ flutter create -t module my_flutter

文件結構以下:github

some/path/

  my_flutter/

    lib/main.dart

    .ios/

  MyApp/

      MyApp.xcodeproj

      Podfile

    MyApp/

      AppDelegate.h

      AppDelegate.m (or swift)

2、利用Cocoapods添加依賴

沒用過Cocoapods的童鞋能夠google搜一下,以前寫過不少Cocoapods的文章因爲博客主機商跑路都沒了,說多了都是淚swift

1.在Podfile文件加入如下代碼:xcode

flutter_application_path = '../my_flutter/'

eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)

注意my_flutter的路徑,由於咱們是以xcodeproj文件的上一級建立的,因此須要加../,這個和React Native引入依賴路徑是同樣的,都須要注意這個問題。bash

2.執行pod install網絡

注:若是報錯app

[!] InvalidPodfilefile: No such file or directory @ rb_sysopen - ./my_flutter/.ios/Flutter/podhelper.rb.ide

須要在my_flutter文件夾下執行如下flutter run,把.ios.androidflutter配置生成出來。

3.禁用bitcode,設置環境變量

找到你的targetBuild Settings->Build Options->Enable Bitcode設置爲NO,另外在Preprocessor Macros添加FLUTTER_BUILD_MODE=DebugFLUTTER_BUILD_MODE=Release,若是還有其餘環境須要根據狀況選擇DebugRelease

image

4.添加run script

找到你的targetBuild Phases -> + -> New Run Script Phase,而且在bashzsh配置FLUTTER_ROOT,不然打包會出錯找不到flutter。涉及多人開發還須要安裝路徑位置,不然團隊小夥伴每一個人的路徑不同。

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

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

注:把這個script放到Target Dependencies phasescript的後面。

5.添加assets資源

官網說須要添加Flutter.framework資源,可是這個Cocoapods已經幫忙引入了,因此就不須要了,若是沒有被引入進去那就手動引入下。這裏只須要添加flutter_assets就好了,記得選Creat folder references引入方式。Flutter 1.0.0版本路徑是my_flutter->build->flutter_assets

6.修改AppDelegate

Objective-C:

AppDelegate.h

#import <UIKit/UIKit.h>

#import <Flutter/Flutter.h>

@interface AppDelegate : FlutterAppDelegate

@property (nonatomic,strong) FlutterEngine *flutterEngine;

@end

AppDelegate.m

#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Only if you have Flutter Plugins

#include "AppDelegate.h"

@implementation AppDelegate

// This override can be omitted if you do not have any Flutter Plugins.

- (BOOL)application:(UIApplication *)application

    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];

  [self.flutterEngine runWithEntrypoint:nil];

  [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];

  return [super application:application didFinishLaunchingWithOptions:launchOptions];

}

@end

Swift:

AppDelegate.swift

import UIKit

import Flutter

import FlutterPluginRegistrant // Only if you have Flutter Plugins.

@UIApplicationMain

class AppDelegate: FlutterAppDelegate {

  var flutterEngine : FlutterEngine?;

  // Only if you have Flutter plugins.

  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil);

    self.flutterEngine?.run(withEntrypoint: nil);

    GeneratedPluginRegistrant.register(with: self.flutterEngine);

    return super.application(application, didFinishLaunchingWithOptions: launchOptions);

  }

}

到這裏,咱們的Flutter就算已經引入工程了

3、跳轉Flutter頁面

Objective-C:

ViewController.m

#import <Flutter/Flutter.h>

#import "AppDelegate.h"

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

    [button addTarget:self

              action:@selector(handleButtonAction)

    forControlEvents:UIControlEventTouchUpInside];

    [button setTitle:@"Press me" forState:UIControlStateNormal];

    [button setBackgroundColor:[UIColor blueColor]];

    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);

    [self.view addSubview:button];

}

- (void)handleButtonAction {

    FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];

    FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];

    [self presentViewController:flutterViewController animated:false completion:nil];

}

@end

Swift:

ViewController.swift

import UIKit

import Flutter

class ViewController: UIViewController {

  override func viewDidLoad() {

    super.viewDidLoad()

    let button = UIButton(type:UIButtonType.custom)

    button.addTarget(self, action: #selector(handleButtonAction), for: .touchUpInside)

    button.setTitle("Press me", for: UIControlState.normal)

    button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)

    button.backgroundColor = UIColor.blue

    self.view.addSubview(button)

  }

  @objc func handleButtonAction() {

    let flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine;

    let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)!;

    self.present(flutterViewController, animated: false, completion: nil)

  }

}

剩下的就是寫Flutter頁面邏輯了以及Flutter模塊須要調用Native API(後面專門寫一篇文章)。

寫完Flutter頁面,若是要跳轉到指定頁面使用Flutter的路由就能夠了

Objective-C:

[flutterViewController setInitialRoute:@"route1"];

Swift:

flutterViewController.setInitialRoute("route1")

4、運行

my_flutter手動flutter run一下,或者直接開啓熱加載,而後就能夠在原有的iOS項目中用Xcoderun起來了。

$ flutter attach --isolate-filter='debug'

Waiting for a connection from Flutter...

Done.

Syncing files to device...      1.1s

🔥  To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".

An Observatory debugger and profiler is available at: http://127.0.0.1:43343/

For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".

Connected view:

  debug isolate (isolates/642101161)

5、發佈

執行flutter build ios以建立release版本(flutter build默認爲--release

相關文章
相關標籤/搜索