fl_coffee_geolocation 1.0.1 copy "fl_coffee_geolocation: ^1.0.1" to clipboard
fl_coffee_geolocation: ^1.0.1 copied to clipboard

The Coffee Flutter Geolocation plugin offers robust and efficient location tracking capabilities for Flutter applications, especially designed for background operation.

example/lib/main.dart

import 'package:fl_coffee_geolocation/models/config.dart';
import 'package:fl_coffee_geolocation/models/location.dart';
import 'package:fl_coffee_geolocation/models/location_config.dart';
import 'package:fl_coffee_geolocation/models/notification.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:fl_coffee_geolocation/fl_coffee_geolocation.dart';
import 'dart:math' as math;

import 'location_settings_dialog.dart';

const primaryColor = Color.fromRGBO(2,71,76, 1);
const accentColor = Color.fromRGBO(120,202,78, 1);
const yellowColor = Color.fromRGBO(200,229,31, 1);

void main() {
  runApp(const GetMaterialApp(home: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Coffee Background Geolocation Example',
      home: CoffeeBackgroudGeolocationScreen(), // Set LocationMapScreen as the home screen
    );
  }
}

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

  @override
  // ignore: library_private_types_in_public_api
  _CoffeeBackgroudGeolocationScreenState createState() => _CoffeeBackgroudGeolocationScreenState();
}

class _CoffeeBackgroudGeolocationScreenState extends State<CoffeeBackgroudGeolocationScreen> {
  bool _isLoading = true;
  bool _isTracking = false;
  bool _isInitializing = false;
  bool _initSucceeded = false;
  bool _isLocationCardExpanded = true;

  CoffeeBackgroundLocation? _currentLocation;

  final MapController _mapController = MapController();
  List<Marker> _currentMarkers = [];
  List<Marker> _updatedLocationMarkers = [];

  List<Polygon> _geofenceMarkers = [];
  List<CoffeeBackgroundLocation> _pathlinePositions = [];
  double _distanceFilter = 0;
  bool _showFences = true;

  @override
  void initState() {
    super.initState();

    Future.delayed(const Duration(seconds: 2),() {
      _getCurrentLocation();
    });
  }

  Future<void> _fetchAndSetDistanceFilter() async {
    LocationConfig? locationSettings = await CoffeeBackgroundGeolocation.getLocationSettings();
    if (locationSettings != null) {
      setState(() {
        _distanceFilter = locationSettings.distanceFilter ?? 0;
      });
    }
  }

  Future<void> _init() async {
    setState(() {
      _isInitializing = true;
    });

    try {
      await CoffeeBackgroundGeolocation.init(Config(
        location: LocationConfig(
          desiredAccuracy: Config.desiredAcuracyNavigation,
          distanceFilter: 5
        ),
        debug: true,
        notification: CoffeeBackgroundNotification(
          priority: Config.notificationPriorityHigh,
          title: "Running in background",
          text: "This is a message for the notification"
        )
      ));

      await _fetchAndSetDistanceFilter();

      setState(() {
        _initSucceeded = true;
      });
    } catch (e) {
      Get.snackbar(
        "Initialization Error",
        "Failed to initialize: $e",
        snackPosition: SnackPosition.BOTTOM,
        backgroundColor: Colors.redAccent,
        colorText: Colors.white,
        borderRadius: 10,
        margin: const EdgeInsets.all(10),
        duration: const Duration(seconds: 3),
      );
    }

    setState(() {
      _isInitializing = false;
    });
  }
  
