美文网首页Flutter圈子程序员iOS开发进阶
iOS老项目集成Flutter(iOS混编Flutter)

iOS老项目集成Flutter(iOS混编Flutter)

作者: GA_ | 来源:发表于2018-11-02 16:25 被阅读13次
    问题

    Flutter已经玩了些日子,但是老项目怎么集成Flutter呢?
    集成了怎么热重载?
    Flutter和原生安卓、iOS是怎么关联的呢?
    Flutter和原生是怎么交互?

    iOS老项目集成Flutter流程:

    1、flutter环境安装 flutter中文网,特别详细,跟着来就行。
    2、确保环境配置正确,全部都是小对号
    终端执行

    flutter doctor -v
    

    如下是打印结果:

    houjianan:FlutterMixed> flutter doctor -v
    [✓] Flutter (Channel master, v0.10.2-pre.119, on Mac OS X 10.13.4 17E202, locale zh-Hans-CN)
        • Flutter version 0.10.2-pre.119 at /Users/houjianan/flutter
        • Framework revision 50098f149d (2 days ago), 2018-10-30 22:54:49 -0400
        • Engine revision 3a67757300
        • Dart version 2.1.0 (build 2.1.0-dev.8.0 bf26f760b1)
    [✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
        • Android SDK at /Users/houjianan/Library/Android/sdk
        • Android NDK at /Users/houjianan/Library/Android/sdk/ndk-bundle
        • Platform android-28, build-tools 28.0.3
        • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
        • All Android licenses accepted.
    [✓] iOS toolchain - develop for iOS devices (Xcode 9.4.1)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Xcode 9.4.1, Build version 9F2000
        • ios-deploy 1.9.2
        • CocoaPods version 1.5.3
    [✓] Android Studio (version 3.2)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin version 29.1.1
        • Dart plugin version 181.5656
        • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    [✓] IntelliJ IDEA Ultimate Edition (version 2018.1.1)
        • IntelliJ at /Applications/IntelliJ IDEA.app
        • Flutter plugin version 27.1.2
        • Dart plugin version 181.4445.29
    [✓] Connected device (6 available)
        • “houjianan”的 iPhone • ffba6dc829885833aab8cf53b275216ee9949c05 • ios • iOS 12.0.1
    • No issues found!
    

    环境配置好了 就可以创建一个iOS项目或者在老项目上操作

    3、iOS工程Enable Bitcode 需要关闭,因为Flutter混合开发不支持Bitcode

    E9666FF3-7A49-478D-AB60-C4984D9FBE51.png

    4、flutter module创建

    a、切换分支flutter channel master
    
    houjianan:Desktop> cd /Users/houjianan/flutter 
    houjianan:flutter> flutter channel master
     ╔════════════════════════════════════════════════════════════════════════════╗
      ║ A new version of Flutter is available!                                     ║
      ║                                                                            ║
      ║ To update to the latest version, run "flutter upgrade".                    ║
      ╚════════════════════════════════════════════════════════════════════════════╝
    Switching to flutter channel 'master'...
    git: Already on 'master'
    git: Your branch is behind 'origin/master' by 19 commits, and can be fast-forwarded.
    git:   (use "git pull" to update your local branch)
    houjianan:flutter> 
    

    拉新代码

    houjianan:flutter> git pull
    

    b、创建flutter create -t module flutter_module

    houjianan:fluttermixed> cd /Users/houjianan/Desktop/fluttermixed 
    houjianan:fluttermixed> ls
    FlutterMixed
    houjianan:fluttermixed> flutter create -t module flutter_module
    Creating project flutter_module...
      flutter_module/.gitignore (created)
      flutter_module/.idea/libraries/Dart_SDK.xml (created)
      flutter_module/.idea/libraries/Flutter_for_Android.xml (created)
      flutter_module/.idea/modules.xml (created)
      flutter_module/.idea/workspace.xml (created)
      flutter_module/.metadata (created)
      flutter_module/lib/main.dart (created)
      flutter_module/flutter_module.iml (created)
      flutter_module/flutter_module_android.iml (created)
      flutter_module/pubspec.yaml (created)
      flutter_module/README.md (created)
      flutter_module/test/widget_test.dart (created)
    Running "flutter packages get" in flutter_module...         21.9s
    Wrote 12 files.
    All done!
    Your module code is in flutter_module/lib/main.dart.
    houjianan:fluttermixed> ls
    FlutterMixed  flutter_module
    houjianan:fluttermixed> cd flutter_module/
    houjianan:flutter_module> ls -la
    total 88
    drwxr-xr-x  15 houjianan  staff   510 11  2 10:38 .
    drwxr-xr-x   5 houjianan  staff   170 11  2 10:37 ..
    drwxr-xr-x  12 houjianan  staff   408 11  2 10:38 .android
    -rw-r--r--   1 houjianan  staff   339 11  2 10:37 .gitignore
    drwxr-xr-x   5 houjianan  staff   170 11  2 10:37 .idea
    drwxr-xr-x   7 houjianan  staff   238 11  2 10:38 .ios
    -rw-r--r--   1 houjianan  staff   308 11  2 10:37 .metadata
    -rw-r--r--   1 houjianan  staff  4983 11  2 10:38 .packages
    -rw-r--r--   1 houjianan  staff   162 11  2 10:37 [README.md](http://readme.md/)
    -rw-r--r--   1 houjianan  staff   896 11  2 10:37 flutter_module.iml
    -rw-r--r--   1 houjianan  staff  1465 11  2 10:37 flutter_module_android.iml
    drwxr-xr-x   3 houjianan  staff   102 11  2 10:37 lib
    -rw-r--r--   1 houjianan  staff  8556 11  2 10:38 pubspec.lock
    -rw-r--r--   1 houjianan  staff   370 11  2 10:37 pubspec.yaml
    drwxr-xr-x   3 houjianan  staff   102 11  2 10:37 test
    houjianan:flutter_module> 
    

    创建完之后 如下图
    注意:查看隐藏文件快捷键 shift+command+。

    B24B53FD-3D23-4004-A08C-2737D130672E.png

    创建iOS项目的Config文件(管理Xcode工程的配置衔接文件) 里面包含分别创建 Flutter.xcconfig、Debug.xcconfig、Release.xcconfig 三个配置文件;其中Flutter.xcconfig 是指向外目录flutter module的Generated.xcconfig 文件路径引用文件,其他两个代表Xcode的环境配置文件。

    10FEC9A7-44D1-423E-B808-82F69E0DE56C.png
    E88EBAF3-AE9A-422D-899F-B0D98933C0CF.png

    使用了cocoapods 在Debug和Release里面添加

    #include "Pods/Target Support Files/Pods-FlutterMixed/Pods-FlutterMixed.debug.xcconfig"
    #include "Pods/Target Support Files/Pods-FlutterMixed/Pods-FlutterMixed.release.xcconfig"
    

    Flutter.xcconfig

    //
    //  Flutter.xcconfig
    //  FlutterMixed
    //
    //  Created by 侯佳男 on 2018/11/2.
    //  Copyright © 2018年 侯佳男. All rights reserved.
    //
    #include "../../flutter_module/.ios/Flutter/Generated.xcconfig"
    ENABLE_BITCODE=NO
    

    Debug.xcconfig

    //
    //  Debug.xcconfig
    //  FlutterMixed
    //
    //  Created by 侯佳男 on 2018/11/2.
    //  Copyright © 2018年 侯佳男. All rights reserved.
    //
    #include "Flutter.xcconfig"
    #include "Pods/Target Support Files/Pods-FlutterMixed/Pods-FlutterMixed.debug.xcconfig"
    

    Release.xcconfig

    //
    //  Release.xcconfig
    //  FlutterMixed
    //
    //  Created by 侯佳男 on 2018/11/2.
    //  Copyright © 2018年 侯佳男. All rights reserved.
    //
    #include "Flutter.xcconfig"
    #include "Pods/Target Support Files/Pods-FlutterMixed/Pods-FlutterMixed.debug.xcconfig"
    FLUTTER_BUILD_MODE=release
    

    Xcode project环境配置选择

    EEE2A73B-AB3E-4C1B-890A-8C4208822188.png

    添加脚本

    "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh"build
    "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh”embed
    
    52EB1B8C-406A-4C1D-9250-3DD0A3445B2C.png 4D47862C-91B6-42A8-9F5D-A70FA5FD6001.png

    注意:
    Run Script 在Target Dependencies或者[CP]Check pods Manifest.lock后面

    添加好了之后run下项目,就会执行脚本,iOS工程文件下会有一个Flutter文件夹(第一次搞是这么回事儿,有时候就是不会生成,具体为啥不清楚,有知道的请留言告诉我,感谢!)。
    run之后如果没有Flutter这个文件夹,手动创建一个,然后把flutter_module->.ios->Fluter里面的App.framework、engine、flutter_assets添加进刚才手动创建的Flutter文件夹内

    A47D9226-51D0-487B-A95E-20FE342AEAA5.png

    项目里面文件夹的颜色不同。


    012931A0-DF96-4FF2-A46A-2E4332EB716D.png

    蓝色文件夹是选择了Create folder references
    黄色文件夹是选择了Create groups
    flutter_assets一定要蓝色的 不然flutter界面啥都看不见。

    用Android Studio运行项目

    main.dart文件里面的内容全部替换为如下代码

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            // This is the theme of your application.
            //
            // Try running your application with "flutter run". You'll see the
            // application has a blue toolbar. Then, without quitting the app, try
            // changing the primarySwatch below to Colors.green and then invoke
            // "hot reload" (press "r" in the console where you ran "flutter run",
            // or press Run > Flutter Hot Reload in a Flutter IDE). Notice that the
            // counter didn't reset back to zero; the application is not restarted.
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      // This widget is the home page of your application. It is stateful, meaning
      // that it has a State object (defined below) that contains fields that affect
      // how it looks.
    
      // This class is the configuration for the state. It holds the values (in this
      // case the title) provided by the parent (in this case the App widget) and
      // used by the build method of the State. Fields in a Widget subclass are
      // always marked "final".
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
    
      // 创建一个给native的channel (类似iOS的通知)
      static const methodChannel = const MethodChannel('com.pages.your/native_get');
    
      int _counter = 0;
    
      void _incrementCounter() {
        setState(() {
          _counter++;
    
          print('flutter的log打印:现在输出count=$_counter');
          // 当个数累积到3的时候给客户端发参数
          if(_counter == 3) {
            _toNativeSomethingAndGetInfo();
          }
    
          // 当个数累积到5的时候给客户端发参数
          if(_counter == 1002) {
            Map<String, String> map = { "title": "这是一条来自flutter的参数" };
            methodChannel.invokeMethod('toNativePush',map);
          }
    
          // 当个数累积到8的时候给客户端发参数
          if(_counter == 1005) {
            Map<String, dynamic> map = { "content": "flutterPop回来","data":[1,2,3,4,5]};
            methodChannel.invokeMethod('toNativePop',map);
          }
        });
      }
    
      // 给客户端发送一些东东 , 并且拿到一些东东
      Future<Null> _toNativeSomethingAndGetInfo() async {
        dynamic result;
        try {
          result = await methodChannel.invokeMethod('toNativeSomething','大佬你点击了$_counter下');
        } on PlatformException {
          result = 100000;
        }
        setState(() {
          // 类型判断
          if (result is int) {
            _counter = result;
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
    
        return new Scaffold(
    //      appBar: new AppBar(
    //        // Here we take the value from the MyHomePage object that was created by
    //        // the App.build method, and use it to set our appbar title.
    //        title: new Text(widget.title),
    //      ),
          body: new Center(
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new Text(
                  'he button this many times:',
                ),
                new Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.display1,
                ),
              ],
            ),
          ),
          floatingActionButton: new FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: new Icon(Icons.add),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    

    OC-ViewController.m

    //
    //  ViewController.m
    //  FlutterMixed
    //
    //  Created by 侯佳男 on 2018/11/2.
    //  Copyright © 2018年 侯佳男. All rights reserved.
    //
    #import "ViewController.h"
    #import <Flutter/Flutter.h>
    @interfaceViewController()
    @end
    @implementationViewController
    - (void)viewDidLoad {
        [superviewDidLoad];
    }
    - (void)didReceiveMemoryWarning {
        [superdidReceiveMemoryWarning];
    }
    - (void)pushFlutterViewController {
        FlutterViewController* flutterViewController = [[FlutterViewControlleralloc] initWithProject:nilnibName:nilbundle:nil];
        flutterViewController.navigationItem.title= @"Flutter Demo";
        __weak__typeof(self) weakSelf = self;
        // 要与main.dart中一致
        NSString*channelName = @"com.pages.your/native_get";
        FlutterMethodChannel*messageChannel = [FlutterMethodChannelmethodChannelWithName:channelName binaryMessenger:flutterViewController];
        [messageChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnullcall, FlutterResult  _Nonnullresult) {
            // call.method 获取 flutter 给回到的方法名,要匹配到 channelName 对应的多个 发送方法名,一般需要判断区分
            // call.arguments 获取到 flutter 给到的参数,(比如跳转到另一个页面所需要参数)
            // result 是给flutter的回调, 该回调只能使用一次
            NSLog(@"flutter 给到我:\nmethod=%@ \narguments = %@",call.method,call.arguments);
            if([call.methodisEqualToString:@"toNativeSomething"]) {
                UIAlertView*alertView = [[UIAlertViewalloc] initWithTitle:@"flutter回调"message:[NSStringstringWithFormat:@"%@",call.arguments] delegate:selfcancelButtonTitle:@"确定"otherButtonTitles:nil];
                [alertView show];
                // 回调给flutter
                if(result) {
                    result(@1000);
                }
            } elseif([call.methodisEqualToString:@"toNativePush"]) {
    //            ThirdViewController *testVC = [[ThirdViewController alloc] init];
    //            testVC.parames = call.arguments;
    //            [weakSelf.navigationController pushViewController:testVC animated:YES];
            } elseif([call.methodisEqualToString:@"toNativePop"]) {
                [weakSelf.navigationControllerpopViewControllerAnimated:YES];
            }
        }];
    //    [self.navigationController pushViewController:flutterViewController animated:YES];
        [selfpresentViewController:flutterViewController animated:YEScompletion:nil];
    }
    -(void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event{
        [selfpushFlutterViewController];
    }
    @end
    

    OC-AppDelegate.h

    //
    //  AppDelegate.h
    //  FlutterMixed
    //
    //  Created by 侯佳男 on 2018/11/2.
    //  Copyright © 2018年 侯佳男. All rights reserved.
    //
    #import <Flutter/Flutter.h>
    @interfaceAppDelegate : FlutterAppDelegate<UIApplicationDelegate, FlutterAppLifeCycleProvider>
    @end
    

    OC-AppDelegate.m

    //
    //  AppDelegate.h
    //  FlutterMixed
    //
    //  Created by 侯佳男 on 2018/11/2.
    //  Copyright © 2018年 侯佳男. All rights reserved.
    //
    #import "AppDelegate.h"
    @interfaceAppDelegate()
    @end
    @implementationAppDelegate
    {
        FlutterPluginAppLifeCycleDelegate*_lifeCycleDelegate;
    }
    - (instancetype)init {
        if(self= [superinit]) {
            _lifeCycleDelegate= [[FlutterPluginAppLifeCycleDelegatealloc] init];
        }
        returnself;
    }
    - (BOOL)application:(UIApplication*)application
    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
        return[_lifeCycleDelegateapplication:application didFinishLaunchingWithOptions:launchOptions];
    }
    - (void)applicationDidEnterBackground:(UIApplication*)application {
        [_lifeCycleDelegateapplicationDidEnterBackground:application];
    }
    - (void)applicationWillEnterForeground:(UIApplication*)application {
        [_lifeCycleDelegateapplicationWillEnterForeground:application];
    }
    - (void)applicationWillResignActive:(UIApplication*)application {
        [_lifeCycleDelegateapplicationWillResignActive:application];
    }
    - (void)applicationDidBecomeActive:(UIApplication*)application {
        [_lifeCycleDelegateapplicationDidBecomeActive:application];
    }
    - (void)applicationWillTerminate:(UIApplication*)application {
        [_lifeCycleDelegateapplicationWillTerminate:application];
    }
    - (void)application:(UIApplication*)application
    didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
        [_lifeCycleDelegateapplication:application
    didRegisterUserNotificationSettings:notificationSettings];
    }
    - (void)application:(UIApplication*)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
        [_lifeCycleDelegateapplication:application
    didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    }
    - (void)application:(UIApplication*)application
    didReceiveRemoteNotification:(NSDictionary*)userInfo
    fetchCompletionHandler:(void(^)(UIBackgroundFetchResultresult))completionHandler {
        [_lifeCycleDelegateapplication:application
           didReceiveRemoteNotification:userInfo
                 fetchCompletionHandler:completionHandler];
    }
    - (BOOL)application:(UIApplication*)application
                openURL:(NSURL*)url
                options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
        return[_lifeCycleDelegateapplication:application openURL:url options:options];
    }
    -(BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
        return[_lifeCycleDelegateapplication:application handleOpenURL:url];
    }
    -(BOOL)application:(UIApplication*)application
                openURL:(NSURL*)url
      sourceApplication:(NSString*)sourceApplication
             annotation:(id)annotation {
        return[_lifeCycleDelegateapplication:application
                                       openURL:url
                             sourceApplication:sourceApplication
                                    annotation:annotation];
    }
    - (void)application:(UIApplication*)application
    performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
      completionHandler:(void(^)(BOOLsucceeded))completionHandler NS_AVAILABLE_IOS(9_0) {
        [_lifeCycleDelegateapplication:application
           performActionForShortcutItem:shortcutItem
                      completionHandler:completionHandler];
    }
    - (void)application:(UIApplication*)application
    handleEventsForBackgroundURLSession:(nonnullNSString*)identifier
      completionHandler:(nonnullvoid(^)(void))completionHandler {
        [_lifeCycleDelegateapplication:application
    handleEventsForBackgroundURLSession:identifier
                      completionHandler:completionHandler];
    }
    - (void)application:(UIApplication*)application
    performFetchWithCompletionHandler:(void(^)(UIBackgroundFetchResultresult))completionHandler {
        [_lifeCycleDelegateapplication:application performFetchWithCompletionHandler:completionHandler];
    }
    - (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate {
        [_lifeCycleDelegateaddDelegate:delegate];
    }
    #pragma mark - Flutter
    // Returns the key window's rootViewController, if it's a FlutterViewController.
    // Otherwise, returns nil.
    - (FlutterViewController*)rootFlutterViewController {
        UIViewController* viewController = [UIApplicationsharedApplication].keyWindow.rootViewController;
        if([viewController isKindOfClass:[FlutterViewControllerclass]]) {
            return(FlutterViewController*)viewController;
        }
        returnnil;
    }
    - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
        [supertouchesBegan:touches withEvent:event];
        // Pass status bar taps to key window Flutter rootViewController.
        if(self.rootFlutterViewController!= nil) {
            [self.rootFlutterViewControllerhandleStatusBarTouches:event];
        }
    }
    @end
    

    贴完代码运行Xcode 点击屏幕 跳转flutter界面。
    更改flutter界面代码 再运行Xcode 点击屏幕 跳转flutter界面,界面被更改。
    热重载具体内容看闲鱼相关文章,多人写作开发看闲鱼相关文章,可以关注闲鱼公众号,他们定期更新超优质Flutter相关文章。s

    中文文档
    混编详细一篇
    混编详细一篇
    闲鱼一篇
    官网
    Flutter TIPS
    Flutter和iOS交互

    相关文章

      网友评论

      • 搞好关系:breaking exception '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>'

        iOS向flutter发送NSDitionry

      本文标题:iOS老项目集成Flutter(iOS混编Flutter)

      本文链接:https://www.haomeiwen.com/subject/ocrcxqtx.html