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

news/2024/7/20 22:08:45 标签: flutter, xcode, ios

在这里插入图片描述

本教程将向您展示如何在 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 和 动态岛。


http://www.niftyadmin.cn/n/879918.html

相关文章

如何使用Flutter的指纹设置本地认证

在用户体验方面&#xff0c;我们最常发现的是安卓手机有指纹认证&#xff0c;iPhone有面部识别。 让我解释一下幕后发生的主要情况。当用户在第一次登录/注册后决定使用上述方法激活认证时&#xff0c;他/她实际上是在说把我的登录凭证保存在本地主机中&#xff0c;没有注册指…

第八届河南省程序设计大赛-B.最大岛屿0000110011000000

最大岛屿 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB描述神秘的海洋&#xff0c;惊险的探险之路&#xff0c;打捞海底宝藏&#xff0c;激烈的海战&#xff0c;海盗劫富等等。加勒比海盗&#xff0c;你知道吧&#xff1f;杰克船长驾驶着自己的的战船黑珍珠1…

Vue.js 3 开源组件推荐:代码差异查看器插件

一个Vue.js差异查看器插件&#xff0c;可以用来比较两个代码片断之间的差异。 Github地址&#xff1a;https://github.com/hoiheart/vue-diff 支持语言&#xff1a; cssxml: xml, html, xhtml, rss, atom, xjb, xsd, xsl, plist, svgmarkdown: markdown, md, mkdown, mkdjav…

Flutter 中的应用内购买

虽然我们总是精心制作我们的应用程序,但我们并不总是让它们免费。除了将我们的应用程序上传到Play商店收取费用外,另一种赚钱的方式是通过应用内购买。Flutter in_app_purchase(IAP)是一个第一方Flutter软件包,允许开发者在其应用程序中实现iOS上的App Store或Android上的…

『NYIST』第九届河南省ACM竞赛队伍选拔赛[正式赛二]-最小内积(第八届北京师范大学程序设计竞赛决赛)

H. 最小内积 Time Limit: 1000msMemory Limit: 65536KB64-bit integer IO format: %lld Java class name: Main向量是几何中的一个重要概念。考虑两个向量v1(x1,x2,...,xn)和v2(y1,y2,...,yn)&#xff0c;向量的内积定义为x1y1x2y2...xnyn例如向量(1,9,8,8)和(0,9,1,1)的内…

NYOJ-116士兵杀敌(二),树状数组~~

士兵杀敌&#xff08;二&#xff09; 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;5描述南将军手下有N个士兵&#xff0c;分别编号1到N&#xff0c;这些士兵的杀敌数都是已知的。 小工是南将军手下的军师&#xff0c;南将军经常想知道第m号到…

突破内卷!写业务代码中的成长机会

写业务代码有成长机会吗&#xff1f;关于这个问题&#xff0c;答案非常肯定&#xff1a;必须有成长机会。对于大部分公司而言&#xff0c;能够写底层代码或者中间件代码的人总是有限的&#xff0c;写业务代码会面临更高的复杂度。 这里分三个层次来看其中的成长机会。 第 1 个层…

BestCoder Round #79 (div.2)-jrMz and angles,,暴力求解~

jrMz and angle Time Limit: 2000/1000 MS (Java/Others)Memory Limit: 65536/65536 K (Java/Others)问题描述jrMz有两种角&#xff0c;第一种角都是正nn边形的内角&#xff0c;第二种角都是正mm边形的内角。jrMz想选出其中一些&#xff0c;某种角可以选多个或一个都不选&#…