跳转至正文

添加 iOS App Extension

了解如何向 Flutter 应用添加 App Extension

本指南介绍如何在你的 Flutter 应用中使用 iOS App Extension。

概述

#

iOS app extensions 让你能在 iOS 应用之外扩展功能。你的应用可以显示为主屏幕 widget,也可以让应用的部分功能在其他应用中可用。

在下面的示例中,当用户在 iOS「照片」应用中选择要分享的照片时,名为 Example App With Extension 的 Flutter 应用会出现在照片应用的分享表单中:

Share sheet with a Flutter app in it.

向 Flutter 应用添加 iOS App Extension

#

若要将 Flutter 应用与 iOS 操作系统集成,可以向 Flutter 项目添加 iOS App Extension。为便于操作,以下步骤演示如何向名为 example_app_with_extension 的新 Flutter 应用添加 Share App Extension;你也可以从现有项目开始。

  1. In the console, create a new Flutter project called example_app_with_extension.

    1. 在控制台中,创建一个名为 example_app_with_extension 的新 Flutter 项目。
    flutter create example_app_with_extension
    
  2. In the console, open the Xcode workspace for the example_app_with_extension project.

    1. 在控制台中,打开 example_app_with_extension 项目的 Xcode 工作区。
    cd example_app_with_extension && open ios/Runner.xcworkspace
    
  3. In Xcode, add an app extension called Share and call it ShareExtension.

    1. 在 Xcode 中,添加名为 Share 的 App Extension,并将其命名为 ShareExtension
    • In the Xcode menu bar, select File > New > Target.

    • 在 Xcode 菜单栏中,选择 File > New > Target

    • Add Share Extension.

    • 添加 Share Extension

    • In the Name field, enter ShareExtension.

    • Name 字段中输入 ShareExtension

    • Click Finish.

    • 点击 Finish

    • In the Activate … Scheme dialog box that appears, select Activate.

    • 在出现的 Activate … Scheme 对话框中,选择 Activate

  4. In Xcode, change the order of the build process.

    1. 在 Xcode 中,调整构建过程的顺序。
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • In the project navigator, at the top, select Runner.

    • 项目导航器 顶部,选择 Runner

    • In the main window under TARGETS, select Runner.

    • 在主窗口的 TARGETS 下,选择 Runner

    • Open the Build Phases tab.

    • 打开 Build Phases 标签页。

    • Drag Embed Foundation Extensions above Run Script.

    • Embed Foundation Extensions 拖到 Run Script 上方。

  5. Make sure your Minimum Deployments iOS value is properly set and matches in both Runner and ShareExtension

    1. 确保 RunnerShareExtensionMinimum Deployments iOS 值已正确设置且一致
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • In the project navigator, at the top, select Runner.

    • 项目导航器 顶部,选择 Runner

    • In the main window under TARGETS, select Runner.

    • 在主窗口的 TARGETS 下,选择 Runner

    • On the General tab check your Minimum Deployments dropdown value to match the one you have on ShareExtension > General tab.

    • General 标签页中,检查 Minimum Deployments 下拉值是否与 ShareExtension > General 标签页中的值一致。

  6. In the console, run the following command to rebuild your iOS app:

    1. 在控制台中,运行以下命令以重新构建 iOS 应用:
    flutter build ios --config-only
    
  7. Test your app with the simulator.

    1. 使用模拟器测试应用

添加新的 App Extension 时,Xcode 会根据所选模板生成示例代码。有关生成代码与 WidgetKit 的更多信息,请参阅 Apple 的 App Extension 文档

测试 iOS App Extension

#

向 Flutter 项目添加 App Extension 后,可使用模拟器或真机进行测试。若在 debug 模式下测试扩展,必须使用 iOS 模拟器。

