live_activity_update 0.0.2
live_activity_update: ^0.0.2 copied to clipboard
Live Activity Update Plugin
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:live_activity_update/live_activity_update.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String? _activityId;
bool _isActivityRunning = false;
double _currentProgress = 0.0;
final _liveActivityUpdatePlugin = LiveActivityUpdate();
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _liveActivityUpdatePlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
Future<void> _startLiveActivity() async {
try {
final id = await _liveActivityUpdatePlugin.start(data: {
'title': 'Food Delivery',
'text': 'Your order is being prepared',
'progress': 0.25,
// Android-specific progress segments (API 34+)
'segments': [
{'weight': 1, 'color': 0xFF4CAF50}, // Green
{'weight': 1, 'color': 0xFFFF9800}, // Orange
{'weight': 2, 'color': 0xFF9E9E9E}, // Grey
],
'points': [
{'position': 25, 'color': 0xFF2196F3}, // Blue point at 25%
],
});
setState(() {
_activityId = id;
_isActivityRunning = true;
_currentProgress = 0.25;
});
_showSnackBar('Live Activity Started: $id');
} on LiveActivityException catch (e) {
_showSnackBar('Error: ${e.code} - ${e.message}');
} catch (e) {
_showSnackBar('Failed to start activity: $e');
}
}
Future<void> _updateLiveActivity() async {
if (_activityId == null) return;
try {
// Simulate progress updates
final newProgress = (_currentProgress + 0.25).clamp(0.0, 1.0);
String newText;
String newTitle;
if (newProgress <= 0.25) {
newTitle = 'Food Delivery';
newText = 'Preparing your order...';
} else if (newProgress <= 0.5) {
newTitle = 'Food Delivery';
newText = 'Order ready for pickup';
} else if (newProgress <= 0.75) {
newTitle = 'Food Delivery';
newText = 'Driver assigned - on the way!';
} else {
newTitle = 'Food Delivery';
newText = 'Delivered! Enjoy your meal 🍕';
}
await _liveActivityUpdatePlugin.update(
id: _activityId!,
data: {
'title': newTitle,
'text': newText,
'progress': newProgress,
// Update segments for Android
'segments': [
{'weight': 1, 'color': newProgress >= 0.25 ? 0xFF4CAF50 : 0xFF9E9E9E},
{'weight': 1, 'color': newProgress >= 0.50 ? 0xFF4CAF50 : 0xFF9E9E9E},
{'weight': 1, 'color': newProgress >= 0.75 ? 0xFF4CAF50 : 0xFF9E9E9E},
{'weight': 1, 'color': newProgress >= 1.0 ? 0xFF4CAF50 : 0xFF9E9E9E},
],
'points': [
{'position': (newProgress * 100).toInt(), 'color': 0xFF2196F3},
],
},
);
setState(() {
_currentProgress = newProgress;
});
_showSnackBar('Activity Updated - Progress: ${(newProgress * 100).toInt()}%');
} on LiveActivityException catch (e) {
_showSnackBar('Update Error: ${e.code} - ${e.message}');
} catch (e) {
_showSnackBar('Failed to update activity: $e');
}
}
Future<void> _stopLiveActivity() async {
if (_activityId == null) return;
try {
await _liveActivityUpdatePlugin.stop(id: _activityId!);
setState(() {
_activityId = null;
_isActivityRunning = false;
_currentProgress = 0.0;
});
_showSnackBar('Live Activity Stopped');
} on LiveActivityException catch (e) {
_showSnackBar('Stop Error: ${e.code} - ${e.message}');
} catch (e) {
_showSnackBar('Failed to stop activity: $e');
}
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 3),
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Live Activity Update Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Live Activity Update Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Platform Information',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text('Running on: $_platformVersion'),
],
),
),
),
const SizedBox(height: 20),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Live Activity Status',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text('Status: ${_isActivityRunning ? 'Active' : 'Inactive'}'),
if (_activityId != null) ...[
const SizedBox(height: 4),
Text('ID: $_activityId'),
const SizedBox(height: 4),
Text('Progress: ${(_currentProgress * 100).toInt()}%'),
],
],
),
),
),
const SizedBox(height: 20),
if (_currentProgress > 0.0 && _currentProgress < 1.0) ...[
LinearProgressIndicator(
value: _currentProgress,
backgroundColor: Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(
_currentProgress >= 1.0 ? Colors.green : Colors.blue,
),
),
const SizedBox(height: 20),
],
ElevatedButton.icon(
onPressed: !_isActivityRunning ? _startLiveActivity : null,
icon: const Icon(Icons.play_arrow),
label: const Text('Start Live Activity'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: _isActivityRunning && _currentProgress < 1.0
? _updateLiveActivity
: null,
icon: const Icon(Icons.update),
label: const Text('Update Activity'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: _isActivityRunning ? _stopLiveActivity : null,
icon: const Icon(Icons.stop),
label: const Text('Stop Activity'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 30),
Card(
color: Colors.blue[50],
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info_outline, color: Colors.blue[700]),
const SizedBox(width: 8),
Text(
'How it works',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.blue[700],
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
const Text(
'• iOS: Creates Live Activities that appear in Dynamic Island and Lock Screen\n'
'• Android: Shows ongoing notifications with live updates\n'
'• Android 14+: Supports rich progress indicators with segments and points\n'
'• Updates happen in real-time without opening the app',
style: TextStyle(height: 1.5),
),
],
),
),
),
],
),
),
),
);
}
}