{"id":19273048,"url":"https://github.com/fluttercandies/flutter_live_activities","last_synced_at":"2025-04-21T22:32:31.820Z","repository":{"id":62827200,"uuid":"562740352","full_name":"fluttercandies/flutter_live_activities","owner":"fluttercandies","description":"Flutter Live Activities Plugin","archived":false,"fork":false,"pushed_at":"2024-06-24T09:54:15.000Z","size":13380,"stargazers_count":22,"open_issues_count":2,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-06-24T11:22:08.960Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fluttercandies.png","metadata":{"files":{"readme":"README-ZH.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-07T06:43:23.000Z","updated_at":"2024-06-24T09:54:18.000Z","dependencies_parsed_at":"2022-11-07T12:45:45.035Z","dependency_job_id":null,"html_url":"https://github.com/fluttercandies/flutter_live_activities","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fflutter_live_activities","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fflutter_live_activities/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fflutter_live_activities/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fflutter_live_activities/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluttercandies","download_url":"https://codeload.github.com/fluttercandies/flutter_live_activities/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223881803,"owners_count":17219268,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T20:40:35.058Z","updated_at":"2024-11-09T20:40:35.660Z","avatar_url":"https://github.com/fluttercandies.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/live_activities.png\" height=100\u003e\n\n# Flutter Live Activities\n\nLive Activities 的 Flutter 插件。用于创建、更新和处理 [DynamicIsland UI] 和 [Lock screen/banner UI] 的动作\n\n[English](README.md) | 中文说明\n\n[![pub package](https://img.shields.io/pub/v/flutter_live_activities?logo=dart\u0026label=stable\u0026style=flat-square)](https://pub.dev/packages/flutter_live_activities)\n[![GitHub stars](https://img.shields.io/github/stars/fluttercandies/flutter_live_activities?logo=github\u0026style=flat-square)](https://github.com/fluttercandies/flutter_live_activities/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/fluttercandies/flutter_live_activities?logo=github\u0026style=flat-square)](https://github.com/fluttercandies/flutter_live_activities/network/members)\n\u003ca target=\"_blank\" href=\"https://jq.qq.com/?_wv=1027\u0026k=5bcc0gy\"\u003e\u003cimg border=\"0\" src=\"https://pub.idqqimg.com/wpa/images/group.png\" alt=\"FlutterCandies\" title=\"FlutterCandies\"\u003e\u003c/a\u003e\n\n\u003e 此插件需要通知权限\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/pre.gif\" width=200\u003e\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/pre2.gif\" width=200\u003e\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/img.png\" width=200\u003e\n\n#### 1. 在iOS项目中添加 Widget\n\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/new.png\" height=400\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/we.png\" height=300\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/config.png\" height=300\u003e \n\n* 创建成功后的目录结构\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/finish.png\" height=400\u003e\n\n#### 2. 编辑 `Runner/Info.plist` 和 `live_activity_test/Info.plist`\n\n两个文件中都添加以下内容:\n```xml\n\u003cplist version=\"1.0\"\u003e\n\u003cdict\u003e\n    ...\n\t\u003ckey\u003eNSSupportsLiveActivities\u003c/key\u003e\n\t\u003ctrue/\u003e\n    ...\n\u003c/dict\u003e\n\u003c/plist\u003e\n```\n\n#### 3. 在小部件 swift 文件中创建数据通道\n\n[live_activity_test/live_activity_testLiveActivity.swift](https://github.com/fluttercandies/flutter_live_activities/blob/main/example/ios/live_activity_test/live_activity_testLiveActivity.swift)\n\n```swift\nimport ActivityKit\nimport SwiftUI\nimport WidgetKit\n\n// 自定义Model\nstruct TestData {\n    var text: String\n\n    init?(JSONData data: [String: String]) {\n        self.text = data[\"text\"] ?? \"\"\n    }\n\n    init(text: String) {\n        self.text = text\n    }\n}\n\n// 数据通道  \u003c-  必要！不能更改任何内容\nstruct FlutterLiveActivities: ActivityAttributes, Identifiable {\n    public typealias LiveData = ContentState\n\n    public struct ContentState: Codable, Hashable {\n        var data: [String: String]\n    }\n\n    var id = UUID()\n}\n\n@available(iOSApplicationExtension 16.1, *)\nstruct live_activity_testLiveActivity: Widget {\n    var body: some WidgetConfiguration {\n        // 绑定通道\n        ActivityConfiguration(for: FlutterLiveActivities.self) { context in\n\n            // 锁屏上显示的内容\n\n            // Json to model\n            let data = TestData(JSONData: context.state.data)\n\n            // UI\n            VStack {\n                Text(data?.text ?? \"\")\n            }\n            .activityBackgroundTint(Color.cyan)\n            .activitySystemActionForegroundColor(Color.black)\n        } dynamicIsland: { context in\n            // Json to model\n            let data = TestData(JSONData: context.state.data)\n\n            // 灵动岛\n            return DynamicIsland {\n                // Expanded UI goes here.  Compose the expanded UI through\n                // various regions, like leading/trailing/center/bottom\n                DynamicIslandExpandedRegion(.leading) {\n                    Text(\"Leading\")\n                }\n                DynamicIslandExpandedRegion(.trailing) {\n                    Text(\"Trailing\")\n                }\n                DynamicIslandExpandedRegion(.bottom) {\n                    // 显示 Flutter 传递过来的数据\n                    Text(data?.text ?? \"\")\n                }\n            } compactLeading: {\n                Text(\"L\")\n            } compactTrailing: {\n                Text(\"T\")\n            } minimal: {\n                Text(\"Min\")\n            }\n            .keylineTint(Color.red)\n        }\n    }\n}\n```\n\n更多布局信息，请参考: [live activities](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities)\n\n#### 4. APIs\n\n```dart\nimport 'package:flutter_live_activities/flutter_live_activities.dart';\n\n...\n\nfinal FlutterLiveActivities _liveActivities = FlutterLiveActivities();\n\nString? _activityId;\n```\n\n* 检查设备是否开启此功能\n```dart\nawait _liveActivities.areActivitiesEnabled();\n```\n\n* 获取启动Link\n```dart\nawait _liveActivities.getInitUri()\n```\n\n* 创建一个 Live Activity\n```dart\n_activityId = await _liveActivities.createActivity(\u003cString, String\u003e{'text': 'Hello World'});\n```\n\n* 更新 Live Activity\n```dart\nif(_activityId != null) {\n    await _liveActivities.updateActivity(_activityId!, \u003cString, String\u003e{'text': 'Update Hello World'});\n}\n```\n\n\u003e ActivityKit 更新和远程推送通知更新的更新动态数据大小不能超过 4KB。 [相关文档](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities)\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/4k.png\" height=300\u003e\n\n\u003e 其它解决方案请参考 [live_activities](https://pub.dev/packages/live_activities)\n\n* 结束 Live Activity\n```dart\nif(_activityId != null) {\n    await _liveActivities.endActivity(_activityId!);\n}\n```\n\n* 结束全部 Live Activities\n```dart\nawait _liveActivities.endAllActivities();\n```\n\n* 获取全部 Live Activities id\n```dart\nawait _liveActivities.getAllActivities()\n```\n\n#### 5. Deeplink(点击动作)\n\n* 默认 urlScheme 为 `fla`\n\n\u003e `FlutterLiveActivities({this.urlScheme = 'fla'})`\n\n* 在项目中添加 urlScheme\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/scheme.png\" height=400\u003e\n\n* Swift 代码:\n\n```swift\n@available(iOSApplicationExtension 16.1, *)\nstruct live_activity_testLiveActivity: Widget {\n    var body: some WidgetConfiguration {\n        ActivityConfiguration(for: FlutterLiveActivities.self) { context in\n            let data = TestData(JSONData: context.state.data)\n\n            // Lock screen/banner UI goes here\n\n            VStack(alignment: .leading) {\n                Text(data?.text ?? \"\")\n                HStack {\n                    // 通过 `Link` 创建一个动作\n                    Link(destination: URL(string: \"fla://xx.xx/tap/A\")!) {\n                        Text(\"A\")\n                            .frame(width: 40, height: 40)\n                            .background(.blue)\n                    }\n                    // 通过 `Link` 创建一个动作\n                    Link(destination: URL(string: \"fla://xx.xx/tap/B\")!) {\n                        Text(\"B\")\n                            .frame(width: 40, height: 40)\n                            .background(.blue)\n                    }\n                    // 通过 `Link` 创建一个动作\n                    Link(destination: URL(string: \"fla://xx.xx/tap/C\")!) {\n                        Text(\"C\")\n                            .frame(width: 40, height: 40)\n                            .background(.blue)\n                    }\n                }\n                .frame(width: .infinity, height: .infinity)\n            }\n            .padding(20)\n            .activityBackgroundTint(Color.cyan)\n            .activitySystemActionForegroundColor(Color.black)\n\n        } dynamicIsland: { context in\n\n            let data = TestData(JSONData: context.state.data)\n\n            return DynamicIsland {\n                DynamicIslandExpandedRegion(.bottom) {\n                    // 通过 `Link` 创建一个动作\n                    Link(destination: URL(string: \"fla://xxxxxxx.xxxxxx\")!) {\n                        Text(data?.text ?? \"\")\n                            .background(.red)\n                    }\n                }\n            } compactLeading: {\n                Text(\"L\")\n            } compactTrailing: {\n                Text(\"T\")\n            } minimal: {\n                Text(\"Min\")\n            }\n            .widgetURL(URL(string: \"fla://www.apple.com\")) // 或使用 widgetURL\n            .keylineTint(Color.red)\n        }\n    }\n}\n```\n\n* Dart 代码:\n\n```dart\n_subscription ??= _liveActivities.uriStream().listen((String? uri) {\n    dev.log('deeplink uri: $uri');\n});\n```\n\n#### 6. Display image\n\n\u003e 由于数据块大小的限制。我们无法向LiveActivities发送图片元数据  \n\n\u003e LiveActivities不支持异步加载，所以我们不能使用AsyncImage或读取本地文件\n\n\u003e 开发者论坛的解决方案: [716902](https://developer.apple.com/forums/thread/716902)\n\n* 添加 AppGroup (此操作需要付费Apple账户)\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/group.png\" height=300\u003e\n\n* 在 Runner 和 Widget 中创建/选择同样的 groupId\n\n\u003cimg src=\"https://raw.githubusercontent.com/xSILENCEx/project_images/main/flutter_live_activities/groupId.png\" height=300\u003e\n\n* 发送图片到 group:\n\nDart代码:\n```dart\nFuture\u003cvoid\u003e _sendImageToGroup() async {\n    const String url = 'https://cdn.iconscout.com/icon/free/png-256/flutter-2752187-2285004.png';\n\n    final String? path = await ImageHelper.getFilePathFromUrl(url);\n\n    if (path != null) {\n        _liveActivities.sendImageToGroup(\n            id: 'test-img',\n            filePath: path,\n            groupId: 'group.live_example',\n        );\n    }\n}\n```\n\nSwift代码:\n```swift\nDynamicIslandExpandedRegion(.leading) {\n    if let imageContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: \"group.live_example\")?.appendingPathComponent(\"test-img\"), /// Use id here\n        let uiImage = UIImage(contentsOfFile: imageContainer.path())\n    {\n        Image(uiImage: uiImage)\n            .resizable()\n            .frame(width: 53, height: 53)\n            .cornerRadius(13)\n    } else {\n        Text(\"Leading\")\n    }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fflutter_live_activities","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluttercandies%2Fflutter_live_activities","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fflutter_live_activities/lists"}