以下步骤假定你使用的是 添加 iOS App Extension 中的示例应用与 Share 扩展。

  1. In Xcode, add an app extension to your project.

    1. 在 Xcode 中,向项目添加 App Extension
  2. In the console, use the following command to run your iOS app:

    1. 在控制台中,使用以下命令运行 iOS 应用:
    flutter run
    
  3. In the simulator, test your app extension.

    1. 在模拟器中测试 App Extension。
    • Launch an app that supports the Share extension, such as the Photos app.

    • 启动支持 Share 扩展的应用(例如「照片」应用)。

    • Select a photo, tap the share button, then tap on the share extension icon of your app.

    • 选择一张照片,点按分享按钮,再点按你应用的分享扩展图标。

  1. Add an app extension to your project.

    1. 向项目添加 App Extension。
  2. In the console, run your Flutter app in release mode:

    1. 在控制台中,以 release 模式运行 Flutter 应用:
    flutter run --release
    
  3. On your device, test your app extension.

    1. 在设备上测试 App Extension。
    • Launch an app that supports the Share extension, such as the Photos app.

    • 启动支持 Share 扩展的应用(例如「照片」应用)。

    • Select a photo, tap the share button, then tap on the share extension icon of your app.

    • 选择一张照片,点按分享按钮,再点按你应用的分享扩展图标。

与 iOS App Extension 交互的其他方式

#

Flutter 应用与 iOS App Extension 的交互方式与 UIKit 或 SwiftUI 应用相同。宿主应用与 App Extension 不直接通信;用户在设备上与扩展交互时,宿主应用可能并未运行。应用与扩展可通过读写共享资源或使用更高级 API 相互通信。

使用更高级的 API

#

部分扩展提供 API。例如,Core Spotlight 框架会为你的应用建立索引,使用户可从 Spotlight 和 Safari 搜索;WidgetKit 框架可触发主屏幕 widget 的更新。

为简化应用与扩展的通信,Flutter 插件封装了这些 API。要查找封装扩展 API 的插件,请参阅 利用 Apple 系统 API 与框架,或在 pub.dev 上搜索。

共享资源

#

要在 Flutter 应用与 App Extension 之间共享资源,将 Runner 应用 target 与扩展 target 置于同一 App Group 中。

要将 target 添加到 App Group:

  1. 在 Xcode 中打开 target 设置。

  2. 进入 Signing & Capabilities 标签页。

  3. 选择 + Capability,然后选择 App Groups

  4. Choose which App Group you want to add the target from one of two options:

    1. 从以下两种方式之一选择要添加 target 的 App Group:

    1. 从列表中选择一个 App Group。

    2. 点击 + 添加新的 App Group。

Selecting an App Group within an Xcode Runner target configuration.

当两个 target 属于同一 App Group 时,它们可读写同一数据源。请为数据选择以下数据源之一:

安排后台更新

#

后台任务让你能在应用处于任何状态时通过代码更新扩展。

要从 Flutter 应用安排后台工作,请使用 workmanager 插件。

添加深层链接

#

你可能希望将用户从 App Extension 引导至 Flutter 应用中的特定页面。要在应用中打开特定路由,可使用 深层链接

添加可滚动列表

#

默认情况下,Share 扩展中的 Flutter 视图不处理滚动手势。若要在 Share 扩展中支持可滚动列表,请按 GitHub 上的说明 操作。

在 iOS App Extension 中打开 Flutter 应用

#

你可以通过 FlutterViewController 在某些 iOS App Extension(例如 Share 扩展)中直接打开 Flutter 应用。

在下面的示例中,名为 Example App With Extension 的 Flutter 应用在 Share 扩展中打开,用户可在应用间分享内容:

An example of an entry added to the share menu by a Flutter app

