[go: up one dir, main page]

Integrate a Flutter module into your iOS project

Flutter UI components can be incrementally added into your existing iOS application as embedded frameworks. To embed Flutter in your existing application, consider one of the following three methods.

Embedding MethodMethodologyBenefit
Use CocoaPods (Recommended)Install and use the Flutter SDK and CocoaPods. Flutter compiles the flutter_module from source each time Xcode builds the iOS app.Least complicated method to embed Flutter into your app.
Use iOS frameworksCreate iOS frameworks for Flutter components, embed them into your iOS, and update your existing app's build settings.Doesn't require every developer to install the Flutter SDK and CocoaPods on their local machines.
Use iOS frameworks and CocoaPodsEmbed the frameworks for your iOS app and the plugins in Xcode, but distribute the Flutter engine as a CocoaPods podspec.Provides an alternative to distributing the large Flutter engine (Flutter.xcframework) library.

When you add Flutter to your existing iOS app, it increases the size of your iOS app.

For examples using an app built with UIKit, see the iOS directories in the add_to_app code samples. For an example using SwiftUI, consult the iOS directory in News Feed App.

Development system requirements

#

Your development environment must meet the macOS system requirements for Flutter with Xcode installed. Flutter supports Xcode 15 or later and CocoaPods 1.16 or later.

Create a Flutter module

#

To embed Flutter into your existing application with any method, create a Flutter module first. Use the following command to create a Flutter module.

cd /path/to/my_flutter
flutter create --template module my_flutter

Flutter creates module project under /path/to/my_flutter/. If you use the CocoaPods method, save the module in the same parent directory as your existing iOS app.

From the Flutter module directory, you can run the same flutter commands you would in any other Flutter project, like flutter run or flutter build ios. You can also run the module in VS Code or Android Studio/IntelliJ with the Flutter and Dart plugins. This project contains a single-view example version of your module before embedding it in your existing iOS app. This helps when testing the Flutter-only parts of your code.

Organize your module

#

The my_flutter module directory structure resembles a typical Flutter app.

my_flutter/
├── .ios/
│   ├── Runner.xcworkspace
│   └── Flutter/podhelper.rb
├── lib/
│   └── main.dart
├── test/
└── pubspec.yaml

Your Dart code should be added to the lib/ directory. Your Flutter dependencies, packages, and plugins must be added to the pubspec.yaml file.

The .ios/ hidden subfolder contains an Xcode workspace where you can run a standalone version of your module. This wrapper project bootstraps your Flutter code. It contains helper scripts to facilitate building frameworks or embedding the module into your existing application with CocoaPods.

Embed a Flutter module in your iOS app

#

After you have developed your Flutter module, you can embed it using the methods described in the table at the top of the page.

You can run in Debug mode on a simulator or a real device, and Release mode on a real device.

Use CocoaPods and the Flutter SDK

#

Approach

#

This first method uses CocoaPods to embed the Flutter modules. CocoaPods manages dependencies for Swift projects, including Flutter code and plugins. Each time Xcode builds the app, CocoaPods embeds the Flutter modules.

This allows rapid iteration with the most up-to-date version of your Flutter module without running additional commands outside of Xcode.

To learn more about CocoaPods, consult the CocoaPods getting started guide.

Watch the video

#

If watching a video helps you learn, this video covers adding Flutter to an iOS app:


Step by step on how to add Flutter to an existing iOS app

Requirements

#

Every developer working on your project must have a local version of the Flutter SDK and CocoaPods installed.

Example project structure

#

This section assumes that your existing app and the Flutter module reside in sibling directories. If you have a different directory structure, adjust the relative paths. The example directory structure resembles the following:

/path/to/MyApp
├── my_flutter/
│   └── .ios/
│       └── Flutter/
│         └── podhelper.rb
└── MyApp/
    └── Podfile

Update your Podfile

#

