live_activities

Creator: coderz1093

Last updated:

Add to Cart

Description:

live activities

A Flutter plugin to use iOS 16.1+ Live Activities & iPhone 14 Pro Dynamic Island features.
🧐 What is it ? #
This plugin uses the iOS ActivityKit API.
live_activities can be used to show dynamic live notification & implement dynamic island feature on iPhones that support it 🏝️

⚠️ live_activities is only intended to use with iOS 16.1+ !
It will simply do nothing on other platform & < iOS 16.1











πŸ‘» Getting started #
Due to technical restriction, it's not currently possible to only use Flutter 🫣.
You need to implement in your Flutter iOS project a Widget Extension & develop in Swift/Objective-C your own Live Activity / Dynamic Island design.

ℹ️ You can check into the example repository for a full example app using Live Activities & Dynamic Island



πŸ“± Native #

Open the Xcode workspace project ios/Runner.xcworkspace.
Click on File -> New -> Target...

Select Widget Extension & click on Next.
Specify the product name (e.g., MyAppWidget) and be sure to select "Runner" in "Embed in Application" dropdown.
Click on Finish.
When selecting Finish, an alert will appear, you will need to click on Activate.








Add the "Push Notifications" capabilities for the main Runner app only!.



Enable live activity by adding this line in Info.plist for both Runner and your Widget Extension.


<key>NSSupportsLiveActivities</key>
<true/>
copied to clipboard



Add the "App Group" capability for both Runner and your widget extension.
After you add the capability, check the checkmark next to the text field that contains an identifier of the form group.example.myapp.
This identifier will be used later and refered to as YOUR_GROUP_ID.






ℹ️ You can check on this resource or here for more native informations.


In your extension, you need to create an ActivityAttributes called EXACTLY LiveActivitiesAppAttributes (if you rename, activity will be created but not appear!)

struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable {
public typealias LiveDeliveryData = ContentState // don't forget to add this line, otherwise, live activity will not display it.

public struct ContentState: Codable, Hashable { }

var id = UUID()
}
copied to clipboard

Create a Swift extension to handle prefixed keys at the end of the file.

extension LiveActivitiesAppAttributes {
func prefixedKey(_ key: String) -> String {
return "\(id)_\(key)"
}
}
copied to clipboard

Create an UserDefaults with your group id to access Flutter data in your Swift code.

// Create shared default with custom group
let sharedDefault = UserDefaults(suiteName: "YOUR_GROUP_ID")!

struct FootballMatchApp: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveActivitiesAppAttributes.self) { context in
// create your live activity widget extension here
// to access Flutter properties:
let myVariableFromFlutter = sharedDefault.string(forKey: context.attributes.prefixedKey("myVariableFromFlutter"))!

// [...]
}
}
}
copied to clipboard


πŸ’™ Flutter #

Import the plugin.

import 'package:live_activities/live_activities.dart';
copied to clipboard

Initialize the Plugin by passing the created App Group Id (created above).

final _liveActivitiesPlugin = LiveActivities();
_liveActivitiesPlugin.init(appGroupId: "YOUR_GROUP_ID");
copied to clipboard

Create your dynamic activity.

final Map<String, dynamic> activityModel = {
'name': 'Margherita',
'ingredient': 'tomato, mozzarella, basil',
'quantity': 1,
};

_liveActivitiesPlugin.createActivity(activityModel);
copied to clipboard

You can pass all type of data you want but keep it mind it should be compatible with UserDefaults




Access Flutter basic data from Native 🧡 #

In your Swift extension, you need to create an UserDefaults instance to access data:

let sharedDefault = UserDefaults(suiteName: "YOUR_GROUP_ID")!
copied to clipboard

⚠️ Be sure to use the SAME group id in your Swift extension and your Flutter app!


Access to your typed data:

let pizzaName = sharedDefault.string(forKey: context.attributes.prefixedKey("name"))! // put the same key as your Dart map
let pizzaPrice = sharedDefault.float(forKey: context.attributes.prefixedKey("price"))
let quantity = sharedDefault.integer(forKey: context.attributes.prefixedKey("quantity"))
// [...]
copied to clipboard
Access Flutter picture from Native 🧡 #

In your map, send a LiveActivityImageFromAsset or LiveActivityImageFromUrl object:

final Map<String, dynamic> activityModel = {
'assetKey': LiveActivityImageFromAsset('assets/images/pizza_chorizo.png'),
'url': LiveActivityImageFromUrl(
'https://cdn.pixabay.com/photo/2015/10/01/17/17/car-967387__480.png',
resizeFactor: 0.3,
),
};

_liveActivitiesPlugin.createActivity(activityModel);
copied to clipboard
ℹ️ Use LiveActivityImageFromAsset to load an image from your Flutter asset.
ℹ️ Use LiveActivityImageFromUrl to load an image from an external url.

