Flutter基础之混合开发项目集成之IOS篇

上一期我们聊了下<Flutter与原生项目Android混合开发的集成>,移动端的开发IOS自然是少不了,那么今天我们来聊一聊Flutter与原生项目IOS混合开发的集成。

还是说一下前提吧:大家在项目的开发中除了那种新立项的而且规模较小的项目可能是纯Flutter开发,大部分在项目中都可能是混合开发的模式,而且是以原生为主Flutter为辅的开发模式,很大部分原因就是你原来原生的项目不可能马上全部用Flutter重写一遍,而且是集成一部分Flutter的页面而已,像这种情况的话那么混合开发就是必须的,那么我们今天就先来聊聊怎么在原生项目去集成Flutter

我们还是回到最初看看一个完成Flutter项目是什么样的:

Pasted Graphic.png

如图一个新建的Flutter项目里面就包括了他的Android/IOS宿主,如果是Flutter为主原生为辅的开发可以就在这里面进行,而且这种项目适合一个全新的项目,就像上面提到的那样,不适合那种原生项目就很成熟了再集成Flutter框架的情况

如果我们要爱原有成熟的原生项目里面再集成Flutter的话首先要创建一个Flutter Module,如下:

Pasted Graphic 1.png

创建的项目路径要选择好,一般我们会与原生项目目录并排,如下:

Pasted Graphic 2.png

Flutter Module已经创建好了,我们来看看一个Flutter Module大概是什么样子的,如图:

Pasted Graphic 3.png

是不是与一个Flutter Project很像啊,是的可以说本质没有什么变化,Flutter Module依然是一个可以运行的项目,你可以进入该目录下运行 flutter run 就可以看到运行效果,上面我们说过一个Flutter的Project需要他们原生的宿主才能运行,Flutter Module也一样他们也需要原生宿主,就是红框里面的部分,不过这里面不建议你去修改(这一点不像Flutter Project)所以他们的文件夹在mac里面隐藏的

那么我们今天看看IOS项目怎么去集成:

目前为止有两种方案可以将flutter集成进iOS项目中

  • 使用CocoaPods和已安装的Flutter SDK(推荐使用这一种)。

  • 为Flutter引擎,已编译的Dart代码和所有Flutter插件创建生成 frameworks,手动在Xocde中嵌入这些frameworks。

  1. 第一种方案的优点:该方案是Flutter官方推荐的方案;集成过程相对简单;

  2. 第一种方案的缺点: 需要求项目中工程师的电脑上都要配置Flutter环境;并且 iOS项目里面要掺着一些Flutter相关的项目工程代码(不过这个不能说为一个明显的缺点)

  3. 第二种方案的优点:如果团队成员无法在本地安装Flutter SDK

  4. 第二种方案的缺点:集成步骤过于复杂;而且Flutter相关的代码有改动(哪怕是你想升级Flutter框架)则需要 flutter build ios 生成新的Frameworks集成在项目中;

所以我们今天就只介绍第一种集成的方式:使用CocoaPods和已安装的Flutter SDK,目前该版本的Flutter对于这种集成方式做得更加的优化了,目前只需要一个步骤即可:

我们只需要在iOS工程的podfile文件中添加如下命令:

# Flutter
flutter_application_path = '../flutter_module/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

target 'XXXAPP' do
   use_frameworks!
 
   # Flutter
   install_all_flutter_pods(flutter_application_path)

end

就可以一次性将flutter的编译的库由此依赖进入iOS宿主项目中, 不用再每次去在Xcode->Build Phases中去添加设置脚本文件路径等繁琐操作,简化了集成的步骤。

我做过实验你只要在Flutter项目修改的代码只需要在Xcode运行他的宿主项目就可以,我想这个是因为你在Xcode点击Run的时候他也会编译Flutter里面的代码,这样编译运行就会变得方便很多

现在我们需要开发一个Flutter页面去验证是否集成成功

IOS混合开发的页面也分两种情况:

  • 一种是从原生跳转到一个Flutter页面,

  • 一种是Flutter与原生混合在一个页面,

我们先看第一种的情况:

import 'dart:ui';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterHybird',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      //window.defaultRouteName可以接收从Native传过来的参数
      home: _widgetForRoute(window.defaultRouteName),
    );
  }
}

