The articles

Look for similar pictures in IOS album NSNotification and class object, instance object icloud-Documents storage CocoaPods private source build Swarm blockchain distributed storage using MacOS stream editor sed

Create the Flutter module

Template generation project

First, switch to a directory where the Flutter module is stored. Create a template project using the command flutter create –template module moduleName

cd some/path/
flutter create --template module moduleName
Copy the code

ModuleName is the name of our generated module.

This command has successfully created a my_flutter template project

Formwork construction

After the template project is created, let’s look at the structure of the template project.

As you can see, the Structure of the Flutter module directory is similar to that of the Project Directory. The functions of each directory are as follows

directory role
.android Store android Project directory (for running the shell project of the Flutter module alone)
.ios Store the ios project directory (for running the Shell project of the Flutter module alone)
lib Storing the source code of Flutter
pubspec.yaml Flutter profile

.ios directory and ios directory

As you can see from the template structure, there are no Android and ios directories. Just the.ios and.Android directories. So. what’s the difference between the ios directory and the ios directory?

. Ios directory structure

Ios directory structure

In Project Flutter, the ios directory is a repository of project Flutter products compiled for the ios platform and used to publish the Flutter app (on ios) or to use specific functions in XCode (e.g. enable push, icloud container, location, permissions, etc.)

In the Flutter module project, the.ios directory is a shell project for running your Flutter module alone. For example, if you don’t want to rebuild the entire original project, you can use this shell project to run and test your Flutter module.

In addition, the. Ios directory contains the App.framework, which contains the compilation of the Dart source code from our Flutter module

The FlutterPluginRegistrant directory is used to hold our flutter third party plug-ins

There is also the Engin directory for running our Flutter module in the ios environment.

And the Flutter module installation help script podhelper.rb

Note that the ios platform code is written in our ios native project, not in the shell project of the Flutter module in the ios directory. The flutter module shell project in the.ios directory may be overwritten by the Flutter and will not be integrated into your existing XCode project. Do not put ios code, do not put ios code, do not put ios code

Install the Flutter module into the Xcode project

CocoaPod installation

Start by creating a Podfile

cd path
vim Podfile
Copy the code

Configure the Podfile contents

flutter_application_path = '/xxxxxxx/my_flutter'
load File.join(flutter_application_path, '.ios'.'Flutter'.'podhelper.rb')

platform :ios.'9.1'
target 'myFlutterModule' do
pod 'Masonry'
install_all_flutter_pods(flutter_application_path)
end
Copy the code

Flutter_application_path indicates the directory path of your Flutter module project. After you have found the Flutter module project, go to the.ios directory and find the installation script podhelper.rb in the.ios directory under the Flutter directory

The script flow for installing the FLUTTER module is shown in the following figure

By looking at the PodHelper script code we can see that it mainly packages three directories in.ios

def install_flutter_engine_pod
  current_directory = File.expand_path('.. '.__FILE__)
  engine_dir = File.expand_path('engine', current_directory)
  framework_name = 'Flutter.xcframework'
  copied_engine = File.expand_path(framework_name, engine_dir)
  if! File.exist? (copied_engine)# Copy the debug engine to have something to link against if the xcode backend script has not run yet.
    # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
    release_framework_dir = File.join(flutter_root, 'bin'.'cache'.'artifacts'.'engine'.'ios-release')
    unlessDir.exist? (release_framework_dir)# iOS artifacts have not been downloaded.
      raise "#{release_framework_dir} must exist. Make sure \"flutter build ios\" has been run at least once"
    end
    FileUtils.cp_r(File.join(release_framework_dir, framework_name), engine_dir)
  end

  # Keep pod path relative so it can be checked into Podfile.lock.
  # Process will be run from project directory.
  engine_pathname = Pathname.new engine_dir
  # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
  project_directory_pathname = defined_in_file.dirname
  relative = engine_pathname.relative_path_from project_directory_pathname

  pod 'Flutter'.:path => relative.to_s, :inhibit_warnings= >true
end
Copy the code

This method is used to package the Flutter. Xcframework in the engine directory mentioned above, which is the engine on which our Flutter runs

def install_flutter_plugin_pods(flutter_application_path)
  flutter_application_path ||= File.join('.. '.'.. ')

  # Keep pod path relative so it can be checked into Podfile.lock.
  # Process will be run from project directory.
  ios_project_directory_pathname = Pathname.new File.expand_path(File.join('.. '.'.. '), __FILE__)
  # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
  project_directory_pathname = defined_in_file.dirname
  relative = ios_project_directory_pathname.relative_path_from project_directory_pathname
  pod 'FlutterPluginRegistrant'.:path => File.join(relative, 'Flutter'.'FlutterPluginRegistrant'), :inhibit_warnings= >true

  symlinks_dir = File.join(relative, '.symlinks'.'plugins')
  FileUtils.mkdir_p(symlinks_dir)

  plugins_file = File.expand_path('.flutter-plugins-dependencies', flutter_application_path)
  plugin_pods = flutter_parse_dependencies_file_for_ios_plugin(plugins_file)
  plugin_pods.each do |plugin_hash|
    plugin_name = plugin_hash['name']
    plugin_path = plugin_hash['path']
    if (plugin_name && plugin_path)
      symlink = File.join(symlinks_dir, plugin_name)
      FileUtils.rm_f(symlink)
      File.symlink(plugin_path, symlink)
      pod plugin_name, :path => File.join(symlink, 'ios'), :inhibit_warnings= >true
    end
  end