⚠️ Image need to be in a small resolution to be displayed in your live activity/dynamic island, you can use resizeFactor to automatically resize the image πŸ‘.


In your Swift extension, display the image:

if let assetImage = sharedDefault.string(forKey: context.attributes.prefixedKey("assetKey")), // <-- Put your key here
let uiImage = UIImage(contentsOfFile: shop) {
Image(uiImage: uiImage)
.resizable()
.frame(width: 53, height: 53)
.cornerRadius(13)
} else {
Text("Loading")
}
copied to clipboard
Communicate between native 🧡 and Flutter πŸ’™ #
In order to pass some useful data between your native live activity / dynamic island with your Flutter app you just need to setup URL scheme.

⚠️ It's recommended to set a custom scheme, in this example, la is used but keep in mind, you should use a more personalized scheme.


ex: for an app named Strava, you could use str.


Add a custom url scheme in Xcode by navigating to Runner > Runner > URL Types > URL Schemes



In your Swift code, just create a new link and open to your custom URL Scheme

Link(destination: URL(string: "la://my.app/order?=123")!) { // Replace "la" with your scheme
Text("See order")
}
copied to clipboard

⚠️ Don't forget to put the URL Scheme you have typed in the previous step. (str:// in the previous example)


In your Flutter App, you need to init the custom scheme you provided before

_liveActivitiesPlugin.init(
appGroupId: 'your.group.id', // replace here with your custom app group id
urlScheme: 'str' // replace here with your custom app scheme
);
copied to clipboard

Finally, on the Flutter App too, you just need to listen on the url scheme Scheme

_liveActivitiesPlugin.urlSchemeStream().listen((schemeData) {
// do what do you want here πŸ€—
});
copied to clipboard
Update Live Activity with push notification 🎯 #
You can update live activity directly in your app using the updateActivity() method, but if your app was killed or in the background, you can’t update the notification...
To do this, you can update it using Push Notification on a server.

Get the push token:

Listen on the activity updates (recommended):
_liveActivitiesPlugin.activityUpdateStream.listen((event) {
event.map(
active: (activity) {
// Get the token
print(activity.activityToken);
},
ended: (activity) {},
unknown: (activity) {},
);
});
copied to clipboard

Get directly the push token (not recommended, because the token may change in the future):
final activityToken = await _liveActivitiesPlugin.getPushToken(_latestActivityId!);
print(activityToken);
copied to clipboard



Update your activity with the token on your server (more information can be found here).

To set matchName for a specific notification, you just need to grab the notification id you want (ex. 35253464632) and concatenate with your key by adding a _, example: 35253464632_matchName.
That's it πŸ˜‡

πŸ“˜ Documentation #



Name
Description
Returned value




.init()
Initialize the Plugin by providing an App Group Id (see above)
Future When the plugin is ready to create/update an activity


.createActivity()
Create an iOS live activity
String The activity identifier


.updateActivity()
Update the live activity data by using the activityId provided
Future When the activity was updated


.endActivity()
End the live activity by using the activityId provided
Future When the activity was ended


.getAllActivitiesIds()
Get all activities ids created
Future<List<String>> List of all activities ids


.getAllActivities()
Get a Map of activitiyIds and the ActivityState
Future<Map<String, LiveActivityState>> Map of all activitiyId -> LiveActivityState


.endAllActivities()
End all live activities of the app
Future When all activities was ended


.areActivitiesEnabled()
Check if live activities feature are supported & enabled
Future<bool> Live activities supported or not


.getActivityState()
Get the activity current state
Future<LiveActivityState?> An enum to know the status of the activity (active, dismissed or ended)


.getPushToken()
Get the activity push token synchronously (prefer using activityUpdateStream instead to keep push token up to date)
String? The activity push token (can be null)


.urlSchemeStream()
Subscription to handle every url scheme (ex: when the app is opened from a live activity / dynamic island button, you can pass data)
Future<UrlSchemeData> Url scheme data which handle scheme url host path queryItems


.dispose()
Remove all pictures passed in the AppGroups directory in the current session, you can use the force parameters to remove all pictures
Future Picture removed


.activityUpdateStream
Get notified with a stream about live activity push token & status
Stream<ActivityUpdate> Status updates for new push tokens or when the activity ends




πŸ‘₯ Contributions #
Contributions are welcome. Contribute by creating a PR or an issue πŸŽ‰.
🎯 Roadmap #

❌ Inject a Widget inside the notification with Flutter Engine ?
βœ… Support push token.
βœ… Pass media between extension & Flutter app.
βœ… Support multiple type instead of String (Date, Number etc.).
βœ… Pass data across native dynamic island and Flutter app.
βœ… Pass data across native live activity notification and Flutter app.
βœ… Cancel all activities.
βœ… Get all activities ids.
βœ… Check if live activities are supported.

License

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Customer Reviews

There are no reviews.