https://github.com/fluttercandies/flutter_live_activities
Flutter Live Activities Plugin
https://github.com/fluttercandies/flutter_live_activities
Last synced: about 1 year ago
JSON representation
Flutter Live Activities Plugin
- Host: GitHub
- URL: https://github.com/fluttercandies/flutter_live_activities
- Owner: fluttercandies
- License: mit
- Created: 2022-11-07T06:43:23.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-06-24T09:54:15.000Z (almost 2 years ago)
- Last Synced: 2024-06-24T11:22:08.960Z (almost 2 years ago)
- Language: Dart
- Size: 12.8 MB
- Stars: 22
- Watchers: 4
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README-ZH.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README

# Flutter Live Activities
Live Activities 的 Flutter 插件。用于创建、更新和处理 [DynamicIsland UI] 和 [Lock screen/banner UI] 的动作
[English](README.md) | 中文说明
[](https://pub.dev/packages/flutter_live_activities)
[](https://github.com/fluttercandies/flutter_live_activities/stargazers)
[](https://github.com/fluttercandies/flutter_live_activities/network/members)

> 此插件需要通知权限



#### 1. 在iOS项目中添加 Widget


* 创建成功后的目录结构

#### 2. 编辑 `Runner/Info.plist` 和 `live_activity_test/Info.plist`
两个文件中都添加以下内容:
```xml
...
NSSupportsLiveActivities
...
```
#### 3. 在小部件 swift 文件中创建数据通道
[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)
```swift
import ActivityKit
import SwiftUI
import WidgetKit
// 自定义Model
struct TestData {
var text: String
init?(JSONData data: [String: String]) {
self.text = data["text"] ?? ""
}
init(text: String) {
self.text = text
}
}
// 数据通道 <- 必要!不能更改任何内容
struct FlutterLiveActivities: ActivityAttributes, Identifiable {
public typealias LiveData = ContentState
public struct ContentState: Codable, Hashable {
var data: [String: String]
}
var id = UUID()
}
@available(iOSApplicationExtension 16.1, *)
struct live_activity_testLiveActivity: Widget {
var body: some WidgetConfiguration {
// 绑定通道
ActivityConfiguration(for: FlutterLiveActivities.self) { context in
// 锁屏上显示的内容
// Json to model
let data = TestData(JSONData: context.state.data)
// UI
VStack {
Text(data?.text ?? "")
}
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)
} dynamicIsland: { context in
// Json to model
let data = TestData(JSONData: context.state.data)
// 灵动岛
return DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
Text("Trailing")
}
DynamicIslandExpandedRegion(.bottom) {
// 显示 Flutter 传递过来的数据
Text(data?.text ?? "")
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T")
} minimal: {
Text("Min")
}
.keylineTint(Color.red)
}
}
}
```
更多布局信息,请参考: [live activities](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities)
#### 4. APIs
```dart
import 'package:flutter_live_activities/flutter_live_activities.dart';
...
final FlutterLiveActivities _liveActivities = FlutterLiveActivities();
String? _activityId;
```
* 检查设备是否开启此功能
```dart
await _liveActivities.areActivitiesEnabled();
```
* 获取启动Link
```dart
await _liveActivities.getInitUri()
```
* 创建一个 Live Activity
```dart
_activityId = await _liveActivities.createActivity({'text': 'Hello World'});
```
* 更新 Live Activity
```dart
if(_activityId != null) {
await _liveActivities.updateActivity(_activityId!, {'text': 'Update Hello World'});
}
```
> ActivityKit 更新和远程推送通知更新的更新动态数据大小不能超过 4KB。 [相关文档](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities)

> 其它解决方案请参考 [live_activities](https://pub.dev/packages/live_activities)
* 结束 Live Activity
```dart
if(_activityId != null) {
await _liveActivities.endActivity(_activityId!);
}
```
* 结束全部 Live Activities
```dart
await _liveActivities.endAllActivities();
```
* 获取全部 Live Activities id
```dart
await _liveActivities.getAllActivities()
```
#### 5. Deeplink(点击动作)
* 默认 urlScheme 为 `fla`
> `FlutterLiveActivities({this.urlScheme = 'fla'})`
* 在项目中添加 urlScheme

* Swift 代码:
```swift
@available(iOSApplicationExtension 16.1, *)
struct live_activity_testLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FlutterLiveActivities.self) { context in
let data = TestData(JSONData: context.state.data)
// Lock screen/banner UI goes here
VStack(alignment: .leading) {
Text(data?.text ?? "")
HStack {
// 通过 `Link` 创建一个动作
Link(destination: URL(string: "fla://xx.xx/tap/A")!) {
Text("A")
.frame(width: 40, height: 40)
.background(.blue)
}
// 通过 `Link` 创建一个动作
Link(destination: URL(string: "fla://xx.xx/tap/B")!) {
Text("B")
.frame(width: 40, height: 40)
.background(.blue)
}
// 通过 `Link` 创建一个动作
Link(destination: URL(string: "fla://xx.xx/tap/C")!) {
Text("C")
.frame(width: 40, height: 40)
.background(.blue)
}
}
.frame(width: .infinity, height: .infinity)
}
.padding(20)
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)
} dynamicIsland: { context in
let data = TestData(JSONData: context.state.data)
return DynamicIsland {
DynamicIslandExpandedRegion(.bottom) {
// 通过 `Link` 创建一个动作
Link(destination: URL(string: "fla://xxxxxxx.xxxxxx")!) {
Text(data?.text ?? "")
.background(.red)
}
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T")
} minimal: {
Text("Min")
}
.widgetURL(URL(string: "fla://www.apple.com")) // 或使用 widgetURL
.keylineTint(Color.red)
}
}
}
```
* Dart 代码:
```dart
_subscription ??= _liveActivities.uriStream().listen((String? uri) {
dev.log('deeplink uri: $uri');
});
```
#### 6. Display image
> 由于数据块大小的限制。我们无法向LiveActivities发送图片元数据
> LiveActivities不支持异步加载,所以我们不能使用AsyncImage或读取本地文件
> 开发者论坛的解决方案: [716902](https://developer.apple.com/forums/thread/716902)
* 添加 AppGroup (此操作需要付费Apple账户)

* 在 Runner 和 Widget 中创建/选择同样的 groupId

* 发送图片到 group:
Dart代码:
```dart
Future _sendImageToGroup() async {
const String url = 'https://cdn.iconscout.com/icon/free/png-256/flutter-2752187-2285004.png';
final String? path = await ImageHelper.getFilePathFromUrl(url);
if (path != null) {
_liveActivities.sendImageToGroup(
id: 'test-img',
filePath: path,
groupId: 'group.live_example',
);
}
}
```
Swift代码:
```swift
DynamicIslandExpandedRegion(.leading) {
if let imageContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.live_example")?.appendingPathComponent("test-img"), /// Use id here
let uiImage = UIImage(contentsOfFile: imageContainer.path())
{
Image(uiImage: uiImage)
.resizable()
.frame(width: 53, height: 53)
.cornerRadius(13)
} else {
Text("Leading")
}
}
```