flutter_location_services 0.0.1
flutter_location_services: ^0.0.1 copied to clipboard
A robust Flutter package for live location tracking with background support, geofencing, routing, and Firebase synchronization.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_location_services/flutter_location_services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:example/firebase_background_service.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Smart Location Demo',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
final List<Widget> _pages = [
const BasicTrackingPage(),
const RoutingPage(),
const GeofencePage(),
];
@override
void initState() {
super.initState();
_initSmartLocation();
}
Future<void> _initSmartLocation() async {
// Basic config
SmartLocation.configure(
accuracy: SmartAccuracy.high,
debugMode: true,
apiKey: 'YOUR_API_KEY_HERE', // User: Replace this with your API Key
);
Future.delayed(const Duration(seconds: 1), () async {
// Trigger a permission check/request on startup
try {
await SmartLocation.getCurrentPosition();
} catch (_) {
// Ignore initial error, just to trigger permission dialog
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (i) => setState(() => _currentIndex = i),
destinations: const [
NavigationDestination(
icon: Icon(Icons.my_location),
label: 'Tracking',
),
NavigationDestination(icon: Icon(Icons.directions), label: 'Routing'),
NavigationDestination(icon: Icon(Icons.security), label: 'Geofence'),
],
),
);
}
}
// --- Page 1: Basic Tracking ---
class BasicTrackingPage extends StatefulWidget {
const BasicTrackingPage({super.key});
@override
State<BasicTrackingPage> createState() => _BasicTrackingPageState();
}
class _BasicTrackingPageState extends State<BasicTrackingPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Live Tracking')),
body: SmartLocationMap(showLiveLocation: true, onMapCreated: (c) {}),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton.extended(
heroTag: 'stop_tracking',
onPressed: () async {
try {
await SmartLocation.stopBackgroundTracking();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Tracking Stopped')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error stopping tracking: $e'),
backgroundColor: Colors.red,
),
);
}
},
label: const Text('Stop'),
icon: const Icon(Icons.stop),
backgroundColor: Colors.red,
),
const SizedBox(height: 10),
FloatingActionButton.extended(
heroTag: 'start_tracking',
onPressed: () async {
try {
await SmartLocation.startBackgroundTracking(
onBackgroundStart: firebaseOnStart,
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Tracking Started')),
);
} on SmartLocationError catch (e) {
if (e.type == SmartErrorType.serviceDisabled) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.message),
backgroundColor: Colors.red,
action: SnackBarAction(
label: 'Enable',
onPressed: () => SmartLocation.openLocationSettings(),
textColor: Colors.white,
),
),
);
} else if (e.type == SmartErrorType.permissionDenied ||
e.type == SmartErrorType.permissionDeniedForever) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.message),
backgroundColor: Colors.red,
action: SnackBarAction(
label: 'Settings',
onPressed: () => SmartLocation.openAppSettings(),
textColor: Colors.white,
),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.message}'),
backgroundColor: Colors.red,
),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: $e'),
backgroundColor: Colors.red,
),
);
}
},
label: const Text('Start'),
icon: const Icon(Icons.play_arrow),
backgroundColor: Colors.green,
),
],
),
);
}
}
// --- Page 2: Routing ---
class RoutingPage extends StatefulWidget {
const RoutingPage({super.key});
@override
State<RoutingPage> createState() => _RoutingPageState();
}
class _RoutingPageState extends State<RoutingPage> {
SmartRoute? _route;
bool _loading = false;
Future<void> _getRoute() async {
setState(() => _loading = true);
try {
// Mock coordinates for demo (New York)
final start = const LatLng(40.7128, -74.0060);
final end = const LatLng(40.730610, -73.935242);
final route = await SmartLocation.getRoute(
source: start,
destination: end,
);
setState(() => _route = route);
} catch (e) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Error: $e')));
} finally {
setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Routing'),
actions: [
IconButton(
icon: const Icon(Icons.bug_report),
tooltip: 'Toggle Test Mode',
onPressed: () {
SmartLocation.configure(
routeProvider: RouteProviderType.mock,
debugMode: true,
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Test Mode Enabled (Mock Routes)'),
),
);
},
),
],
),
body: Stack(
children: [
SmartLocationMap(
route: _route,
showLiveLocation: false,
markers: _route == null
? {}
: {
Marker(
markerId: const MarkerId('start'),
position: _route!.points.first,
),
Marker(
markerId: const MarkerId('end'),
position: _route!.points.last,
),
},
),
if (_loading) const Center(child: CircularProgressIndicator()),
],
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (_route != null) ...[
FloatingActionButton.extended(
heroTag: 'sim',
onPressed: () {
SmartLocation.startSimulation(_route!, speedKmH: 120);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Simulation Started')),
);
},
label: const Text('Simulate Trip'),
icon: const Icon(Icons.play_arrow),
backgroundColor: Colors.green,
),
const SizedBox(height: 10),
],
FloatingActionButton(
heroTag: 'route',
onPressed: _getRoute,
child: const Icon(Icons.directions),
),
],
),
);
}
}
// --- Page 3: Geofence ---
class GeofencePage extends StatelessWidget {
const GeofencePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Geofencing')),
body: Center(
child: ElevatedButton(
onPressed: () {
SmartLocation.addGeofence(
GeofenceRegion(
id: 'home',
latitude: 37.422,
longitude: -122.084,
radiusMeters: 200,
onEnter: (_) => print('Entered Home!'),
onExit: (_) => print('Left Home!'),
),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Geofence added at Googleplex')),
);
},
child: const Text('Add Geofence (Googleplex)'),
),
),
);
}
}