Add your Flutter modules to your Podfile configuration file. This section presumes you called your Swift app MyApp.

  1. (Optional) If your existing app lacks a Podfile config file, navigate to the root of your app directory. Use the pod init command to create the Podfile file.

  2. Update your Podfile config file.

    1. Add the following lines after the platform declaration.

      MyApp/Podfile
      ruby
      flutter_application_path = '../my_flutter'
      load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    2. For each Podfile target that needs to embed Flutter, add a call to the install_all_flutter_pods(flutter_application_path) method. Add these calls after the settings in the previous step.

      MyApp/Podfile
      ruby
      target 'MyApp' do
        install_all_flutter_pods(flutter_application_path)
      end
    3. In the Podfile's post_install block, add a call to flutter_post_install(installer). This block should be the last block in the Podfile config file.

      MyApp/Podfile
      ruby
      post_install do |installer|
        flutter_post_install(installer) if defined?(flutter_post_install)
      end

To review an example Podfile, consult this Flutter Podfile sample.

Embed your frameworks

#

At build time, Xcode packages your Dart code, each Flutter plugin, and the Flutter engine into their own *.xcframework bundles. CocoaPod's podhelper.rb script then embeds these *.xcframework bundles into your project.

  • Flutter.xcframework contains the Flutter engine.
  • App.xcframework contains the compiled Dart code for this project.
  • <plugin>.xcframework contains one Flutter plugin.

To embed the Flutter engine, your Dart code, and your Flutter plugins into your iOS app, complete the following procedure.

  1. Refresh your Flutter plugins.

    If you change the Flutter dependencies in the pubspec.yaml file, run flutter pub get in your Flutter module directory. This refreshes the list of plugins that the podhelper.rb script reads.

    flutter pub get
  2. Embed the plugins and frameworks with CocoaPods.

    1. Navigate to your iOS app project at /path/to/MyApp/MyApp.

    2. Use the pod install command.

      pod install

    Your iOS app's Debug and Release build configurations embed the corresponding Flutter components for that build mode.

  3. Build the project.

    1. Open MyApp.xcworkspace in Xcode.

      Verify that you're opening MyApp.xcworkspace and not opening MyApp.xcodeproj. The .xcworkspace file has the CocoaPod dependencies, the .xcodeproj doesn't.

    2. Select Product > Build or press Cmd + B.

Link and Embed frameworks in Xcode

#

Approach

#

In this second method, edit your existing Xcode project, generate the necessary frameworks, and embed them in your app. Flutter generates iOS frameworks for Flutter itself, for your compiled Dart code, and for each of your Flutter plugins. Embed these frameworks and update your existing application's build settings.

Requirements

#

No additional software or hardware requirements are needed for this method. Use this method in the following use cases:

  • Members of your team can't install the Flutter SDK and CocoaPods
  • You don't want to use CocoaPods as a dependency manager in existing iOS apps

Limitations

#

Flutter can't handle common dependencies with xcframeworks. If both the host app and the Flutter module's plugin define the same pod dependency and you integrate Flutter module using this option, errors result. These errors include issues like Multiple commands produce 'CommonDependency.framework'.

To work around this issue, link every plugin source in its podspec file from the Flutter module to the host app's Podfile. Link the source instead of the plugins' xcframework framework. The next section explains how to produce that framework.

To prevent the error that occurs when common dependencies exist, use flutter build ios-framework with the --no-plugins flag.

Example project structure

#

The following example assumes that you want to generate the frameworks to /path/to/MyApp/Flutter/.

flutter build ios-framework --output=/path/to/MyApp/Flutter/

Run this every time you change code in your Flutter module.

The resulting project structure should resemble this directory tree.

/path/to/MyApp/
└── Flutter/
    ├── Debug/
    │   ├── Flutter.xcframework
    │   ├── App.xcframework
    │   ├── FlutterPluginRegistrant.xcframework (only if you have plugins with iOS platform code)
    │   └── example_plugin.xcframework (each plugin is a separate framework)
    ├── Profile/
    │   ├── Flutter.xcframework
    │   ├── App.xcframework
    │   ├── FlutterPluginRegistrant.xcframework
    │   └── example_plugin.xcframework
    └── Release/
        ├── Flutter.xcframework
        ├── App.xcframework
        ├── FlutterPluginRegistrant.xcframework
        └── example_plugin.xcframework