  void _getCurrentLocation() async {
    setState(() {
      _isLoading = true;
    });

    try {
      CoffeeBackgroundLocation? location = await CoffeeBackgroundGeolocation.getCurrentLocation();
      
      if (location == null) {
        setState(() {
          _isLoading = false;
        });
        Get.snackbar(
          "Error",
          "Failed to get current location",
          snackPosition: SnackPosition.BOTTOM,
          borderRadius: 10,
          margin: const EdgeInsets.all(10),
          duration: const Duration(seconds: 2),
        );
        return;
      }

      final currentMarker = _buildMarker(
        latitude: location.latitude, 
        longitude: location.longitude,
        heading: location.heading,
        color: Colors.blue
      );

      if(_currentLocation == null) {
        _centerMap(location, zoom: 15);
      }

      setState(() {
        _currentLocation = location;
        _isLoading = false;
        _currentMarkers = [currentMarker];
      });

    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      Get.snackbar(
        "Error",
        "Failed to get location: $e",
        snackPosition: SnackPosition.BOTTOM,
        borderRadius: 10,
        margin: const EdgeInsets.all(10),
        duration: const Duration(seconds: 2),
      );
    }
  }

  void _toggleTracking() async {
    if(!_initSucceeded) {
      Get.snackbar(
        "Initialization Required",
        "Please initialize the Coffee Background Geolocation service first by pressing the 'Initialize Coffee' button.",
        snackPosition: SnackPosition.BOTTOM,
        borderRadius: 10,
        margin: const EdgeInsets.all(10),
        duration: const Duration(seconds: 2),
      );

      return;
    }

    if (_isTracking) {
      await CoffeeBackgroundGeolocation.stop();
    } else {
      await CoffeeBackgroundGeolocation.start();
      CoffeeBackgroundGeolocation.onLocation((CoffeeBackgroundLocation location) {
        if(_currentLocation == null) {
          _centerMap(location, zoom: 15);
        }

        _handleLocationUpdate(location);
      });
    }

    setState(() {
      _isTracking = !_isTracking;
    });
  }

  void _handleLocationUpdate(CoffeeBackgroundLocation location) {
    // Add the new location to the pathline
    _pathlinePositions.add(location);

    // Add a new geofence marker
    List<LatLng> circlePoints = createCirclePoints(
      LatLng(location.latitude, location.longitude), 
      _distanceFilter
    );

    Polygon geofenceCircle = Polygon(
      points: circlePoints,
      color: accentColor.withOpacity(0.5),
      borderColor: accentColor,
      isFilled: true,
      borderStrokeWidth: 2.0,
    );

    setState(() {
      _geofenceMarkers.add(geofenceCircle);
    });

    // Update UI
    final updateMarker = _buildMarker(
      latitude: location.latitude, 
      longitude: location.longitude,
      heading: location.heading,
      color: primaryColor
    );

    setState(() {
      _pathlinePositions = _pathlinePositions;
      _geofenceMarkers = _geofenceMarkers;
      _updatedLocationMarkers = [updateMarker];
      _currentLocation = location;
    });
  }

  Future<void> _openDialogSettings() async {
     if(!_initSucceeded) {
      Get.snackbar(
        "Initialization Required",
        "Please initialize the Coffee Background Geolocation service first by pressing the 'Initialize Coffee' button.",
        snackPosition: SnackPosition.BOTTOM,
        borderRadius: 10,
        margin: const EdgeInsets.all(10),
        duration: const Duration(seconds: 2),
      );

      return;
    }

    final result = await showDialog(
      context: context,
      builder: (BuildContext context) {
        return LocationSettingsDialog(showFences: _showFences);
      },
    );

    if(result != null) {

      if(result['resetPathLine'] == true) {
        setState(() {
          _pathlinePositions = [];
          _geofenceMarkers = [];
        });
      }

      setState(() {
        _showFences = result['showFences'];
      });

      _fetchAndSetDistanceFilter();
    }
  }

  void _centerMap(CoffeeBackgroundLocation location, { double? zoom }) {
    _mapController.move(LatLng(location.latitude, location.longitude), zoom ?? _mapController.camera.zoom);
  }

