vk_location_sharing 1.0.0
vk_location_sharing: ^1.0.0 copied to clipboard
A Flutter package for implementing location sharing with foreground streams and periodic background updates.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:vk_location_sharing/vk_location_sharing.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Location Sharing Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const LocationHome(),
);
}
}
class LocationHome extends StatefulWidget {
const LocationHome({super.key});
@override
State<LocationHome> createState() => _LocationHomeState();
}
class _LocationHomeState extends State<LocationHome> {
final _vkLocation = VkLocationSharing();
Position? _foregroundPosition;
Position? _backgroundPosition;
bool _isSharing = false;
bool _handleSeparately = false;
@override
void initState() {
super.initState();
_initializeLocation();
}
Future<void> _initializeLocation() async {
// Initialize the package with configuration
await _vkLocation.initialize(
config: LocationConfig(
handleForegroundAndBackgroundSeparately: _handleSeparately,
accuracy: LocationAccuracy.best,
distanceFilter: 5,
backgroundUpdateFrequency: const Duration(minutes: 15),
onLocationUpdate: (position, {required isBackground}) async {
// Custom callback to handle location data
// You can send it to your backend, analytics, etc.
if (isBackground) {
print(
'Background Location: ${position.latitude}, ${position.longitude}',
);
} else {
print(
'Foreground Location: ${position.latitude}, ${position.longitude}',
);
}
},
),
);
// Listen to foreground location updates
_vkLocation.foregroundLocationStream.listen(
(position) {
if (mounted) {
setState(() {
_foregroundPosition = position;
});
}
},
onError: (error) {
print('Foreground stream error: $error');
},
);
// Listen to background location updates (if handling separately)
if (_handleSeparately) {
_vkLocation.backgroundLocationStream.listen(
(position) {
if (mounted) {
setState(() {
_backgroundPosition = position;
});
}
},
onError: (error) {
print('Background stream error: $error');
},
);
}
}
Future<void> _toggleSharing() async {
if (!_isSharing) {
// Start sharing
final success = await _vkLocation.shareLocation(
includeForeground: true,
includeBackground: true,
);
if (success) {
setState(() {
_isSharing = true;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Location sharing started!')),
);
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Location permissions denied'),
backgroundColor: Colors.red,
),
);
}
}
} else {
// Stop sharing
await _vkLocation.stopSharing(
stopForeground: true,
stopBackground: true,
);
setState(() {
_isSharing = false;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Location sharing stopped')),
);
}
}
}
@override
void dispose() {
_vkLocation.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('VK Location Sharing'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Configuration section
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Configuration',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
CheckboxListTile(
title: const Text(
'Handle Foreground & Background Separately'),
value: _handleSeparately,
onChanged: _isSharing
? null
: (value) {
setState(() {
_handleSeparately = value ?? false;
});
},
subtitle: const Text(
'If disabled, all updates go to foreground stream',
style: TextStyle(fontSize: 12),
),
),
],
),
),
),
const SizedBox(height: 20),
// Foreground location display
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Foreground Location',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
if (_foregroundPosition != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Latitude: ${_foregroundPosition!.latitude.toStringAsFixed(6)}',
),
Text(
'Longitude: ${_foregroundPosition!.longitude.toStringAsFixed(6)}',
),
Text(
'Accuracy: ${_foregroundPosition!.accuracy.toStringAsFixed(2)}m',
),
Text(
'Altitude: ${_foregroundPosition!.altitude.toStringAsFixed(2)}m',
),
Text(
'Speed: ${(_foregroundPosition!.speed * 3.6).toStringAsFixed(2)}km/h',
),
Text(
'Time: ${_foregroundPosition!.timestamp}',
style: const TextStyle(fontSize: 12),
),
],
)
else
const Text(
'No foreground location data yet',
style: TextStyle(color: Colors.grey),
),
],
),
),
),
const SizedBox(height: 20),
// Background location display (if handling separately)
if (_handleSeparately)
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Background Location',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
if (_backgroundPosition != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Latitude: ${_backgroundPosition!.latitude.toStringAsFixed(6)}',
),
Text(
'Longitude: ${_backgroundPosition!.longitude.toStringAsFixed(6)}',
),
Text(
'Time: ${_backgroundPosition!.timestamp}',
style: const TextStyle(fontSize: 12),
),
],
)
else
const Text(
'No background location data yet',
style: TextStyle(color: Colors.grey),
),
],
),
),
),
const SizedBox(height: 30),
// Main action button
ElevatedButton.icon(
onPressed: _toggleSharing,
icon: Icon(_isSharing ? Icons.stop : Icons.play_arrow),
label: Text(
_isSharing
? 'Stop Sharing Location'
: 'Start Sharing Location',
style: const TextStyle(fontSize: 16),
),
style: ElevatedButton.styleFrom(
backgroundColor:
_isSharing ? Colors.red.shade400 : Colors.green.shade400,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
const SizedBox(height: 20),
// Info section
Card(
color: Colors.blue.shade50,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'ℹ️ Info',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'• Foreground: Real-time location updates',
style: TextStyle(fontSize: 12),
),
Text(
'• Background: Periodic updates every 15 minutes',
style: TextStyle(fontSize: 12),
),
Text(
'• Custom callback executes with each location update',
style: TextStyle(fontSize: 12),
),
],
),
),
),
],
),
),
),
);
}
}