Procedures

#

How you link, embed, or both the generated frameworks into your existing app in Xcode depends on the type of framework.

Flutter plugins might produce static or dynamic frameworks. Link static frameworks, never embed them.

If you embed a static framework into your iOS app, you can't publish that app to the App Store. Publishing fails with a Found an unexpected Mach-O header code archive error.

#

To link the necessary frameworks, follow this procedure.

  1. Choose the frameworks to link.

    1. In the Project Navigator, click on your project.

    2. Click the Build Phases tab.

    3. Expand Link Binary With Libraries.

      Expand the Link Binary With Libraries build phase in Xcode
      Expand the Link Binary With Libraries build phase in Xcode

    4. Click + (plus sign).

    5. Click Add Other... then Add Files....

    6. From the Choose frameworks and libraries to add: dialog box, navigate to the /path/to/MyApp/Flutter/Release/ directory.

    7. Command-click the frameworks in that directory then click Open.

      Choose frameworks to link from the Choose frameworks and
libraries to add: dialog box in Xcode
      Choose frameworks to link from the Choose frameworks and libraries to add: dialog box in Xcode

  2. Update the paths to the libraries to account for build modes.

    1. Launch the Finder.

    2. Navigate to the /path/to/MyApp/ directory.

    3. Right-click on MyApp.xcodeproj and select Show Package Contents.

    4. Open project.pbxproj with Xcode. The file opens in Xcode's text editor. This also locks Project Navigator until you close the text editor.

      The  file open in the Xcode text editor
      The project-pbxproj file open in the Xcode text editor

    5. Find the lines that resemble the following text in the /* Begin PBXFileReference section */.

      text
      312885572C1A441C009F74FF /* Flutter.xcframework */ = {
        isa = PBXFileReference;
        expectedSignature = "AppleDeveloperProgram:S8QB4VV633:FLUTTER.IO LLC";
        lastKnownFileType = wrapper.xcframework;
        name = Flutter.xcframework;
        path = Flutter/Release/Flutter.xcframework;
        sourceTree = "<group>";
      };
      312885582C1A441C009F74FF /* App.xcframework */ = {
        isa = PBXFileReference;
        lastKnownFileType = wrapper.xcframework;
        name = App.xcframework;
        path = Flutter/Release/App.xcframework;
        sourceTree = "<group>";
      };
    6. Change the Release text highlighted in the prior step and change it to $(CONFIGURATION). Also wrap the path in quotation marks.

      text
      312885572C1A441C009F74FF /* Flutter.xcframework */ = {
        isa = PBXFileReference;
        expectedSignature = "AppleDeveloperProgram:S8QB4VV633:FLUTTER.IO LLC";
        lastKnownFileType = wrapper.xcframework;
        name = Flutter.xcframework;
        path = "Flutter/$(CONFIGURATION)/Flutter.xcframework";
        sourceTree = "<group>";
      };
      312885582C1A441C009F74FF /* App.xcframework */ = {
        isa = PBXFileReference;
        lastKnownFileType = wrapper.xcframework;
        name = App.xcframework;
        path = "Flutter/$(CONFIGURATION)/App.xcframework";
        sourceTree = "<group>";
      };
  3. Update the search paths.

    1. Click the Build Settings tab.

    2. Navigate to Search Paths

    3. Double-click to the right of Framework Search Paths.

    4. In the combo box, click + (plus sign).

    5. Type $(inherited). and press Enter.

    6. Click + (plus sign).

    7. Type $(PROJECT_DIR)/Flutter/$(CONFIGURATION)/ and press Enter.

      Update Framework Search Paths in Xcode
      Update Framework Search Paths in Xcode

After linking the frameworks, they should display in the Frameworks, Libraries, and Embedded Content section of your target's General settings.

Embed the dynamic frameworks
#