  List<LatLng> createCirclePoints(LatLng center, double radiusInMeters) {
    const int circlePointsCount = 60; // Adjust for desired polygon density
    List<LatLng> points = [];

    for (int i = 0; i < circlePointsCount; i++) {
      double angle = (360 / circlePointsCount) * i;
      // Convert angle and radius to radians
      double radians = angle * (math.pi / 180);
      double earthRadius = 6378137.0; // Radius of the Earth in meters
      double latRadian = center.latitude * (math.pi / 180);

      double dx = radiusInMeters * math.cos(radians);
      double dy = radiusInMeters * math.sin(radians);
      double lat = center.latitude + (dy / earthRadius) * (180 / math.pi);
      double lon = center.longitude + (dx / (earthRadius * math.cos(latRadian))) * (180 / math.pi);

      points.add(LatLng(lat, lon));
    }

    return points;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Coffee Background Geolocation',
          style: TextStyle(color: yellowColor),
        ),
        backgroundColor: primaryColor,
      ),
      body: SafeArea(
        child: FlutterMap(
          mapController: _mapController,
          options: const MapOptions(
            initialCenter: LatLng(0, 0), // Default center
            initialZoom: 13.0,
          ),
          children: [
            TileLayer(
              urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
              subdomains: const ['a', 'b', 'c'],
            ),
            _showFences ?
            PolygonLayer(
              polygons: _geofenceMarkers,
            ) : const SizedBox(),
            PolylineLayer(polylines: [
              Polyline(
                points: _pathlinePositions.map((e) => LatLng(e.latitude, e.longitude)).toList(),
                strokeWidth: 4.0,
                color: Colors.green,
              ),
            ]),
            MarkerLayer(
              markers: _pathlinePositions.map((e) => _buildMarker(
                latitude: e.latitude, 
                longitude: e.longitude, 
                heading: e.heading, 
                color: Colors.grey
              )).toList(),
            ),
            MarkerLayer(
              markers: _isTracking ? _currentMarkers : _updatedLocationMarkers,
            ),
            MarkerLayer(
              markers: _isTracking ? _updatedLocationMarkers : _currentMarkers,
            ),
            Stack(
              children: [
                Positioned(
                  left: 10,
                  bottom: 10,
                  child: _currentLocation != null ? _buildLocationCard() : const SizedBox(),
                ),
                Positioned(
                  top: 20,
                  right: 10,
                  child: Column(
                    children: [
                      FloatingActionButton(
                        onPressed: _getCurrentLocation,
                        backgroundColor: _isLoading ? Colors.grey : primaryColor,
                        child: _isLoading ? 
                        const CircularProgressIndicator(color: Colors.white) : 
                        const Icon(Icons.my_location, color: Colors.white,),
                      ),
                      const SizedBox(height: 20),
                      FloatingActionButton(
                        onPressed: _toggleTracking,
                        backgroundColor: Colors.white,
                        child: Icon(
                          _isTracking ? Icons.stop : Icons.play_arrow,
                          color: _isTracking ? Colors.red : accentColor,
                        ),
                      ),
                      const SizedBox(height: 20),
                      FloatingActionButton(
                        onPressed: _openDialogSettings,
                        backgroundColor: Colors.white,
                        child: const Icon(Icons.settings,
                          color: primaryColor,
                        ),
                      ),
                      const SizedBox(height: 20),
                      FloatingActionButton(
                        onPressed: () {
                          if(_currentLocation == null) {
                            return;
                          }

                          _centerMap(_currentLocation!);
                        },
                        backgroundColor: yellowColor,
                        child: const Icon(Icons.center_focus_strong,
                          color: primaryColor,
                        ),
                      ),
                     ],
                  )
                ),
                Positioned(
                  top: 10,
                  left: 10,
                  child: _buildInitButton()
                )
              ]
            )
          ],
        ),
      )
    );
  }

  Widget _buildInitButton() {
    if(_isInitializing) {
      return const CircularProgressIndicator(
        color: primaryColor,
      );
    }

    if(_initSucceeded) {
      return ElevatedButton(
        onPressed: null,
        style: ElevatedButton.styleFrom(
          backgroundColor: Colors.green,
          disabledForegroundColor: Colors.green,
        ),
        child: const Text(
          'Init Succeeded',
          style: TextStyle(
            color: primaryColor
          ),
        ),
      );
    }
    
    return ElevatedButton(
      onPressed: () {
        _init();
      },
      style: ElevatedButton.styleFrom(
        backgroundColor: accentColor,
      ),
      child: const Text(
        'Initialize Coffee',
        style: TextStyle(
          color: Colors.white
        ),
      ),
    );
  }

  Marker _buildMarker({
    required double latitude,
    required double longitude,
    required double heading,
    Color color = primaryColor
  }) {
     return Marker(
      width: 20.0,
      height: 20.0,
      point: LatLng(latitude, longitude),
      child: Container(
        decoration: BoxDecoration(
          color: Colors.white.withOpacity(0.8),
          shape: BoxShape.circle,
          border: Border.all(color: color, width: 2),
        ),
        child: Transform.rotate(
          angle: heading * (math.pi / 180),
          child: Icon(Icons.navigation, color: color, size: 14),
        ),
      ),
    );
  }

  Widget _buildLocationCard() {
    String title = "Current Location";

    if(_isTracking && _updatedLocationMarkers.isNotEmpty) {
      title =  "Tracking Location";
    }
  
    if(_currentLocation == null) {
      return const SizedBox();
    }

    return Container(
      padding: const EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Colors.black.withOpacity(0.6),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.all(8), // Set your desired padding
            color: Colors.black.withOpacity(0.6), // Set the background color
            child: GestureDetector(
              onTap: () {
                setState(() {
                  _isLocationCardExpanded = !_isLocationCardExpanded;
                });
              },
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    title,
                    style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
                  ),
                  Icon(
                    _isLocationCardExpanded ? Icons.expand_less : Icons.expand_more,
                    color: Colors.white,
                  )
                ],
              ),
            )
          ),
          SizedBox(height: _isLocationCardExpanded ? 10 : 0),
          if (_isLocationCardExpanded)
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _locationDetailRow('Latitude', _currentLocation!.latitude.toString()),
              _locationDetailRow('Longitude', _currentLocation!.longitude.toString()),
              _locationDetailRow('Accuracy', _currentLocation!.accuracy.toString()),
              _locationDetailRow('Altitude', _currentLocation!.altitude.toString()),
              _locationDetailRow('Heading', _currentLocation!.heading.toString()),
              _locationDetailRow('Speed', _currentLocation!.speed.toString()),
              _locationDetailRow('SpeeedAccuracy', _currentLocation!.speedAccuracy.toString()),
              _locationDetailRow('BatteryLevel', _currentLocation!.batteryLevel.toString()),
              _locationDetailRow('deviceVersion', _currentLocation!.deviceVersion.toString()),
              _locationDetailRow('deviceName', _currentLocation!.deviceName.toString()),
              _locationDetailRow('deviceType', _currentLocation!.deviceType.toString()),
              _locationDetailRow('dateTime', _currentLocation!.dateTime.toString()),
              _locationDetailRow('connectionType', _currentLocation!.internetConnectionType.toString())
            ],
          ),
        ]
      )
    );
  }

  Widget _locationDetailRow(String property, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2.0),
      child: Row(
        children: [
          Text('$property: ', style: const TextStyle(color: Colors.white)),
          Text(value, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
        ],
      ),
    );
  }
}
2
likes
140
points
17
downloads

Publisher

unverified uploader

Weekly Downloads

The Coffee Flutter Geolocation plugin offers robust and efficient location tracking capabilities for Flutter applications, especially designed for background operation.

Homepage

Documentation

API reference

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on fl_coffee_geolocation

Packages that implement fl_coffee_geolocation