geofence_foreground_service 1.1.6 copy "geofence_foreground_service: ^1.1.6" to clipboard
geofence_foreground_service: ^1.1.6 copied to clipboard

A Flutter project that creates a foreground service to handle geofencing.

example/lib/main.dart

import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:geofence_foreground_service/constants/geofence_event_type.dart';
import 'package:geofence_foreground_service/exports.dart';
import 'package:geofence_foreground_service/geofence_foreground_service.dart';
import 'package:geofence_foreground_service/models/notification_icon_data.dart';
import 'package:geofence_foreground_service/models/zone.dart';
import 'package:permission_handler/permission_handler.dart';

@pragma('vm:entry-point')
void callbackDispatcher() async {
  GeofenceForegroundService().handleTrigger(
    backgroundTriggerHandler: (zoneID, triggerType) {
      log(zoneID, name: 'zoneID');

      if (triggerType == GeofenceEventType.enter) {
        log('enter', name: 'triggerType');
      } else if (triggerType == GeofenceEventType.exit) {
        log('exit', name: 'triggerType');
      } else if (triggerType == GeofenceEventType.dwell) {
        log('dwell', name: 'triggerType');
      } else {
        log('unknown', name: 'triggerType');
      }

      return Future.value(true);
    },
  );
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  static final LatLng _londonCityCenter = LatLng.degree(51.509865, -0.118092);
  static final List<LatLng> _timesSquarePolygon = [
    LatLng.degree(40.758078, -73.985640),
    LatLng.degree(40.757983, -73.985417),
    LatLng.degree(40.757881, -73.985493),
    LatLng.degree(40.757956, -73.985688),
  ];

  bool _hasServiceStarted = false;

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

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

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

  Future<void> _handleAppResumedPermissionCheck() async {
    final bgPermission = await Permission.locationAlways.status;
    if (!bgPermission.isGranted) {
      if (_hasServiceStarted) {
        await GeofenceForegroundService().stopGeofencingService();
        if (mounted) setState(() => _hasServiceStarted = false);
      }
      if (mounted) _showPermissionDialog();
    }
  }

  void _showPermissionDialog() {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        title: const Text('Permission Required'),
        content: const Text('Background location is required for geofencing.'),
        actions: [
          TextButton(
            onPressed: () => openAppSettings(),
            child: const Text('Open Settings'),
          ),
        ],
      ),
    );
  }

  void _showPermissionDeniedMessage() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content:
              Text('Permissions not granted. Please enable them in settings.'),
          backgroundColor: Colors.red,
        ),
      );
    });
  }

  void _showServiceStartFailedMessage() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            'Geofencing service could not be started. Check permissions and try again.',
          ),
          backgroundColor: Colors.red,
        ),
      );
    });
  }

  Future<bool> checkAndRequestPermissions() async {
    var locationStatus = await Permission.location.status;
    var locationAlwaysStatus = await Permission.locationAlways.status;
    var notificationStatus = await Permission.notification.status;

    if (!locationStatus.isGranted) {
      locationStatus = await Permission.location.request();
    }

    if (!locationAlwaysStatus.isGranted) {
      locationAlwaysStatus = await Permission.locationAlways.request();
      if (!locationAlwaysStatus.isGranted) {
        await openAppSettings();
        return false;
      }
    }

    if (!notificationStatus.isGranted) {
      notificationStatus = await Permission.notification.request();
    }

    return locationStatus.isGranted &&
        locationAlwaysStatus.isGranted &&
        notificationStatus.isGranted;
  }

  /// Returns whether the foreground geofencing service is running and ready.
  Future<bool> _startServiceIfNeeded() async {
    if (_hasServiceStarted) {
      return true;
    }
    final started = await GeofenceForegroundService().startGeofencingService(
      contentTitle: 'Test app is running in the background',
      contentText:
          'Test app will be running to ensure seamless integration with ops team',
      notificationChannelId: 'com.app.geofencing_notifications_channel',
      serviceId: 525600,
      isInDebugMode: true,
      notificationIconData: const NotificationIconData(
        resType: ResourceType.mipmap,
        resPrefix: ResourcePrefix.ic,
        name: 'launcher',
      ),
      callbackDispatcher: callbackDispatcher,
    );
    if (mounted) {
      setState(() => _hasServiceStarted = started);
    } else {
      _hasServiceStarted = started;
    }
    log(started.toString(), name: 'hasServiceStarted');
    return started;
  }

  Future<void> _createLondonGeofence() async {
    final hasPermissions = await checkAndRequestPermissions();
    if (!hasPermissions) {
      _showPermissionDeniedMessage();
      return;
    }

    final serviceReady = await _startServiceIfNeeded();
    if (!serviceReady) {
      _showServiceStartFailedMessage();
      return;
    }

    await GeofenceForegroundService().addGeofenceZone(
      zone: Zone(
        id: 'zone#1_id',
        radius: 1000,
        coordinates: [_londonCityCenter],
        notificationResponsivenessMs: 15000,
        triggers: [
          GeofenceEventType.dwell,
          GeofenceEventType.enter,
          GeofenceEventType.exit
        ],
        expirationDuration: const Duration(days: 1),
        dwellLoiteringDelay: const Duration(hours: 1),
        initialTrigger: GeofenceEventType.enter,
      ),
    );
  }

  Future<void> _createTimesSquarePolygonGeofence() async {
    final hasPermissions = await checkAndRequestPermissions();
    if (!hasPermissions) {
      _showPermissionDeniedMessage();
      return;
    }

    final serviceReady = await _startServiceIfNeeded();
    if (!serviceReady) {
      _showServiceStartFailedMessage();
      return;
    }

    await GeofenceForegroundService().addGeofenceZone(
      zone: Zone(
        id: 'zone#2_id',
        radius: 10000,
        coordinates: _timesSquarePolygon,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                  onPressed: _createLondonGeofence,
                  child: const Text('Create Circular London Geofence')),
              const SizedBox(height: 30),
              ElevatedButton(
                  onPressed: _createTimesSquarePolygonGeofence,
                  child: const Text('Create Polygon Times Square Geofence')),
            ],
          ),
        ),
      ),
    );
  }
}
38
likes
160
points
355
downloads

Documentation

API reference

Publisher

verified publisher525k.io

Weekly Downloads

A Flutter project that creates a foreground service to handle geofencing.

Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

flutter, latlng, plugin_platform_interface

More

Packages that depend on geofence_foreground_service

Packages that implement geofence_foreground_service