Widget _widgetForRoute(String route) {
  switch (route) {
    case 'route1':
      return Center(
        child: Text("route1"),
      );
    case 'route2':
      return Center(
        child: Text("route2"),
      );
    default:
      return Center(
        child: Text('route3’),
      );
  }
}

其中通过window.defaultRouteName可以按照Native传递过来的参数来选择启动的Widget

我们再开发一个Native的页面用于承载上面的Flutter,用于被原生页面跳转

#import "ViewController.h"
#import "AppDelegate.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setFrame:CGRectMake(100, 100, 200, 100)];
    [button setTitle:@"Click" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(button_click) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
}

- (void)button_click {
    
    FlutterViewController *flutterViewController = [[FlutterViewController alloc] init];
    [flutterViewController setInitialRoute:self.inputParams];
    [self.navigationController pushViewController:flutterViewController animated:YES];

}

然后在原生进行跳转即可,通过setInitialRoute传递参数,对应就是Flutter的window.defaultRouteName来获取参数

是的你没有看错,就是这样就可以了,他内部帮你做了很多处理,例如默认找到了Flutter的main.dark,默认执行里面的main方法,这种情况再ReactNative是想都不敢想的,因为我们知道ReactNative的JS module至少是要指定跳转的moduleName的,因为在ReactNative里面每个module都是需要注册的,这个在Flutter没有这个步骤

initialRoute从名称上看起来是Flutter提供给我们进行Native与Flutter交互的路由跳转的,
但是实际上他就是一个字符串,我们可以传递一个路由名称,有时候我们也可以通过这个参数传递一个字符串,然后在Flutter端进行解析,就像上面一样,下面我们来一个传递路由页面的例子来使用他

我们先指定一个注册了路由的Flutter页面(因为只有你注册了路由才知道要跳转到哪个路由),如下:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      routes: {
        'one_page':(context){
          return OnePage();
        },
        'two_page':(context){
          return TwoPage();
        }
      },
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

然后我们使用initialRoute传递跳转的路由页面即可:

FlutterViewController *flutterViewController1 = [[FlutterViewController alloc] init];
[flutterViewController1 setInitialRoute:@“one_page”];
[self.navigationController pushViewController:flutterViewController animated:YES];

OK 上面聊了下Flutter里面简单的页面跳转,我们再来聊聊在IOS原生页面中嵌入FlutterUI组件:

  • 第一种方式非常的简单,直接把FlutterViewController当做子VC集成在主ViewController里面即可如下:
FlutterViewController *fluvc = [[FlutterViewController alloc]init];
    [self addChildViewController:fluvc];
    fluvc.view.frame = self.view1.bounds;
    [fluvc didMoveToParentViewController:self];
    [self.view1 addSubview:fluvc.view];
    [self.navigationController pushViewController:fluvc animated:YES];

这种方式非常的简单,也是我非常推荐的一种方式

  • 第二种方式针对于Xib或者StoryBoard开发的情况操作也是非常的简单,这种方式也是我从网上看到的,如下:
1__#$!@%!#__Pasted Graphic.png

里面拖拉了两个Container View ,与两个ViewController,两个Container View与两个ViewController通过Segue相连,Segue的连接方式为Embed即可,这样的话你的XIB或者StoryBoard上面也可以通过拖拉控件去与Flutter页面混合集成

最后说一说Flutter页面的性能问题,加载 FlutterActivity 页面时明显看到一段时间的黑屏,这段时间主要是启动 Flutter 引擎(FlutterEngine),Flutter 引擎启动的时间在不同手机上不同,每一个 FlutterActivity 页面都会重新启动一个Flutter 引擎,所以不要在一个项目中创建多个 FlutterActivity(或者启动多个 FlutterActivity 实例),否则内存会越来越大,为了减少 FlutterActivity 页面的延迟时间和多个 FlutterActivity 实例内存一直增长问题,我们可以缓存 Flutter 引擎(FlutterEngine),在启动 App 的时候先启动 Flutter 引擎,然后使用缓存的引擎加载页面,通常将其放在 AppDelegate 中:

import UIKit
import Flutter

@UIApplicationMain
class AppDelegate: FlutterAppDelegate {
  lazy var flutterEngine = FlutterEngine(name: "my flutter engine")

  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    flutterEngine.run();
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions);
  }
}

使用的时候再取出来就可以了:

    @objc func showFlutter() {
      let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
      let flutterViewController =
          FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
      present(flutterViewController, animated: true, completion: nil)
    }

好了,我们的《Flutter与原生混合开发项目集成之IOS篇》已经到结尾了,相信上面的内容还是比较简单易懂的,如果你有什么问题可以给我留言,欢迎大家一起讨论,如果想看<Flutter与原生项目Android混合开发的集成>的话可以点击这里,Flutter是一个非常庞大的框架,希望接下来的学习中可以输出更多高质量的文章,好了我们一起期待吧···

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容