译|如何使用 Flutter 创建动态岛和 ActivityKit


theme: v-green
highlight: vs

提示:机器翻译,仅供参考!
原文:https://medium.com/kbtg-life/how-to-create-dynamic-island-and-activitykit-with-flutter-b16d386e557e

本教程将向您展示如何在 iOS 中设置动态岛。我使用的是 Xcode 14.1 Beta 2,但您可以将其用作 Native 和 Flutter 的指南。当 Xcode 14.1 发布或 Apple 对 Beta 版进行更改时,我将再次更新这篇文章。

让我们首先创建一个小部件工具包。转到File > Target

选择 iOS 平台并搜索 Widget Extension。

插入产品名称。您不需要选中“Include Configuration”框,因为它在本教程中没有任何作用,但我还是选中了它。

完成所有步骤后,您将获得一个小部件文件夹以及 Xcode 中的主文件夹。

现在让我们运行它。是的!现在我们的主页上有一个小部件 UI。只需触摸并按住主屏幕上的小部件套件,然后单击 + 即可将您的应用添加为小部件。

我在这里讨论 Widget Kit 是因为我们可以将它与 ActivityKit 一起使用。但是,我不会深入探讨,因为我们在本教程中的重点是动态岛(Dynamic Island)。

顶部只是关于 Widget Kit,那么动态岛上的 Live Activity 在哪里?让我们开始研究 ActivityKit 好吗?

首先,转到 info.plist 并添加一个新密钥“NSSupportsLiveActivities”并将其设置为 true。

添加 PizzaDeliveryAttributes.swift 文件,然后像这样实现 ActivityAttributes 协议。

import ActivityKit
import Foundation
struct PizzaDeliveryAttributes: ActivityAttributes {
    public typealias PizzaDeliveryStatus = ContentState
    public struct ContentState: Codable, Hashable {
       var driverName: String
       var deliveryTimer: ClosedRange<Date>
   }
    var numberOfPizzas: Int
    var totalAmount: String
    var orderNumber: String
}

把这个文件的目标添加到主项目和小部件的扩展中。你可以在Xcode的右边看到这个。

为动态岛创建一个新布局。我正在使用 Apple 开发者网站上的那个。

import SwiftUI
import WidgetKit
import ActivityKit

struct PizzaDeliveryActivityWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: PizzaDeliveryAttributes.self) { context in
            LockScreenLiveActivityView(context: context)

        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    Label("\(context.attributes.totalAmount) Pizzas", systemImage: "bag")
                        .foregroundColor(.indigo)
                        .font(.title2)
                }

                DynamicIslandExpandedRegion(.trailing) {
                    Label {
                        Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                            .multilineTextAlignment(.trailing)
                            .frame(width: 50)
                            .monospacedDigit()
                    } icon: {
                        Image(systemName: "timer")
                            .foregroundColor(.indigo)
                    }
                    .font(.title2)
                }

                DynamicIslandExpandedRegion(.center) {
                    Text("\(context.state.driverName) is on their way!")
                        .lineLimit(1)
                        .font(.caption)
                }

                DynamicIslandExpandedRegion(.bottom) {
                    Button {
                        // Deep link into your app.
                    } label: {
                        Label("Call driver", systemImage: "phone")
                    }
                    .foregroundColor(.indigo)
                }
            } compactLeading: {
                Label {
                    Text("\(context.attributes.totalAmount) Pizzas")
                } icon: {
                    Image(systemName: "bag")
                        .foregroundColor(.indigo)
                }
                .font(.caption2)
            } compactTrailing: {
                Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                    .multilineTextAlignment(.center)
                    .frame(width: 40)
                    .font(.caption2)
            } minimal: {
                VStack(alignment: .center) {
                    Image(systemName: "timer")
                    Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                        .multilineTextAlignment(.center)
                        .monospacedDigit()
                        .font(.caption2)
                }
            }
            .keylineTint(.cyan)
        }
    }
}

struct LockScreenLiveActivityView: View {
    let context: ActivityViewContext<PizzaDeliveryAttributes>

    var body: some View {
        VStack {
            Spacer()
            Text("\(context.state.driverName) is on their way with your pizza!")
            Spacer()
            HStack {
                Spacer()
                Label {
                    Text("\(context.attributes.totalAmount) Pizzas")
                } icon: {
                    Image(systemName: "bag")
                        .foregroundColor(.indigo)
                }
                .font(.title2)
                Spacer()
                Label {
                    Text(timerInterval: context.state.deliveryTimer, countsDown: true)
                        .multilineTextAlignment(.center)
                        .frame(width: 50)
                        .monospacedDigit()
                } icon: {
                    Image(systemName: "timer")
                        .foregroundColor(.indigo)
                }
                .font(.title2)
                Spacer()
            }
            Spacer()
        }
        .activitySystemActionForegroundColor(.indigo)
        .activityBackgroundTint(.cyan)
    }
}

如果您有一个现有的 Widget Kit,则必须将 @main 从 Widget Kit 移动到一个新类,以便我们可以将两个当前的小部件与 ActivityKit 一起使用。

import SwiftUI
@main
struct PizzaDeliveryWidgets: WidgetBundle {
   var body: some Widget {
      widget()
      PizzaDeliveryActivityWidget()
   }
}

我的大部分教程来自 Apple 文档。也就是说,Apple 没有提供任何示例代码,所以我为自己创建了一个,以便在它发布时在我的项目中使用。您可以阅读 Apple 文档以获取更深入的详细信息。

Apple Developer Documentation:https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities
以上是针对Native的。对于 Flutter 开发人员,您需要在 Dart 端实现更多功能以将请求发送到 Native。

如果想快速玩一下,你可以拉出这个 repo 来玩一下。
https://github.com/theamorn/flutter-dynamicisland

就是这样!现在您可以享受 LiveActivity 和 动态岛。

作者:杭州程序员张张

%s 个评论

要回复文章请先登录注册