按以下步骤在 Share App Extension 中显示 Flutter 应用。本示例中,App Extension 的 scheme 为 ShareExtension,Flutter 应用的 scheme 为 Runner,应用名为 Example App With Extension

  1. Add an extension to your Flutter app if you haven't already done so.

    1. 若尚未添加,请先 向 Flutter 应用添加扩展
  2. In the console, navigate to your Flutter project directory and then open your project in Xcode with the following command:

    1. 在控制台中,进入 Flutter 项目目录,然后用以下命令在 Xcode 中打开项目:
    open ios/Runner.xcworkspace
    
  3. In Xcode, disable user script sandboxing.

    1. 在 Xcode 中,禁用用户脚本沙盒(User Script Sandboxing)。
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • In the main window under TARGETS, select ShareExtension.

    • 在主窗口的 TARGETS 下,选择 ShareExtension

    • Open the Build Settings tab.

    • 打开 Build Settings 标签页。

    • Navigate to Build Options.

    • 找到 Build Options

    • Set User Script Sandboxing as No.

    • User Script Sandboxing 设为 No

  4. In Xcode, add the pre-action to the ShareExtension scheme.

    1. 在 Xcode 中,为 ShareExtension scheme 添加预操作(pre-action)。
    • Open the Manage Schemes window (Product > Scheme > Manage Schemes).

    • 打开 Manage Schemes 窗口(Product > Scheme > Manage Schemes)。

    • Select the ShareExtension scheme and edit it.

    • 选择 ShareExtension scheme 并编辑。

    • Expand the Build tab.

    • 展开 Build 标签页。

    • Select Pre-actions.

    • 选择 Pre-actions

    • Click + and select New Run Script Action.

    • 点击 +,选择 New Run Script Action

    • In the Provide build settings from drop-down list, select ShareExtension.

    • Provide build settings from 下拉列表中选择 ShareExtension

    • In the Shell text field, enter:

    • Shell 文本框中输入:

      /bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare
      
    • Click Close.

    • 点击 Close

  5. In Xcode, share the build configurations.

    1. 在 Xcode 中,共享构建配置。
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • In the main window under PROJECT, select Runner.

    • 在主窗口的 PROJECT 下,选择 Runner

    • Open the Info tab.

    • 打开 Info 标签页。

    • Expand Configuration.

    • 展开 Configuration

    • Expand Debug and update the value for ShareExtension to match the value for Runner.

    • 展开 Debug,将 ShareExtension 的值更新为与 Runner 一致。

    • Repeat the previous step for Profile, and Release.

    • ProfileRelease 重复上一步。

    • When you are finished, make sure that the configurations look similar to the following:

    • 完成后,确认配置与下图类似:

      Xcode configurations

  6. (Optional) In Xcode, replace any storyboard files with an extension class, if needed.

    1. (可选)如有需要,在 Xcode 中用扩展类替换 storyboard 文件。
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • Select Runner > ShareExtension > Info.

    • 选择 Runner > ShareExtension > Info

    • Expand Information Property List.

    • 展开 Information Property List

    • Delete the NSExtensionMainStoryboard key.

    • 删除 NSExtensionMainStoryboard 键。

    • Add the NSExtensionPrincipalClass key.

    • 添加 NSExtensionPrincipalClass 键。

    • Add one of these values for the NSExtensionPrincipalClass key:

    • NSExtensionPrincipalClass 键添加以下值之一:

      • (Swift) ShareExtension.ShareViewController
      • (Swift)ShareExtension.ShareViewController
      • (Objective-C) ShareViewController
      • (Objective-C)ShareViewController
  7. In Xcode, update the ShareViewController to use the FlutterViewController.

    1. 在 Xcode 中,将 ShareViewController 更新为使用 FlutterViewController
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • Select Runner > ShareExtension > ShareViewController.

    • 选择 Runner > ShareExtension > ShareViewController

    • Update ShareViewController to use the FlutterViewController class:

    • ShareViewController 更新为使用 FlutterViewController 类:

ShareViewController.swift
swift
import UIKit
import Flutter

class ShareViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        showFlutter()
    }

    func showFlutter() {
        let flutterEngine = FlutterEngine(name: "my flutter engine")
        flutterEngine.run()
        let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
        addChild(flutterViewController)
        view.addSubview(flutterViewController.view)
        flutterViewController.view.frame = view.bounds
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.extensionContext?.cancelRequest(
            withError: NSError(domain: Bundle.main.bundleIdentifier!, code: 0))
    }
}
ShareViewController.h
objc
@import Flutter;
@import UIKit;

@interface ShareViewController : UIViewController

@end
ShareViewController.m
objc
#import "ShareViewController.h"
@import Flutter;

@implementation ShareViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self showFlutter];
}

- (void)showFlutter {
    FlutterEngine *flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
    [flutterEngine run];
    FlutterViewController *flutterViewController =
            [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
    [self addChildViewController:flutterViewController];
    [self.view addSubview:flutterViewController.view];
    flutterViewController.view.frame = self.view.bounds;
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.extensionContext cancelRequestWithError:[NSError errorWithDomain:NSBundle.mainBundle.bundleIdentifier code:0 userInfo:nil]];
}
@end
  1. Test your app with the simulator.

  2. 使用模拟器测试应用