To embed your dynamic frameworks, complete the following procedure.

  1. Navigate to General > Frameworks, Libraries, and Embedded Content.

  2. Click on each of your dynamic frameworks and select Embed & Sign.

    Select Embed & Sign for each of your frameworks in Xcode
    Select Embed & Sign for each of your frameworks in Xcode

    Don't include any static frameworks, including FlutterPluginRegistrant.xcframework.

  3. Click the Build Phases tab.

  4. Expand Embed Frameworks. Your dynamic frameworks should display in that section.

    The expanded Embed Frameworks build phase in Xcode
    The expanded Embed Frameworks build phase in Xcode

  5. Build the project.

    1. Open MyApp.xcworkspace in Xcode.

      Verify that you're opening MyApp.xcworkspace and not opening MyApp.xcodeproj. The .xcworkspace file has the CocoaPod dependencies, the .xcodeproj doesn't.

    2. Select Product > Build or press Cmd + B.

Use frameworks in Xcode and Flutter framework as podspec

#

Approach

#

This method generates Flutter as a CocoaPods podspec instead of distributing the large Flutter.xcframework to other developers, machines, or continuous integration systems. Flutter still generates iOS frameworks for your compiled Dart code, and for each of your Flutter plugins. Embed these frameworks and update your existing application's build settings.

Requirements

#

No additional software or hardware requirements are needed for this method. Use this method in the following use cases:

  • Members of your team can't install the Flutter SDK and CocoaPods
  • You don't want to use CocoaPods as a dependency manager in existing iOS apps

Limitations

#

Flutter can't handle common dependencies with xcframeworks. If both the host app and the Flutter module's plugin define the same pod dependency and you integrate Flutter module using this option, errors result. These errors include issues like Multiple commands produce 'CommonDependency.framework'.

To work around this issue, link every plugin source in its podspec file from the Flutter module to the host app's Podfile. Link the source instead of the plugins' xcframework framework. The next section explains how to produce that framework.

To prevent the error that occurs when common dependencies exist, use flutter build ios-framework with the --no-plugins flag.

This method only works with the beta or stable release channels.

Example project structure

#

The following example assumes that you want to generate the frameworks to /path/to/MyApp/Flutter/.

flutter build ios-framework --output=/path/to/MyApp/Flutter/

Run this every time you change code in your Flutter module.

The resulting project structure should resemble this directory tree.

/path/to/MyApp/
└── Flutter/
    ├── Debug/
    │   ├── Flutter.xcframework
    │   ├── App.xcframework
    │   ├── FlutterPluginRegistrant.xcframework (only if you have plugins with iOS platform code)
    │   └── example_plugin.xcframework (each plugin is a separate framework)
    ├── Profile/
    │   ├── Flutter.xcframework
    │   ├── App.xcframework
    │   ├── FlutterPluginRegistrant.xcframework
    │   └── example_plugin.xcframework
    └── Release/
        ├── Flutter.xcframework
        ├── App.xcframework
        ├── FlutterPluginRegistrant.xcframework
        └── example_plugin.xcframework

Add Flutter engine to your Podfile

#

Host apps using CocoaPods can add the Flutter engine to their Podfile.

MyApp/Podfile
ruby
pod 'Flutter', :podspec => '/path/to/MyApp/Flutter/[build mode]/Flutter.podspec'
#

Flutter plugins might produce static or dynamic frameworks. Link static frameworks, never embed them.

If you embed a static framework into your iOS app, you can't publish that app to the App Store. Publishing fails with a Found an unexpected Mach-O header code archive error.

#

To link the necessary frameworks, follow this procedure.

  1. Choose the frameworks to link.

    1. In the Project Navigator, click on your project.

    2. Click the Build Phases tab.

    3. Expand Link Binary With Libraries.

      Expand the Link Binary With Libraries build phase in Xcode
      Expand the Link Binary With Libraries build phase in Xcode

    4. Click + (plus sign).

    5. Click Add Other... then Add Files....

    6. From the Choose frameworks and libraries to add: dialog box, navigate to the /path/to/MyApp/Flutter/Release/ directory.

    7. Command-click the frameworks in that directory then click Open.

      Choose frameworks to link from the Choose frameworks and
