The original link

tryenough.com/flutter03


Add Flutter to an existing iOS project


Creating an iOS project

If you already have an iOS project, you can use it directly. Let’s start by creating an empty iOS project to simulate an existing one, called TestOne

Create a Flutter module

Enter the same directory as your project, if your project is in… Path1 /path2/yourApp, then you should go to the path2 directory

$ cd. path1/path2/
$ flutter create -t module my_flutter
Copy the code

The command above creates a project module for Flutter. Inside the Flutter folder is a hidden.ios folder containing Cocoapods and Ruby scripts.

The finished project catalog looks like this:

Add the Flutter module as a dependency to the main project

Adding Flutter requires using Cocoapods. If you haven’t already installed Flutter, please refer to the detailed tutorial here: Install Cocoapods

Create the Podfile file

If your project already uses Cocoapods, skip this step. Once the installation is complete, switch to the project folder:

CD Project PathCopy the code

Initialize the pod environment:

pod init
Copy the code

At this point, a Podfile appears in the project, where the third-party libraries to add project dependencies are configured.

Edit the Podfile file and add the last two lines of code:

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

target 'TestOne' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

  # Pods for TestOne

  target 'TestOneTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'TestOneUITests' do
    inherit! :search_paths
    # Pods for testing
  end

end

# New code added
flutter_application_path = '.. /my_flutter'
eval(File.read(File.join(flutter_application_path, '.ios'.'Flutter'.'podhelper.rb')), binding)
Copy the code

Where Flutter_application_path represents the path of your FLUTTER module. run

pod install
Copy the code

Add build Phase for compiling Dart code

Open the iOS project, select the Build Phases option for the TARGETTestOne project, click the + button in the upper left corner, select New Run Script Phase, and add the following shell Script to the input box:

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

As shown below:

Then you need to compile the project: Command + B. If you run into a similar problem:

FlutterPluginRegistrant/libFlutterPluginRegistrant.a(GeneratedPluginRegistrant.o)’ does not contain bitcode.You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.for architecture arm64

Just turn off EnableBitCode because BitCode is not currently supported by Flutter.

Use the FlutterViewController in the main app

Modify Appdelegate. h inherited from FlutterAppDelegate:

#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>

@interface AppDelegate : FlutterAppDelegate
@end
Copy the code

Modify the appdelegate. m file:

#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 {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end
Copy the code

If you are Swift code, you can modify the appdelegate. Swift file:

import UIKit
import Flutter
import FlutterPluginRegistrant // Only if you have Flutter Plugins.

@UIApplicationMain
class AppDelegate: FlutterAppDelegate {

  // Only if you have Flutter plugins.
  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    GeneratedPluginRegistrant.register(with: self);
    return super.application(application, didFinishLaunchingWithOptions: launchOptions); }}Copy the code

But if your delegate is inherited from other classes, can let you delegate implementation FlutterAppLifeCycleProvider protocol:

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

@interface AppDelegate : UIResponder <UIApplicationDelegate, FlutterAppLifeCycleProvider>
@property (strong, nonatomic) UIWindow *window;
@end
Copy the code

Then life cycle method should by FlutterPluginAppLifeCycleDelegate agent:

@implementation AppDelegate { FlutterPluginAppLifeCycleDelegate *_lifeCycleDelegate; } - (instancetype)init { if (self = [super init]) { _lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init]; } return self; } - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { [GeneratedPluginRegistrant registerWithRegistry:self]; // Only if you are using Flutter plugins. return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions]; } // Returns the key window's rootViewController, if it's a FlutterViewController. // Otherwise, returns nil. - (FlutterViewController*)rootFlutterViewController { UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[FlutterViewController class]]) { return (FlutterViewController*)viewController; } return nil; } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { [super touchesBegan:touches withEvent:event]; // Pass status bar taps to key window Flutter rootViewController. if (self.rootFlutterViewController ! = nil) { [self.rootFlutterViewController handleStatusBarTouches:event]; } } - (void)applicationDidEnterBackground:(UIApplication*)application { [_lifeCycleDelegate applicationDidEnterBackground:application]; } - (void)applicationWillEnterForeground:(UIApplication*)application { [_lifeCycleDelegate applicationWillEnterForeground:application]; } - (void)applicationWillResignActive:(UIApplication*)application { [_lifeCycleDelegate applicationWillResignActive:application]; } - (void)applicationDidBecomeActive:(UIApplication*)application { [_lifeCycleDelegate applicationDidBecomeActive:application]; } - (void)applicationWillTerminate:(UIApplication*)application { [_lifeCycleDelegate applicationWillTerminate:application]; } - (void)application:(UIApplication*)application didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings { [_lifeCycleDelegate application:application didRegisterUserNotificationSettings:notificationSettings]; } - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { [_lifeCycleDelegate application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { [_lifeCycleDelegate application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options { return [_lifeCycleDelegate application:application openURL:url options:options]; } - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url { return [_lifeCycleDelegate application:application handleOpenURL:url]; } - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation { return [_lifeCycleDelegate application:application openURL:url sourceApplication:sourceApplication annotation:annotation]; } - (void)application:(UIApplication*)application performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) { [_lifeCycleDelegate application:application performActionForShortcutItem:shortcutItem completionHandler:completionHandler]; } - (void)application:(UIApplication*)application handleEventsForBackgroundURLSession:(nonnull NSString*)identifier completionHandler:(nonnull void (^)(void))completionHandler { [_lifeCycleDelegate application:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler]; } - (void)application:(UIApplication*)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { [_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler]; } - (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate { [_lifeCycleDelegate addDelegate:delegate]; } @endCopy the code

Using FlutterViewController

Add test code to viewController.m:

#import <Flutter/Flutter.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 {
    FlutterViewController* flutterViewController = [[FlutterViewController alloc] init];
    [self presentViewController:flutterViewController animated:false completion:nil];
}
@end
Copy the code

The swift version:

import UIKit
import Flutter

class ViewController: UIViewController {
  override func viewDidLoad(a) {
    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(a) {
    let flutterViewController = FlutterViewController(a)self.present(flutterViewController, animated: false, completion: nil)}}Copy the code

Now run the code and click the button to see a full screen Flutter interface on your app. You can also set the route to another component:

  • Objective-C:
[flutterViewController setInitialRoute:@"route1"];
Copy the code
  • Swift:
flutterViewController.setInitialRoute("route1")
Copy the code

You can use SystemNavigator.pop() in the Dart code to pop the FlutterViewController onto the display stack before showing it (just like iOS native).

Sample screenshot:

Click the button to jump to the Flutter page:

Use the thermal overload method

Hot reloading refers to seeing the changes without rebooting, similar to the way you see them when you save them during Web page development. Enter the FLUTTER module and run the following command:

$ cd some/path/my_flutter
$ flutter attach
Waiting for a connection from Flutter on iPhone XR...

Copy the code

Next, launch your app in Xcode and enter the Page of Flutter. You should see the following message on the console:

🔥 To hot reload changes while running, press “r”. To hot restart (and rebuild state), press “R”. An Observatory debugger and profiler on Android SDK built for x86 is available at: http://127.0.0.1:61513/ For a more detailed help message, press “h”. To detach, press “D “; to quit, press “q”.

You can edit the Dart code in my_flutter and then enter R at the terminal to use hot overloading. You can also type the URL in your browser to view breakpoints, analyze memory, and perform other debugging tasks.

Follow us at TryEnough for more tutorials in this series