likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 14

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

提示:机器翻译,仅供参考! 原文:medium.com/kbtg-life/h…

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

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

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

选择 iOS 平台并搜索 Widget Extension。

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

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

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

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

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

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

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

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

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

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

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

添加 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的右边看到这个。

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

为动态岛创建一个新布局。我正在使用 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:developer.apple.com/documentati… 以上是针对Native的。对于 Flutter 开发人员,您需要在 Dart 端实现更多功能以将请求发送到 Native。

如果想快速玩一下,你可以拉出这个 repo 来玩一下。 github.com/theamorn/fl…

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