libraries to add: dialog box in Xcode
      Choose frameworks to link from the Choose frameworks and libraries to add: dialog box in Xcode

  2. Update the paths to the libraries to account for build modes.

    1. Launch the Finder.

    2. Navigate to the /path/to/MyApp/ directory.

    3. Right-click on MyApp.xcodeproj and select Show Package Contents.

    4. Open project.pbxproj with Xcode. The file opens in Xcode's text editor. This also locks Project Navigator until you close the text editor.

      The  file open in the Xcode text editor
      The project-pbxproj file open in the Xcode text editor

    5. Find the lines that resemble the following text in the /* Begin PBXFileReference section */.

      text
      312885572C1A441C009F74FF /* Flutter.xcframework */ = {
        isa = PBXFileReference;
        expectedSignature = "AppleDeveloperProgram:S8QB4VV633:FLUTTER.IO LLC";
        lastKnownFileType = wrapper.xcframework;
        name = Flutter.xcframework;
        path = Flutter/Release/Flutter.xcframework;
        sourceTree = "<group>";
      };
      312885582C1A441C009F74FF /* App.xcframework */ = {
        isa = PBXFileReference;
        lastKnownFileType = wrapper.xcframework;
        name = App.xcframework;
        path = Flutter/Release/App.xcframework;
        sourceTree = "<group>";
      };
    6. Change the Release text highlighted in the prior step and change it to $(CONFIGURATION). Also wrap the path in quotation marks.

      text
      312885572C1A441C009F74FF /* Flutter.xcframework */ = {
        isa = PBXFileReference;
        expectedSignature = "AppleDeveloperProgram:S8QB4VV633:FLUTTER.IO LLC";
        lastKnownFileType = wrapper.xcframework;
        name = Flutter.xcframework;
        path = "Flutter/$(CONFIGURATION)/Flutter.xcframework";
        sourceTree = "<group>";
      };
      312885582C1A441C009F74FF /* App.xcframework */ = {
        isa = PBXFileReference;
        lastKnownFileType = wrapper.xcframework;
        name = App.xcframework;
        path = "Flutter/$(CONFIGURATION)/App.xcframework";
        sourceTree = "<group>";
      };
  3. Update the search paths.

    1. Click the Build Settings tab.

    2. Navigate to Search Paths

    3. Double-click to the right of Framework Search Paths.

    4. In the combo box, click + (plus sign).

    5. Type $(inherited). and press Enter.

    6. Click + (plus sign).

    7. Type $(PROJECT_DIR)/Flutter/$(CONFIGURATION)/ and press Enter.

      Update Framework Search Paths in Xcode
      Update Framework Search Paths in Xcode

After linking the frameworks, they should display in the Frameworks, Libraries, and Embedded Content section of your target's General settings.

Embed the dynamic frameworks
#

To embed your dynamic frameworks, complete the following procedure.

  1. Navigate to General > Frameworks, Libraries, and Embedded Content.

  2. Click on each of your dynamic frameworks and select Embed & Sign.

    Select Embed & Sign for each of your frameworks in Xcode
    Select Embed & Sign for each of your frameworks in Xcode

    Don't include any static frameworks, including FlutterPluginRegistrant.xcframework.

  3. Click the Build Phases tab.

  4. Expand Embed Frameworks. Your dynamic frameworks should display in that section.

    The expanded Embed Frameworks build phase in Xcode
    The expanded Embed Frameworks build phase in Xcode

  5. Build the project.

    1. Open MyApp.xcworkspace in Xcode.

      Verify that you're opening MyApp.xcworkspace and not opening MyApp.xcodeproj. The .xcworkspace file has the CocoaPod dependencies, the .xcodeproj doesn't.

    2. Select Product > Build or press Cmd + B.

Set local network privacy permissions

#

On iOS 14 and later, enable the Dart multicast DNS service in the Debug version of your iOS app. This adds debugging functionalities such as hot-reload and DevTools using flutter attach.

