The Release Preview 1 version of Flutter has attracted a lot of attention. It’s easy to write an App from scratch with Flutter, but more and more people are finding that Flutter doesn’t seem to be friendly enough to support existing apps. So this article takes you through how to get existing apps to support Flutter.
The environment
Build a Flutter: 0.5.1 Xcode 9.4.1 Flutter project: Flutter /examples/hello_world
Debug: Flutter Hot Restart
We all know that Hot Restart for Flutter is very helpful for RAPID prototyping of UI in a development environment. It is not difficult to get existing apps to support Flutter and enable Hot Restart.
Flutter based library (engine)
First, drag the Flutter. Framework into your project. This library is the Flutter Engine, which hosts the Dart runtime and drawing Engine. There is a one-to-one correspondence between the Flutter. Framework and the command line tool versions. If you do not know where to find the file, you can directly perform a Flutter run in the Flutter source project. You can then find Flutter under /
Flutter ViewController
Then we need to introduce the underlying code of the Flutter into the existing project. Only with the underlying Flutter ViewController can we display the Flutter view. This step is as simple as pushing in your existing ViewController:
|
Note, however, that you need to pass life cycle events from the AppDelegate to Flutter
After the Release Preview 1 document have FlutterAppLifeCycleProvider mentioned in this agreement, but 0.5.1: not yet, so this approach first
-
The existing AppDelegate can inherit from the FlutterAppDelegate directly, but this has the negative effect that the root ViewController is set to the Flutter ViewController
-
Modify the AppDelegate yourself. Fortunately, Flutter APPDelegate is not too difficult to adapt and can also support module Delegate. Start by having your Delegate follow the FlutterPluginRegistry protocol
|
/ / the Registrar declaration and implementation @ interface DemoFlutterAppDelegateRegistrar: NSObject<FlutterPluginRegistrar> @property(nonatomic, copy) NSString *pluginKey; @property(nonatomic, strong) DemoFlutterBaseAppDelegate *appDelegate; - (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(DemoFlutterBaseAppDelegate*)delegate; @end @implementation DemoFlutterAppDelegateRegistrar - (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(DemoFlutterBaseAppDelegate*)appDelegate { self = [super init]; NSAssert(self, @"Super init cannot be nil "); _pluginKey = [pluginKey copy]; _appDelegate = appDelegate; return self; } - (NSObject<FlutterBinaryMessenger>*)messenger { return [_appDelegate binaryMessenger]; } - (NSObject<FlutterTextureRegistry>*)textures { return [_appDelegate textures]; } - (void)publish:(NSObject*)value { _appDelegate.pluginPublications[_pluginKey] = value; } - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate channel:(FlutterMethodChannel*)channel { [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [delegate handleMethodCall:call result:result]; }]; } - (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate { [_appDelegate.pluginDelegates addObject:delegate]; } - (NSString*)lookupKeyForAsset:(NSString*)asset { return [FlutterDartProject lookupKeyForAsset:asset]; } - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; } @ the end / / AppDelegate @ interface DemoFlutterBaseAppDelegate () @ property (nonatomic, strong) DemoFlutterBaseViewController *rootController; @property(readonly, nonatomic) NSMutableArray* pluginDelegates; @property(readonly, nonatomic) NSMutableDictionary* pluginPublications; @end @implementation DemoFlutterBaseAppDelegate /* * ... Here write forward various statement cycle events to the plugin * / - (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions { for (id<FlutterPlugin> plugin in _pluginDelegates) { if ([plugin respondsToSelector:_cmd]) { [plugin application:application didFinishLaunchingWithOptions:launchOptions]; } } return YES; } #pragma mark-getters for flutter - (FlutterViewController *)rootController {//... } - (NSObject<FlutterBinaryMessenger> *)binaryMessenger { if ([self.rootController conformsToProtocol:@protocol(FlutterBinaryMessenger)]) { return (NSObject<FlutterBinaryMessenger> *)self.rootController; } return nil; } - (NSObject<FlutterTextureRegistry> *)textures { if ([self.rootController conformsToProtocol:@protocol(FlutterTextureRegistry)]) { return (NSObject<FlutterTextureRegistry> *)self.rootController; } return nil; } - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey { NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@ ", pluginKey); self.pluginPublications[pluginKey] = [NSNull null]; return [[DemoFlutterAppDelegateRegistrar alloc] initWithPlugin:pluginKey appDelegate:self]; } - (BOOL)hasPlugin:(NSString*)pluginKey { return _pluginPublications[pluginKey] ! = nil; } - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { return _pluginPublications[pluginKey]; } @endCopy the code
This way the Flutter environment is ready to run, both Hot Restart and AOT can support it. The Debug Hot Restart will first execute the build bundle of a Flutter Asset in your Flutter code directory. This will help us package a Flutter Asset. Then drag the flutter_assets directory into the project.
Build your project and make sure you get an.app file. Create a new folder called Payload, place the.app file in the Payload folder, and compress it into a ZIP file. This file can then be used by the Flutter command line tool.
|
Then the effect is shown as follows:
There is no Hot Reload implementation. The current support for the Flutter tool chain is not good, but the Hot Restart can be used.
Release: Flutter AOT
Release mode is different from Debug, we need to do a few things:
- the
Flutter.framework
replaceflutter/bin/cache/artifacts/engine/ios-release/Flutter.framework
Because the library we used in the previous step was actually the JIT Runtime - Execute under the Flutter code project
flutter build aot --release --target-platform ios --ios-arch armv7,arm64
Then we can get a packaged one in the build directoryApp.framework
But don’t forget to put an info.plist in it. And drag the library into the project - Delete from project
flutter_assets
Under folderisolate_snapshot_data
,kernel_blob.bin
,platform.dill
,vm_snapshot_data
These files - Compile and package to the real machine to run, the effect is as follows:
The End
In fact, Flutter has an official plan to support the integration of existing apps, and part of the documentation is also introduced. However, the overall tool chain is not supported yet, and the degree of support is similar to the above method. If necessary, existing projects can use the above method of integration, in order to reduce workflow, some work needs to be done, such as:
- The project provides empty shells of App. Framework and Flutter. Framework to be easily replaced with scripts at any time under debug and release
- You can also add a build Post action that calls the Flutter command line directly to integrate Xcode with the Flutter. Do not manually Debug all the Flutter processes as described above
- Automation of Release AOT will definitely need to be done and integrated with existing CI’s