注册插件

#

按以下步骤为 App Extension 注册插件。本示例中,App Extension 的 scheme 为 ShareExtension,Flutter 应用的 scheme 为 Runner,应用名为 Example App With Extension

  1. Add an extension to your Flutter app if you haven't already done so.

    1. 若尚未添加,请先 向 Flutter 应用添加扩展
  2. In Xcode, add GeneratedPluginRegistrant.m to the app extension target.

    1. 在 Xcode 中,将 GeneratedPluginRegistrant.m 添加到 App Extension target。
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • In the main window under TARGETS, select ShareExtension.

    • 在主窗口的 TARGETS 下,选择 ShareExtension

    • Open the Build Phases tab.

    • 打开 Build Phases 标签页。

    • Expand Compile Sources.

    • 展开 Compile Sources

    • Click +.

    • 点击 +

    • From the list in the Choose item to add dialog box, select GeneratedPluginRegistrant.m.

    • Choose item to add 对话框的列表中选择 GeneratedPluginRegistrant.m

    • Click Add.

    • 点击 Add

  3. (Swift only) In Xcode, update the SWIFT_OBJC_BRIDGING_HEADER build setting.

    1. (仅 Swift)在 Xcode 中,更新 SWIFT_OBJC_BRIDGING_HEADER 构建设置。
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • In the main window under TARGETS, select ShareExtension.

    • 在主窗口的 TARGETS 下,选择 ShareExtension

    • Open the Build Settings tab.

    • 打开 Build Settings 标签页。

    • Select the All filter.

    • 选择 All 筛选器。

    • Navigate to Swift Compiler - General and change the value for the Objective-C Bridging Header key to Runner/Runner-Bridging-Header.h.

    • 找到 Swift Compiler - General,将 Objective-C Bridging Header 的值改为 Runner/Runner-Bridging-Header.h

  4. In Xcode, update the code for ShareViewController to register GeneratedPluginRegistrant.h.

    1. 在 Xcode 中,更新 ShareViewController 代码以注册 GeneratedPluginRegistrant.h
    • Open the project navigator (View > Navigators > Project).

    • 打开 项目导航器View > Navigators > Project)。

    • Select Runner > ShareExtension > ShareViewController.

    • 选择 Runner > ShareExtension > ShareViewController

    • Update the ShareViewController file to use the GeneratedPluginRegistrant.h:

    • 更新 ShareViewController 文件以使用 GeneratedPluginRegistrant.h

ShareViewController.swift
swift
// Add this inside `showFlutter()` at the top
GeneratedPluginRegistrant.register(with: flutterEngine)
ShareViewController.m
objc
// Add this import at the top
#import "GeneratedPluginRegistrant.h"
ShareViewController.m
objc
// Add this after [flutterEngine run]
[GeneratedPluginRegistrant registerWithRegistry:flutterEngine];
  1. (Xcode) Test your app with the simulator.

  2. (Xcode)使用模拟器测试应用

限制

#
  • You must use an iOS simulator to test your extension in debug mode.

  • 在 debug 模式下测试扩展时,必须使用 iOS 模拟器。

  • Flutter doesn't fully support running app extensions in debug mode on physical devices when used to build extension UIs because a physical device might run out of memory.

  • 在真机上以 debug 模式构建扩展 UI 时,Flutter 对 App Extension 的支持并不完整,因为真机可能内存不足。

  • iOS app extensions have limited memory. It is advisable to only modify an app extension's UI if the app extension supports at least 100MB of memory.

  • iOS App Extension 内存有限。建议仅在扩展至少支持 100MB 内存时才修改其 UI。

在 iOS App Extension 中调用 Dart 代码 / 渲染 Flutter 内容

#

home_widget 插件提供大量功能,包括:

其他资源

#

要在 Flutter iOS 应用中使用 App Extension,请参阅 Codelab 向 Flutter 应用添加主屏幕 Widget

要了解将 Flutter 界面添加到 iOS 应用的各种方式,请参阅 向 iOS 应用添加 Flutter 界面