flutter ios 16 live activities

一个 Flutter 插件,用于使用 iOS 16.1+ 的实时活动和 iPhone 14 Pro 的灵动岛功能。

? 这是什么?

此插件使用了 iOS ActivityKit API

live_activities 可用于显示动态实时通知和在 iPhone 14 Pro / Max 上实现灵动岛功能 ?

⚠️ live_activities 仅适用于 iOS 16.1+!在其他平台和 < iOS 16.1 上它将不会有任何作用。

flutter ios 16 live activities dynamic island flutter ios 16 live activities lockscreen

flutter ios 16 live activities preview dynamic island flutter ios 16 live activities preview action

? 入门

由于一些技术限制,目前无法仅使用 Flutter ?。

您需要在 Flutter iOS 项目中实现一个小部件扩展 (Widget Extension),并用 *Swift*/*Objective-C* 开发您自己的实时活动 / 灵动岛设计。

ℹ️ 您可以在 示例存储库 中找到一个使用实时活动和灵动岛的完整示例应用程序。

  • ? 原生

    • 打开 Xcode 工作空间项目 ios/Runner.xcworkspace
    • 点击 文件 -> 新建 -> 目标…
      • 选择 Widget Extension 并点击下一步
      • 指定产品名称(例如:*MyApp*Widget),并确保在“嵌入到应用程序”下拉菜单中选择“Runner”。
      • 点击完成
      • 点击完成时,会出现一个提示,您需要点击激活

create widget extension xcode

  • 在主 Runner 应用中启用推送通知功能!

enable push notification capabilities

  • 通过在 Runner 和您的 Widget ExtensionInfo.plist 中添加此行来启用实时活动。

<key>NSSupportsLiveActivities</key>
<true/>

enable live activities xcode

  • Runner 和您的 Widget Extension 创建 App Group。

enable live activity

ℹ️ 您可以参考这个 资源这里 获取更多原生信息。

  • 在您的扩展中,您需要创建一个名为 正好 LiveActivitiesAppAttributesActivityAttributes(如果您重命名了,活动会创建但不会显示!)

struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable {
  public typealias LiveDeliveryData = ContentState // don't forget to add this line, otherwise, live activity will not display it.

  public struct ContentState: Codable, Hashable { }
  
  var id = UUID()
}
  • 创建一个 UserDefaults 并指定您的 group id,以便在 Swift 代码中访问 Flutter 数据。

// Create shared default with custom group
let sharedDefault = UserDefaults(suiteName: "YOUR_GROUP_ID")!

struct FootballMatchApp: Widget {
  var body: some WidgetConfiguration {
    ActivityConfiguration(for: LiveActivitiesAppAttributes.self) { context in
      // create your live activity widget extension here
      // to access Flutter properties:
      let myVariableFromFlutter = sharedDefault.string(forKey: "myVariableFromFlutter")!

      // [...]
    }
  }
}
  • ? Flutter

    • 导入插件。
    import 'package:live_activities/live_activities.dart';
    • 通过传入已创建的应用组 ID (App Group Id) 来初始化插件(如上所述)。

    final _liveActivitiesPlugin = LiveActivities();
    _liveActivitiesPlugin.init(appGroupId: "YOUR_CREATED_APP_ID");
    • 创建您的动态活动。

    final Map<String, dynamic> activityModel = {
      'name': 'Margherita',
      'ingredient': 'tomato, mozzarella, basil',
      'quantity': 1,
    };
    
    _liveActivitiesPlugin.createActivity(activityModel);

    您可以传递任何类型的数据,但请记住它应该与 UserDefaults 兼容。

从原生访问 Flutter 基本数据?

  • 在您的 Swift 扩展中,您需要创建一个 UserDefaults 实例来访问数据。
let sharedDefault = UserDefaults(suiteName: "YOUR_CREATED_APP_ID")!

⚠️ 确保在 Swift 扩展和 Flutter 应用中使用相同的 group id!

  • 访问您的类型化数据

let pizzaName = sharedDefault.string(forKey: "name")! // put the same key as your Dart map
let pizzaPrice = sharedDefault.float(forKey: "price")
let quantity = sharedDefault.integer(forKey: "quantity")
// [...]

从原生访问 Flutter 图片?

  • 在您的 map 中,传递一个 LiveActivityImageFromAssetLiveActivityImageFromUrl 对象。

final Map<String, dynamic> activityModel = {
  'assetKey': LiveActivityImageFromAsset('assets/images/pizza_chorizo.png'),
  'url': LiveActivityImageFromUrl(
    'https://cdn.pixabay.com/photo/2015/10/01/17/17/car-967387__480.png',
    resizeFactor: 0.3,
  ),
};

_liveActivitiesPlugin.createActivity(activityModel);

ℹ️ 使用 LiveActivityImageFromAsset 从您的 Flutter 资源加载图片。

ℹ️ 使用 LiveActivityImageFromUrl 从外部 URL 加载图片。

⚠️ 图片需要是小分辨率才能显示在您的实时活动/灵动岛中,您可以使用 resizeFactor 自动调整图片大小 ?。

  • 在您的 Swift 扩展中显示图片

if let assetImage = sharedDefault.string(forKey: "assetKey"), // <-- Put your key here
  let uiImage = UIImage(contentsOfFile: shop) {
  Image(uiImage: uiImage)
      .resizable()
      .frame(width: 53, height: 53)
      .cornerRadius(13)
} else {
  Text("Loading")
}

通过原生 ? 和 Flutter ? 进行通信

为了在您的原生实时活动 / 灵动岛与您的Flutter 应用之间传递一些有用的数据,您只需要设置URL 方案

  • 通过导航到 Runner -> Runner -> URL 类型 -> URL 方案,在 Xcode 中添加自定义 URL 方案。

add url scheme xcode

  • 在您的 Swift 代码中,只需创建一个新的链接并打开到您的自定义URL 方案

Link(destination: URL(string: "la://my.app/order?=123")!) { // Replace "la" with your scheme
  Text("See order")
}

⚠️ 不要忘记输入上一步中键入的URL 方案

  • 在您的 Flutter 应用中,您只需要监听URL 方案

_liveActivitiesPlugin.urlSchemeStream().listen((schemeData) {
  // do what do you want here ?
});

通过推送通知更新实时活动 ?

您可以使用 updateActivity() 方法直接在应用中更新实时活动,但如果您的应用已被杀死或在后台,您将无法更新通知……

要做到这一点,您可以通过服务器上的推送通知来更新它。

  • 获取推送令牌
    • 监听活动更新(推荐)

      _liveActivitiesPlugin.activityUpdateStream.listen((event) {
        event.map(
          active: (activity) {
            // Get the token
            print(activity.activityToken);
          },
          ended: (activity) {},
          unknown: (activity) {},
        );
      });
    • 直接获取推送令牌(不推荐,因为令牌将来可能会更改)。

      final activityToken = await _liveActivitiesPlugin.getPushToken(_latestActivityId!);
      print(activityToken);
  • 在您的服务器上使用令牌更新您的活动(更多信息可以在 这里 找到)。

? 文档

名称 描述 返回值
.init() 通过提供应用组 ID (App Group Id) 来初始化插件(见上文)。 Future 插件准备好创建/更新活动时。
.createActivity() 创建 iOS 实时活动。 String 活动标识符。
.updateActivity() 使用提供的 activityId 更新实时活动数据。 Future 活动更新后。
.endActivity() 使用提供的 activityId 结束实时活动。 Future 活动结束后。
.getAllActivitiesIds() 获取所有已创建的活动 ID。 Future<List<String>> 所有活动 ID 的列表。
.endAllActivities() 结束应用的所有实时活动。 Future 所有活动结束后。
.areActivitiesEnabled() 检查实时活动功能是否受支持和启用。 Future<bool> 实时活动是否受支持。
.getActivityState() 获取活动的当前状态。 Future<LiveActivityState> 一个枚举,用于了解活动的狀態(activedismissedended)。
.getPushToken() 同步获取活动的推送令牌(最好使用 activityUpdateStream 来保持推送令牌的最新)。 String? 活动的推送令牌(可能为 null)。
.urlSchemeStream() 用于处理所有 URL 方案的订阅(例如:当应用从实时活动/灵动岛按钮打开时,您可以传递数据)。 Future<UrlSchemeData> URL 方案数据,处理 scheme url host path queryItems
.dispose() 移除当前会话中已传递到 AppGroups 目录的所有图片,您可以使用 force 参数来删除所有图片。 Future 图片已移除。
.activityUpdateStream 获取实时活动推送令牌和状态的流式通知。 Stream<ActivityUpdate> 新推送令牌或活动结束时的状态更新。

? 贡献

欢迎贡献。通过创建 PR 或提交 issue 来贡献 ?。

? 路线图

  • 通过 Flutter Engine 将 Widget 注入通知中 ?
  • 支持推送令牌。
  • 在扩展和 Flutter 应用之间传递媒体。
  • 支持多种类型,而不是仅支持 String(日期、数字等)。
  • 在原生灵动岛和 Flutter 应用之间传递数据。
  • 在原生实时活动通知和 Flutter 应用之间传递数据。
  • 取消所有活动。
  • 获取所有活动 ID。
  • 检查实时活动是否受支持。

GitHub

查看 Github