添加 iOS App 扩展
了解如何向 Flutter 应用添加 App 扩展
本指南向您展示如何在 Flutter 应用中使用 iOS App 扩展。
概述
#iOS App 扩展允许您在 iOS 应用之外扩展功能。您的应用可以作为主屏幕小组件出现,或者您可以让应用的部分功能在其他应用内可用。
在以下示例中,当用户选择一张照片在 iOS 照片应用中分享时,名为 Example App With Extension 的 Flutter 应用会显示在照片应用的分享表单中。
向 Flutter 应用添加 iOS App 扩展
#如果您想将 Flutter 应用与 iOS 操作系统集成,可以向 Flutter 项目添加 iOS App 扩展。为了实现流畅的工作流程,以下步骤展示了如何向名为 example_app_with_extension 的新 Flutter 应用添加 Share(分享) App 扩展,但您也可以从现有项目开始。
-
在控制台中,创建一个名为
example_app_with_extension的新 Flutter 项目。flutter create example_app_with_extension -
在控制台中,打开
example_app_with_extension项目的 Xcode 工作区。cd example_app_with_extension && open ios/Runner.xcworkspace -
在 Xcode 中,添加一个名为
Share的 App 扩展,并将其命名为ShareExtension。在 Xcode 菜单栏中,选择 File > New > Target。
添加 Share Extension。
在 Name 字段中,输入 ShareExtension。
点击 Finish(完成)。
在弹出的 Activate … Scheme 对话框中,选择 Activate。
-
在 Xcode 中,更改构建过程的顺序。
打开 项目导航器 (View > Navigators > Project)。
在 项目导航器 顶部,选择 Runner。
在主窗口的 TARGETS 下,选择 Runner。
打开 Build Phases 选项卡。
将 Embed Foundation Extensions 拖动到 Run Script 上方。
-
确保您的 Minimum Deployments (最低部署) iOS 版本设置正确,并且 Runner 和 ShareExtension 中的设置保持一致。
打开 项目导航器 (View > Navigators > Project)。
在 项目导航器 顶部,选择 Runner。
在主窗口的 TARGETS 下,选择 Runner。
在 General 选项卡上,检查您的 Minimum Deployments 下拉菜单的值,使其与 ShareExtension > General 选项卡上的值相匹配。
-
在控制台中,运行以下命令以重新构建您的 iOS 应用:
flutter build ios --config-only
当您添加新的 App 扩展时,Xcode 会根据您选择的模板生成示例代码。有关生成代码和 WidgetKit 的更多信息,请参阅 Apple 的 App 扩展文档。
测试 iOS App 扩展
#将 App 扩展添加到 Flutter 项目后,您可以使用模拟器或物理设备进行测试。如果您在调试模式下测试扩展,则必须使用 iOS 模拟器。
以下步骤假设您使用的是 添加 iOS App 扩展 中的示例应用程序和 Share 扩展。
在 Xcode 中,将 App 扩展添加到您的项目。
-
在控制台中,使用以下命令运行您的 iOS 应用:
flutter run -
在模拟器中测试您的 App 扩展。
启动支持 Share 扩展的应用,例如照片应用。
选择一张照片,点击分享按钮,然后点击您应用的分享扩展图标。
将 App 扩展添加到您的项目。
-
在控制台中,以发布模式运行您的 Flutter 应用:
flutter run --release -
在您的设备上测试您的 App 扩展。
启动支持 Share 扩展的应用,例如照片应用。
选择一张照片,点击分享按钮,然后点击您应用的分享扩展图标。
与 iOS App 扩展交互的其他方式
#Flutter 应用使用与 UIKit 或 SwiftUI 应用相同的技术与 iOS App 扩展进行交互。宿主应用和 App 扩展不会直接通信。当设备用户与扩展交互时,宿主应用可能并未运行。应用和您的扩展可以读取和写入共享资源,或者使用更高级的 API 进行通信。
使用更高级的 API
#有些扩展具有 API。例如,Core Spotlight 框架会对您的应用进行索引,允许用户通过 Spotlight 和 Safari 进行搜索。WidgetKit 框架可以触发主屏幕小组件的更新。
为了简化应用与扩展之间的通信方式,Flutter 插件封装了这些 API。要查找封装了扩展 API 的插件,请查看 利用 Apple 的系统 API 和框架 或搜索 pub.dev。
共享资源
#要共享 Flutter 应用和 App 扩展之间的资源,请将 Runner 应用目标和扩展目标放入同一个 App Group 中。
要将目标添加到 App Group:
在 Xcode 中打开目标设置。
导航到 Signing & Capabilities 选项卡。
选择 + Capability,然后选择 App Groups。
-
从两个选项中选择要将目标添加到的 App Group:
- 从列表中选择一个 App Group。
- 点击 + 以添加新的 App Group。
当两个目标属于同一个 App Group 时,它们可以读取和写入同一个源。为您的数据选择以下源之一:
-
键值对: 使用
shared_preference_app_group插件读取或写入同一个 App Group 内的UserDefaults。 -
文件: 使用
path_provider插件提供的 App Group 容器路径来读写文件。 -
数据库: 使用
path_provider插件提供的 App Group 容器路径,通过sqflite插件创建数据库。
安排后台更新
#后台任务提供了一种无论应用状态如何,都能通过代码更新扩展的方法。
要从 Flutter 应用安排后台工作,请使用 workmanager 插件。
添加深度链接
#您可能希望引导用户从 App 扩展跳转到 Flutter 应用中的特定页面。要打开应用中的特定路由,您可以使用深度链接 (Deep Linking)。
添加可滚动列表
#默认情况下,Flutter 视图不处理 Share 扩展中的滚动手势。要在 Share 扩展中支持可滚动列表,请按照 GitHub 上的说明 进行操作。
在 iOS App 扩展中打开 Flutter 应用
#您可以使用 FlutterViewController 直接在某些 iOS App 扩展(例如 Share 扩展)中打开您的 Flutter 应用。
在以下示例中,名为 Example App With Extension 的 Flutter 应用在 Share 扩展中打开,允许用户在应用之间共享内容。
按照以下步骤在 Share App 扩展中显示 Flutter 应用。在本示例中,App 扩展方案名为 ShareExtension,Flutter 应用方案名为 Runner,Flutter 应用名为 Example App With Extension。
-
如果您尚未执行,请向您的 Flutter 应用添加扩展。
-
在控制台中,导航到您的 Flutter 项目目录,然后使用以下命令在 Xcode 中打开您的项目:
open ios/Runner.xcworkspace -
在 Xcode 中,禁用用户脚本沙盒 (User Script Sandboxing)。
打开 项目导航器 (View > Navigators > Project)。
在主窗口的 TARGETS 下,选择 ShareExtension。
打开 Build Settings 选项卡。
导航到 Build Options。
将 User Script Sandboxing 设置为 No。
-
在 Xcode 中,将预操作 (pre-action) 添加到
ShareExtension方案。打开 Manage Schemes 窗口 (Product > Scheme > Manage Schemes)。
选择 ShareExtension 方案并进行编辑。
展开 Build 选项卡。
选择 Pre-actions。
点击 + 并选择 New Run Script Action。
在 Provide build settings from 下拉列表中,选择 ShareExtension。
在 Shell 文本字段中,输入:
/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare点击 Close。
-
在 Xcode 中,共享构建配置。
打开 项目导航器 (View > Navigators > Project)。
在主窗口的 PROJECT 下,选择 Runner。
打开 Info 选项卡。
展开 Configuration。
展开 Debug 并更新 ShareExtension 的值,使其与 Runner 的值相匹配。
对 Profile 和 Release 重复上述步骤。
完成后,确保配置看起来与以下内容相似:

-
(可选)如果需要,在 Xcode 中将任何 Storyboard 文件替换为扩展类。
打开 项目导航器 (View > Navigators > Project)。
选择 Runner > ShareExtension > Info。
展开 Information Property List。
删除 NSExtensionMainStoryboard 键。
添加 NSExtensionPrincipalClass 键。
为
NSExtensionPrincipalClass键添加以下值之一:- (Swift) ShareExtension.ShareViewController
- (Objective-C) ShareViewController
-
在 Xcode 中,更新
ShareViewController以使用FlutterViewController。打开 项目导航器 (View > Navigators > Project)。
选择 Runner > ShareExtension > ShareViewController。
更新
ShareViewController以使用FlutterViewController类:
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))
}
}
@import Flutter;
@import UIKit;
@interface ShareViewController : UIViewController
@end
#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
注册插件
#按照以下步骤为 App 扩展注册插件。在本示例中,App 扩展方案名为 ShareExtension,Flutter 应用方案名为 Runner,Flutter 应用名为 Example App With Extension。
-
如果您尚未执行,请向您的 Flutter 应用添加扩展。
-
在 Xcode 中,将
GeneratedPluginRegistrant.m添加到 App 扩展目标。打开 项目导航器 (View > Navigators > Project)。
在主窗口的 TARGETS 下,选择 ShareExtension。
打开 Build Phases 选项卡。
展开 Compile Sources。
点击 +。
在 Choose item to add 对话框的列表中,选择 GeneratedPluginRegistrant.m。
点击 Add。
-
(仅 Swift)在 Xcode 中,更新
SWIFT_OBJC_BRIDGING_HEADER构建设置。打开 项目导航器 (View > Navigators > Project)。
在主窗口的 TARGETS 下,选择 ShareExtension。
打开 Build Settings 选项卡。
选择 All 过滤器。
导航到 Swift Compiler - General,并将 Objective-C Bridging Header 键的值更改为 Runner/Runner-Bridging-Header.h。
-
在 Xcode 中,更新
ShareViewController的代码以注册GeneratedPluginRegistrant.h。打开 项目导航器 (View > Navigators > Project)。
选择 Runner > ShareExtension > ShareViewController。
更新
ShareViewController文件以使用GeneratedPluginRegistrant.h:
// Add this inside `showFlutter()` at the top
GeneratedPluginRegistrant.register(with: flutterEngine)
// Add this import at the top
#import "GeneratedPluginRegistrant.h"
// Add this after [flutterEngine run]
[GeneratedPluginRegistrant registerWithRegistry:flutterEngine];
- (Xcode)在模拟器中测试您的应用。
约束
#-
您必须使用 iOS 模拟器在调试模式下测试您的扩展。
-
当 Flutter 用于构建扩展 UI 时,它不支持在物理设备上以调试模式完全运行 App 扩展,因为物理设备可能会耗尽内存。
-
iOS App 扩展的内存有限。建议仅在 App 扩展支持至少 100MB 内存的情况下修改其 UI。
在 iOS App 扩展中调用 Dart 代码 / 渲染 Flutter 内容
#home_widget 软件包提供了大量功能,包括允许执行以下操作:
其他资源
#有关在 Flutter iOS 应用中使用 App 扩展的分步说明,请查看 向您的 Flutter 应用添加主屏幕小组件 Codelab。
要了解有关向 iOS 应用添加 Flutter 屏幕的各种方式的更多信息,请参阅 向 iOS 应用添加 Flutter 屏幕。