Recent Posts

Categories

Author: Orenji

Intro

Making it easy to add Flutter to an existing app is work in progress, tracked by the Add-to-App project.

This page documents the current state of that work and will be updated as we build out the necessary tooling.

Last updated November 26, 2018.

The “add-to-app” support is in preview, and is so far only available on the master channel.

Disclaimer

Since Flutter’s “Add-to-App” functionality is in preview, the associated APIs and tooling are not stable and are subject to change.

The Flutter module project template

Flutter projects created using flutter create xxx include very simple host apps for your Flutter/Dart code (a single-Activity Android host and a single-ViewController iOS host). You can modify these host apps to suit your needs and build from there.

But if you’re starting off with an existing host app for either platform, you’ll likely want to include your Flutter project in that app as some form of library instead.

This is what the Flutter module template provides. Executing flutter create -t module xxxproduces a Flutter project containing an Android library and a Cocoapods pod designed for consumption by your existing host app.

Android

Create a Flutter module

Let’s assume you have an existing Android app at some/path/MyApp, and that you want your Flutter project as a sibling:

$ cd some/path/
$ flutter create -t module my_flutter

This creates a some/path/my_flutter/ Flutter module project with some Dart code to get you started and a .android/ hidden subfolder that wraps up the module project in an Android library.

(While not required in what follows, if you so desire, you can build that library using Gradle:

$ cd .android/
$ ./gradlew flutter:assembleDebug

This results in a flutter-debug.aar archive file in .android/Flutter/build/outputs/aar/.)

The binding and script evaluation allows the Flutter module to include itself (as :flutter) and any Flutter plugins used by the module (as :package_info, :video_player, etc) in the evaluation context of your settings.gradle.

Introduce an implementation dependency on the Flutter module from your app:

Above we use the string "route1" to tell the Dart code which widget to display in the Flutter view. The lib/main.dart file of the Flutter module project template should switch on (or otherwise interpret) the provided route string, available as window.defaultRouteName, to determine which widget to create and pass to runApp. Schematically,

It is entirely up to you which route strings you want and how to interpret them.

Building and running your app

You build and run MyApp in exactly the same way that you did before you added the Flutter module dependency, typically using Android Studio. The same goes for editing, debugging, and profiling your Android code.

Hot restart/reload and debugging Dart code

Full IDE integration to support working with the Flutter/Dart code of your hybrid app is work in progress. But the fundamentals are already present via the Flutter command line tools and the Dart Observatory web user interface.

Connect a device or launch an emulator. Then make Flutter CLI tooling listen for your app to come up:

Launch MyApp in debug mode from Android Studio (or whichever way you usually do it). Navigate to an area of the app that uses Flutter. Then turn back to the terminal, and you should see output similar to the following:

You can now edit the Dart code in my_flutter, and the changes can be hot reloaded by pressing r in the terminal. You can also paste the URL above into your browser to use the Dart Observatory for setting breakpoints, analyzing memory retention and other debugging tasks.

iOS

Create a Flutter module

Let’s assume you have an existing iOS app at some/path/MyApp, and that you want your Flutter project as a sibling:

$ cd some/path/
$ flutter create -t module my_flutter

This creates a some/path/my_flutter/ Flutter module project with some Dart code to get you started and a .ios/ hidden subfolder that wraps up the module project that contains some Cocoapods and a helper Ruby script.

Make the host app depend on the Flutter module

The description below assumes that your existing iOS app has a structure similar to what you get by asking Xcode version 10.0 to generate a new “Single View App” project using Objective-C. If your existing app has a different folder structure and/or existing .xcconfig files, you can reuse those, but probably need to adjust some of the relative paths mentioned below accordingly.

Add your Flutter app to your Podfile

Integrating the Flutter framework requires use of the CocoaPods dependency manager. This is because the Flutter framework needs to be available also to any Flutter plugins that you might include in my_flutter.

Please refer to cocoapods.org for how to install CocoaPods on your development machine, if needed.