To set local network privacy permissions only in the Debug version of your app, create a separate Info.plist per build configuration. SwiftUI projects start without an Info.plist file. If you need to create a property list, you can do so through Xcode or text editor. The following instructions assume the default Debug and Release. Adjust the names as needed depending on your app's build configurations.

  1. Create a new property list.

    1. Open your project in Xcode.

    2. In the Project Navigator, click on the project name.

    3. From the Targets list in the Editor pane, click on your app.

    4. Click the Info tab.

    5. Expand Custom iOS Target Properties.

    6. Right-click on the list and select Add Row.

    7. From the dropdown menu, select Bonjour Services. This creates a new property list in the project directory called Info. This displays as Info.plist in the Finder.

  2. Rename the Info.plist to Info-Debug.plist

    1. Click on Info file in the project list at the left.

    2. In the Identity and Type panel at the right, change the Name from Info.plist to Info-Debug.plist.

  3. Create a Release property list.

    1. In the Project Navigator, click on Info-Debug.plist.

    2. Select File > Duplicate....
      You can also press Cmd + Shift + S.

    3. In the dialog box, set the Save As: field to Info-Release.plist and click Save.

  4. Add the necessary properties to the Debug property list.

    1. In the Project Navigator, click on Info-Debug.plist.

    2. Add the String value _dartVmService._tcp to the Bonjour Services array.

    3. (Optional) To set your desired customized permission dialog text, add the key Privacy - Local Network Usage Description.

      The  property list with the Bonjour Services
and Privacy - Local Network Usage Description keys added
      The Info-Debug property list with the Bonjour Services and Privacy - Local Network Usage Description keys added

  5. Set the target to use different property lists for different build modes.

    1. In the Project Navigator, click on your project.

    2. Click the Build Settings tab.

    3. Click All and Combined sub-tabs.

    4. In the Search box, type plist.
      This limits the settings to those that include property lists.

    5. Scroll through the list until you see Packaging.

    6. Click on the Info.plist File setting.

    7. Change the Info.plist File value from path/to/Info.plist to path/to/Info-$(CONFIGURATION).plist.Updating the  build setting to use build
mode-specific property lists
      Updating the Info.plist build setting to use build mode-specific property lists

      This resolves to the path Info-Debug.plist in Debug and Info-Release.plist in Release.

      The updated Info.plist File build setting displaying the
configuration variations
      The updated Info.plist File build setting displaying the configuration variations

  6. Remove the Release property list from the Build Phases.

    1. In the Project Navigator, click on your project.

    2. Click the Build Phases tab.

    3. Expand Copy Bundle Resources.

    4. If this list includes Info-Release.plist, click on it and then click the - (minus sign) under it to remove the property list from the resources list.

      The Copy Bundle build phase displaying the
Info-Release.plist setting. Remove this setting.
      The Copy Bundle build phase displaying the Info-Release.plist setting. Remove this setting.

  7. The first Flutter screen your Debug app loads prompts for local network permission.

    Click OK.

    (Optional) To grant permission before the app loads, enable Settings > Privacy > Local Network > Your App.

Mitigate known issue with Apple Silicon Macs

#

On Macs running Apple Silicon, the host app builds for an arm64 simulator. While Flutter supports arm64 simulators, some plugins might not. If you use one of these plugins, you might see a compilation error like Undefined symbols for architecture arm64. If this occurs, exclude arm64 from the simulator architectures in your host app.

  1. In the Project Navigator, click on your project.

  2. Click the Build Settings tab.

  3. Click All and Combined sub-tabs.

  4. Under Architectures, click on Excluded Architectures.

  5. Expand to see the available build configurations.

  6. Click Debug.

  7. Click the + (plus sign).

  8. Select iOS Simulator.

  9. Double-click in the value column for Any iOS Simulator SDK.

  10. Click the + (plus sign).

  11. Type arm64 in the Debug > Any iOS Simulator SDK dialog box.

    Add  as an excluded architecture for your app
    Add arm64 as an excluded architecture for your app

  12. Press Esc to close this dialog box.

  13. Repeat these steps for the Release build mode.

  14. Repeat for any iOS unit test targets.

Next steps

#

You can now add a Flutter screen to your existing iOS app.