end
Copy the code

This method is used to package the FlutterPluginRegistrant directory, which holds the third-party plug-ins we use in the Flutter module

def install_flutter_application_pod(flutter_application_path)
  current_directory_pathname = Pathname.new File.expand_path('.. '.__FILE__)
  app_framework_dir = File.expand_path('App.framework', current_directory_pathname.to_path)
  app_framework_dylib = File.join(app_framework_dir, 'App')
  if! File.exist? (app_framework_dylib)# Fake an App.framework to have something to link against if the xcode backend script has not run yet.
    # CocoaPods will not embed the framework on pod install (before any build phases can run) if the dylib does not exist.
    # Create a dummy dylib.
    FileUtils.mkdir_p(app_framework_dir)
    `echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -`
  end

  # Keep pod and script phase paths relative so they can be checked into source control.
  # Process will be run from project directory.

  # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
  project_directory_pathname = defined_in_file.dirname
  relative = current_directory_pathname.relative_path_from project_directory_pathname
  pod 'my_flutter'.:path => relative.to_s, :inhibit_warnings= >true

  flutter_export_environment_path = File.join('${SRCROOT}', relative, 'flutter_export_environment.sh');
  script_phase :name= >'Run Flutter Build my_flutter Script'.:script= >"set -e\nset -u\nsource \"#{flutter_export_environment_path}\"\nexport VERBOSE_SCRIPT_LOGGING=1 && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build".:execution_position= >:before_compile
end
Copy the code

This method is used to package the results of the compilation of the Flutter module source code

Then run Pod Install to add the Flutter module to our project.

Note here that the target of a Pod corresponds to an install_ALL_Flutter_Pods. In this example we only import Install_all_Flutter_Pods into the myFlutterModule target. If there are other targets that need to use the Flutter module, You need to import install_ALL_Flutter_Pods under the target.

After Pod Intall completes, we see that three modules have been successfully installed

In the project generated by CocoaPod, we have access to each of the three modules to compile the product for the Flutter module source, the flutter running engine, and the plugin used by the flutter module

Open our project in Xcode and you can now use the ⌘B compilation project.

Pay attention to

When we changed the plugins that the Flutter module depended on in pubspec. Yaml, we needed to update the list of plugins that would be used by the Podhelper.rb script using the Flutter pub get in the Flutter module directory. Then use Pod Install again in our Xcode project

In addition, the changes in the Lib source file of the Flutter module are automatically updated through the script every time Xcode builds an IOS project. So we don’t need to re-install Pod using Pod Install, change the module style or functionality directly in the Flutter module, and then build the project directly in Xcode.

Invoke the Flutter page on IOS projects

AppDelegate set

Here recommend let our AppDelegate inherit FlutterAppDelegate directly, or you can choose not to inherit, if no inheritance, you need to achieve in the AppDelegate FlutterAppLifeCycleProvider agreement, To ensure that the Flutter plugins receive the necessary callbacks

For convenience, WHAT I’ve chosen here is to have the AppDelegate inherit from FlutterAppDelegate

AppDelegate. H file

@import UIKit;
@import Flutter;


@interface AppDelegate : FlutterAppDelegate 
@property (nonatomic.strong) FlutterEngine *flutterEngine;


@end
Copy the code

AppDelegate. M file

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.flutterEngine = [[FlutterEngine alloc] initWithName:@"coderjun flutter engine"];
     // Runs the default Dart entrypoint with a default Flutter route.
     [self.flutterEngine run];
     // Used to connect plugins (only if you have plugins with iOS platform code).
     [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];;
}
Copy the code

Now I have created our own flutter engine in the AppDelegate.

Now we can use the Flutter engine to run our Flutter module in the ios environment

Use FlutterEngin to display FlutterViewController

Here we simply create a ViewController and switch that ViewController to FlutterViewController

- (void)viewDidLoad {

    UIButton* btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
    btn.center = self.view.center;
    [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void) btnClick:(UIButton*) sender{
    NSLog(@"tag is %ld", (long)index);
    FlutterEngine *flutterEngine =
         ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
     FlutterViewController *flutterViewController =
         [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
     [self presentViewController:flutterViewController animated:YES completion:nil];
}

Copy the code

When we click the button, the ViewController pushes our FlutterViewControler over the current window using a modal view. Results the following

At the end

If there are any mistakes or questions, please leave them in the comments section and I will reply.

My GitHub home page GitHub

Will not regularly do some open source projects for everyone to exchange learning