If your host application (MyApp) is already using Cocoapods, you only have to do the following to integrate with your my_flutter app:

Whenever you change the Flutter plugin dependencies in some/path/my_flutter/pubspec.yaml, you need to run flutter packages get from some/path/my_flutter to refresh the list of plugins read by the podhelper.rb script. Then run pod install again from some/path/MyApp.

The podhelper.rb script will ensure that your plugins and the Flutter.framework get added to your project, and also ensure that bitcode is disabled for all targets.

Add a build phase for building the Dart code

Select the top-level MyApp project in the Project navigator. Select TARGETMyApp in the left part of the main view, and then select the Build Phases tab. Add a new build phase by clicking the + towards the top left of the main view. Select New Run Script Phase. Expand the new Run Script, just appended to the list of phases.

Finally, drag the new build phase to just after the Target Dependencies phase.

You should now be able to build the project using ⌘B.

Under the hood

If you have some reason to do this manually or debug why these steps aren’t working, here’s what’s going on under the hood:

Flutter.framework (the Engine library) is getting embedded into your app for you. This has to match up with the release type (debug/profile/release) as well as the architecture for your app (arm*, i386, x86_64, etc.). Cocoapods pulls this in as a vendored framework and makes sure it gets embedded into your native app.

flutter_assets folder is getting embedded as a resource – it contains fonts, images, and in certain build modes it also contains binary files required by the engine at runtime. Problems with this folder can lead to runtime errors such as “Could not run engine for configuration” – usually indicating that either the folder is not getting embedded, or you’re trying to cross a JIT application with an AOT enabled engine, or vice versa! The flutter_assets folder gets embedded into App.framework as of flutter/flutter#26630, and this step is no longer necessary.

Any plugins are getting added as Cocoapods. In theory, it should be possible to manually merge those in as well, but that becomes much more specific to the plugin itself.

Bitcode is disabled for every target in your project. This is a requirement to link with the Flutter Engine.

Generated.xcconfig (containing Flutter-specific environment varaibles) is included in the release and debug .xcconfig files that Cocoapods generates.

The build phase script (xcode_backend.sh) is ensuring that the binaries you build stay up to date with the Dart code that’s actually in the folder. It also attempt to respect your build configuration setting once this pull request lands.

Write code to use FlutterViewController from your host app

The proper place to do this will be specific to your host app. Here is an example that makes sense for the blank screen of the host app generated by Xcode 10.0.

First declare your app delegate to be a subclass of FlutterAppDelegate. Then define a FlutterEngine property, which help you to register a plugin without a FlutterViewController instance.

#import<FlutterPluginRegistrant/GeneratedPluginRegistrant.h>// Only if you have Flutter Plugins
#include"AppDelegate.h"@implementationAppDelegate// This override can be omitted if you do not have any Flutter Plugins.
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter"project:nil];
[self.flutterEngine runWithEntrypoint:nil];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [superapplication:application didFinishLaunchingWithOptions:launchOptions];
}
@end

If you are writing in Swift, you can do the following in your AppDelegate.swift:

You should now be able to build and launch MyApp on the Simulator or on a device. Pressing the button should bring up a full-screen Flutter view with the standard Flutter Demo counting app. You can use routes to show different widgets at different places in your app, as described in the Android section above. To set the route, call

Objective-C:

[flutterViewController setInitialRoute:@"route1"];

Swift:

flutterViewController.setInitialRoute("route1")

immediately after construction of the FlutterViewController (and before presenting it).

You can have the Flutter app dismiss itself by calling SystemNavigator.pop() in the Dart code.

Building and running your app

You build and run MyApp using Xcode in exactly the same way that you did before you added the Flutter module dependency. The same goes for editing, debugging, and profiling your iOS code.

Hot restart/reload and debugging Dart code

Connect a device or launch a Simulator. Then make Flutter CLI tooling listen for your app to come up:

You can now edit the Dart code in my_flutter, and the changes can be hot reloaded by pressing r in the terminal. You can also paste the URL above into your browser to use the Dart Observatory for setting breakpoints, analyzing memory retention and other debugging tasks.

Debugging specific instances of Flutter

It’s possible to add multiple instances of Flutter (root isolates) to an app. flutter attachconnects to all of the available isolates by default. Any commands sent from the attached CLI are then forwarded to each of the attached isolates.

List all attached isolates by typing l from an attached flutter CLI tool. If unspecified, isolate names are automatically generated from the dart entry point file and function name.

OrenjiApp built the apps as an Ad Supported apps. This SERVICE is provided by
OrenjiApp at no cost and is intended for use as is.

This page is used to inform visitors regarding my policies with the collection, use, and disclosure
of Personal Information if anyone decided to use my Service.

If you choose to use my Service, then you agree to the collection and use of information in
relation to this policy. The Personal Information that I collect is used for providing and improving
the Service. I will not use or share your information with anyone except as described
in this Privacy Policy.

The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is
accessible at ACG Gallery unless otherwise defined in this Privacy Policy.

Information Collection and Use

For a better experience, while using our Service, I may require you to provide us with certain
personally identifiable information. The information that I request will be retained on your device and is not collected by me in any way.

The app does use third party services that may collect information used to identify you.

Link to privacy policy of third party service providers used by the app

I want to inform you that whenever you use my Service, in a case of
an error in the app I collect data and information (through third party products) on your phone
called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address,
device name, operating system version, the configuration of the app when utilizing my Service,
the time and date of your use of the Service, and other statistics.

Cookies

Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers.
These are sent to your browser from the websites that you visit and are stored on your device’s internal
memory.

This Service does not use these “cookies” explicitly. However, the app may use third party code and
libraries that use “cookies” to collect information and improve their services. You have the option to
either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose
to refuse our cookies, you may not be able to use some portions of this Service.

Service Providers

I may employ third-party companies and individuals due to the following reasons:

To facilitate our Service;

To provide the Service on our behalf;

To perform Service-related services; or

To assist us in analyzing how our Service is used.

I want to inform users of this Service that these third parties have access to
your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However,
they are obligated not to disclose or use the information for any other purpose.

Security

I value your trust in providing us your Personal Information, thus we are striving
to use commercially acceptable means of protecting it. But remember that no method of transmission over
the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee
its absolute security.

Links to Other Sites

This Service may contain links to other sites. If you click on a third-party link, you will be directed
to that site. Note that these external sites are not operated by me. Therefore, I strongly
advise you to review the Privacy Policy of these websites. I have no control over
and assume no responsibility for the content, privacy policies, or practices of any third-party sites
or services.

Children’s Privacy

These Services do not address anyone under the age of 13. I do not knowingly collect
personally identifiable information from children under 13. In the case I discover that a child
under 13 has provided me with personal information, I immediately delete this from
our servers. If you are a parent or guardian and you are aware that your child has provided us with personal
information, please contact me so that I will be able to do necessary actions.

Changes to This Privacy Policy

I may update our Privacy Policy from time to time. Thus, you are advised to review
this page periodically for any changes. I will notify you of any changes by posting
the new Privacy Policy on this page. These changes are effective immediately after they are posted on
this page.

Contact Us

If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact
me.

libstdc++ was deprecated 5 years ago. Apple’s more recent platforms (tvOS and watchOS) don’t support it.

Support was removed from the iOS 12.0 Simulator runtime, but it remains in the iOS 12.0 (device) runtime for binary compatibility with shipping apps.

You should update your project to use libc++ rather than libstdc++ by setting the CLANG_CXX_LIBRARY build setting (“C++ Standard Library”) to libc++.

If you have any static libraries that depend on libstdc++.tbd, you can workaround it for now by copying the file from the SDKs in Xcode 9.4 (and libstdc++.*.dylib in the iOS simulator runtime), but that is not a long term solution. You should contact the provider of those libraries and request versions built using libc++.