flutter_bg_location 1.0.0 copy "flutter_bg_location: ^1.0.0" to clipboard
flutter_bg_location: ^1.0.0 copied to clipboard

A Flutter plugin for background location tracking that continues even when the app is killed. Features include persistent storage, real-time updates, and server sync.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bg_location/flutter_bg_location.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Background Location Tracker',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const LocationTrackerPage(),
    );
  }
}

class LocationTrackerPage extends StatefulWidget {
  const LocationTrackerPage({super.key});

  @override
  LocationTrackerPageState createState() => LocationTrackerPageState();
}

class LocationTrackerPageState extends State<LocationTrackerPage>
    with WidgetsBindingObserver {
  Location? _currentLocation;
  bool _isTracking = false;
  List<Location> _storedLocations = [];

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _setupLocationTracking();
    _checkServiceStatus();
    _loadStoredLocations();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _loadStoredLocations();
    }
  }

  Future<void> _setupLocationTracking() async {
    try {
      await FlutterBgLocation.setAndroidNotification(
        title: 'Location Tracking Active',
        message: 'Your location is being tracked in background',
        icon: '@mipmap/ic_launcher',
      );

      await FlutterBgLocation.setAndroidConfiguration(5000);

      FlutterBgLocation.getLocationUpdates((location) {
        setState(() {
          _currentLocation = location;
        });
        if (kDebugMode) {
          print('Real-time location: $location');
        }
      });
    } catch (e) {
      if (kDebugMode) {
        print('Error setting up location tracking: $e');
      }
    }
  }

  Future<void> _checkServiceStatus() async {
    try {
      final isRunning = await FlutterBgLocation.isServiceRunning();
      setState(() {
        _isTracking = isRunning;
      });
    } catch (e) {
      if (kDebugMode) {
        print('Error checking service status: $e');
      }
    }
  }

  Future<void> _loadStoredLocations() async {
    try {
      final locations = await FlutterBgLocation.getStoredLocations();
      setState(() {
        _storedLocations = locations;
      });
      if (kDebugMode) {
        print('Loaded ${locations.length} stored locations');
      }
    } catch (e) {
      if (kDebugMode) {
        print('Error loading stored locations: $e');
      }
    }
  }

  Future<void> _requestPermissions() async {
    // For iOS, we need to request in stages
    if (Platform.isIOS) {
      // First request "When In Use"
      var status = await Permission.locationWhenInUse.request();

      if (status.isGranted) {
        // Then request "Always"
        await Future.delayed(const Duration(seconds: 1));

        if (mounted) {
          await showDialog(
            context: context,
            builder: (context) => AlertDialog(
              title: const Text('Background Location'),
              content: const Text(
                'To track your location in the background, please select '
                '"Allow While Using App" first, then we\'ll ask for '
                '"Always" permission.',
              ),
              actions: [
                TextButton(
                  onPressed: () => Navigator.pop(context),
                  child: const Text('OK'),
                ),
              ],
            ),
          );
        }

        await Permission.locationAlways.request();
      }
    } else {
      // Android
      var status = await Permission.location.request();

      if (status.isGranted) {
        if (await Permission.locationAlways.isDenied) {
          if (mounted) {
            await showDialog(
              context: context,
              builder: (context) => AlertDialog(
                title: const Text('Background Location Permission'),
                content: const Text(
                  'To track location when app is closed, please allow '
                  '"Allow all the time" in the next permission dialog.',
                ),
                actions: [
                  TextButton(
                    onPressed: () => Navigator.pop(context),
                    child: const Text('OK'),
                  ),
                ],
              ),
            );
          }
          await Permission.locationAlways.request();
        }

        await Permission.ignoreBatteryOptimizations.request();
      }
    }
  }

  Future<void> _startTracking() async {
    await _requestPermissions();

    try {
      final success = await FlutterBgLocation.startLocationService(
        distanceFilter: 10.0,
      );

      if (success) {
        setState(() {
          _isTracking = true;
        });
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Location tracking started')),
          );
        }
      }
    } catch (e) {
      if (kDebugMode) {
        print('Error starting tracking: $e');
      }
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Error: $e')),
        );
      }
    }
  }

  Future<void> _stopTracking() async {
    try {
      await FlutterBgLocation.stopLocationService();
      setState(() {
        _isTracking = false;
      });
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Location tracking stopped')),
        );
      }
    } catch (e) {
      if (kDebugMode) {
        print('Error stopping tracking: $e');
      }
    }
  }

  Future<void> _clearStoredLocations() async {
    try {
      await FlutterBgLocation.clearStoredLocations();
      await _loadStoredLocations();
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Stored locations cleared')),
        );
      }
    } catch (e) {
      if (kDebugMode) {
        print('Error clearing locations: $e');
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Background Location Tracker'),
        backgroundColor: _isTracking ? Colors.green : Colors.grey,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Service Status Card
            Card(
              color: _isTracking ? Colors.green[50] : Colors.grey[50],
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Icon(
                          _isTracking ? Icons.check_circle : Icons.cancel,
                          color: _isTracking ? Colors.green : Colors.grey,
                        ),
                        const SizedBox(width: 8),
                        Text(
                          _isTracking ? 'Service Running' : 'Service Stopped',
                          style: const TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 8),
                    Text(
                      _isTracking
                          ? 'Location is being tracked even when app is killed'
                          : 'Start tracking to collect location data',
                      style: TextStyle(color: Colors.grey[700]),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 20),

            // Current Location Card
            const Text(
              'Current Location (Real-time):',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: _currentLocation != null
                    ? Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                              'Latitude: ${_currentLocation!.latitude.toStringAsFixed(6)}'),
                          Text(
                              'Longitude: ${_currentLocation!.longitude.toStringAsFixed(6)}'),
                          Text(
                              'Accuracy: ${_currentLocation!.accuracy.toStringAsFixed(2)}m'),
                          Text(
                              'Speed: ${_currentLocation!.speed.toStringAsFixed(2)}m/s'),
                        ],
                      )
                    : const Text('No location data yet'),
              ),
            ),

            const SizedBox(height: 20),

            // Stored Locations
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  'Stored Locations: ${_storedLocations.length}',
                  style: const TextStyle(
                      fontSize: 18, fontWeight: FontWeight.bold),
                ),
                if (_storedLocations.isNotEmpty)
                  TextButton.icon(
                    onPressed: _clearStoredLocations,
                    icon: const Icon(Icons.delete),
                    label: const Text('Clear'),
                  ),
              ],
            ),
            const SizedBox(height: 8),
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: _storedLocations.isEmpty
                    ? const Text('No stored locations yet')
                    : SizedBox(
                        height: 200,
                        child: ListView.builder(
                          itemCount: _storedLocations.length > 10
                              ? 10
                              : _storedLocations.length,
                          itemBuilder: (context, index) {
                            final loc = _storedLocations[
                                _storedLocations.length - 1 - index];
                            return ListTile(
                              dense: true,
                              leading: const Icon(Icons.location_on, size: 16),
                              title: Text(
                                '${loc.latitude.toStringAsFixed(4)}, ${loc.longitude.toStringAsFixed(4)}',
                              ),
                            );
                          },
                        ),
                      ),
              ),
            ),

            const SizedBox(height: 20),

            // Control Buttons
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isTracking ? null : _startTracking,
                    icon: const Icon(Icons.play_arrow),
                    label: const Text('Start'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isTracking ? _stopTracking : null,
                    icon: const Icon(Icons.stop),
                    label: const Text('Stop'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
              ],
            ),

            const SizedBox(height: 16),

            SizedBox(
              width: double.infinity,
              child: OutlinedButton.icon(
                onPressed: _loadStoredLocations,
                icon: const Icon(Icons.refresh),
                label: const Text('Reload Stored Locations'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
6
likes
160
points
189
downloads
screenshot

Publisher

verified publisherashishcodes.site

Weekly Downloads

A Flutter plugin for background location tracking that continues even when the app is killed. Features include persistent storage, real-time updates, and server sync.

Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_bg_location

Packages that implement flutter_bg_location