smart_location_plus 0.2.0 copy "smart_location_plus: ^0.2.0" to clipboard
smart_location_plus: ^0.2.0 copied to clipboard

A unified Flutter location package combining geolocator, geocoding, and native geofencing into one simple, powerful API. Battery-efficient, platform-native geofencing. Zero boilerplate, full customization.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:smart_location_plus/smart_location_plus.dart';

void main() => runApp(const SmartLocationExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SmartLocation Example',
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      home: const ExampleHome(),
    );
  }
}

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

  @override
  State<ExampleHome> createState() => _ExampleHomeState();
}

class _ExampleHomeState extends State<ExampleHome> {
  String _output = 'Tap a button to try a feature.';
  bool _loading = false;

  void _log(String msg) => setState(() => _output = msg);
  void _setLoading(bool v) => setState(() => _loading = v);

  // ── 1. locateOnce ──────────────────────────────────────────────────────

  Future<void> _locateOnce() async {
    _setLoading(true);
    try {
      final loc = await SmartLocation.locateOnce();
      _log('📍 locateOnce()\n'
          'Lat: ${loc.latitude}\n'
          'Lng: ${loc.longitude}\n'
          'Address: ${loc.address ?? "not resolved"}\n'
          'City: ${loc.city}\n'
          'Accuracy: ${loc.accuracy.toStringAsFixed(1)} m');
    } catch (e) {
      _log('❌ Error: $e');
    } finally {
      _setLoading(false);
    }
  }

  // ── 1b. locateOnce — high accuracy, Hindi address ──────────────────────

  Future<void> _locateOnceCustom() async {
    _setLoading(true);
    try {
      final loc = await SmartLocation.locateOnce(
        locationConfig: LocationConfig(
          accuracy: LocationAccuracy.best,
          timeLimit: const Duration(seconds: 10),
        ),
        geocodingConfig: const GeocodingConfig(
          localeIdentifier: 'hi',
          maxResults: 1,
        ),
      );
      _log('📍 locateOnce() — high accuracy, Hindi\n'
          'Lat: ${loc.latitude}\n'
          'Lng: ${loc.longitude}\n'
          'Address (hi): ${loc.address ?? "—"}');
    } catch (e) {
      _log('❌ Error: $e');
    } finally {
      _setLoading(false);
    }
  }

  // ── 2. locateAndWatch ─────────────────────────────────────────────────

  Stream<LocationResult>? _watchStream;

  void _startWatch() {
    _watchStream = SmartLocation.locateAndWatch(
      locationConfig: LocationConfig(
        accuracy: LocationAccuracy.high,
        distanceFilter: 5,
      ),
      geocodingConfig: const GeocodingConfig(resolveAddress: false),
    );
    _watchStream!.listen(
      (loc) => _log('🔄 locateAndWatch()\n'
          'Lat: ${loc.latitude}\n'
          'Lng: ${loc.longitude}\n'
          'Speed: ${(loc.speed * 3.6).toStringAsFixed(1)} km/h'),
      onError: (e) => _log('❌ Stream error: $e'),
    );
    _log('▶️ Watching location... move device to see updates.');
  }

  // ── 3. Last known location ─────────────────────────────────────────────

  Future<void> _lastKnown() async {
    _setLoading(true);
    try {
      final loc = await SmartLocation.getLastKnownLocation();
      if (loc == null) {
        _log('⚠️ No last known location cached.');
      } else {
        _log('🕓 Last Known Location\n'
            'Lat: ${loc.latitude}\n'
            'Lng: ${loc.longitude}\n'
            'Time: ${loc.timestamp}');
      }
    } catch (e) {
      _log('❌ Error: $e');
    } finally {
      _setLoading(false);
    }
  }

  // ── 4. Reverse geocode ─────────────────────────────────────────────────

  Future<void> _reverseGeocode() async {
    _setLoading(true);
    try {
      final result = await SmartLocation.reverseGeocode(
        latitude: 18.9220,
        longitude: 72.8347,
        config: const GeocodingConfig(localeIdentifier: 'en_IN'),
      );
      _log('🗺️ reverseGeocode(18.9220, 72.8347)\n'
          'Address: ${result.address}\n'
          'Street: ${result.street}\n'
          'City: ${result.city}\n'
          'State: ${result.state}\n'
          'Country: ${result.country}\n'
          'PIN: ${result.postalCode}');
    } catch (e) {
      _log('❌ Error: $e');
    } finally {
      _setLoading(false);
    }
  }

  // ── 5. Forward geocode ─────────────────────────────────────────────────

  Future<void> _forwardGeocode() async {
    _setLoading(true);
    try {
      final result = await SmartLocation.forwardGeocode(
        address: 'Gateway of India, Mumbai',
        config: const GeocodingConfig(maxResults: 2),
      );
      _log('🔍 forwardGeocode("Gateway of India, Mumbai")\n'
          'Lat: ${result.latitude}\n'
          'Lng: ${result.longitude}\n'
          'Total results: ${result.allResults.length}');
    } catch (e) {
      _log('❌ Error: $e');
    } finally {
      _setLoading(false);
    }
  }

  // ── 6. Geofencing ──────────────────────────────────────────────────────

  Future<void> _startGeofence() async {
    _setLoading(true);
    try {
      await SmartLocation.watchZone(
        zones: [
          LocationZone(
            id: 'home',
            latitude: 19.2183,
            longitude: 73.0587,
            config: const GeofenceConfig(
              radius: 100,
              loiteringDelay: 30000,
              listenFor: {
                GeofenceEvent.enter,
                GeofenceEvent.exit,
                GeofenceEvent.dwell,
              },
            ),
            onEnter: (zone) => _log('✅ Entered zone: ${zone.id}'),
            onExit: (zone) => _log('🚪 Exited zone: ${zone.id}'),
            onDwell: (zone) => _log('⏳ Dwelling in zone: ${zone.id}'),
          ),
          LocationZone(
            id: 'office',
            latitude: 19.3000,
            longitude: 73.0700,
            config: const GeofenceConfig(radius: 200),
            onEnter: (zone) => _log('🏢 Arrived at office!'),
            onExit: (zone) => _log('👋 Left office'),
          ),
        ],
        globalConfig: GeofenceConfig(
          useActivityRecognition: true,
          allowMockLocations: true,
          printDevLog: true,
          onError: (e) => _log('❌ Geofence error: $e'),
        ),
      );
      _log('📡 Geofencing started!\n'
          'Monitoring ${SmartLocation.activeZones.length} zones:\n'
          '• home (r: 100m)\n'
          '• office (r: 200m)');
    } catch (e) {
      _log('❌ Error: $e');
    } finally {
      _setLoading(false);
    }
  }

  Future<void> _stopGeofence() async {
    await SmartLocation.stopWatchingZones();
    _log('🛑 Geofencing stopped.');
  }

  // ── 7. Utilities ───────────────────────────────────────────────────────

  void _distance() {
    final metres = SmartLocation.distanceBetween(
      startLat: 19.2183, startLng: 73.0587,
      endLat: 19.3000,   endLng: 73.0700,
    );
    final km = (metres / 1000).toStringAsFixed(2);
    _log('📏 distanceBetween()\n'
        '${metres.toStringAsFixed(0)} metres (~$km km)\n'
        'From: home (19.2183, 73.0587)\n'
        'To: office (19.3000, 73.0700)');
  }

  void _bearing() {
    final deg = SmartLocation.bearingBetween(
      startLat: 19.2183, startLng: 73.0587,
      endLat: 19.3000,   endLng: 73.0700,
    );
    _log('🧭 bearingBetween()\n'
        '${deg.toStringAsFixed(1)}° from home to office');
  }

  void _isInside() {
    final inside = SmartLocation.isInsideZone(
      latitude: 19.2185,
      longitude: 73.0589,
      zone: LocationZone(
        id: 'home',
        latitude: 19.2183,
        longitude: 73.0587,
        config: const GeofenceConfig(radius: 100),
      ),
    );
    _log('📌 isInsideZone()\n'
        'Point (19.2185, 73.0589) is ${inside ? "INSIDE ✅" : "OUTSIDE ❌"} '
        'the home zone (r: 100m)');
  }

  // ── 8. Permissions ──────────────────────────────────────────────────────

  Future<void> _checkPermission() async {
    final status = await SmartLocation.checkPermission();
    _log('🔐 Permission: $status');
  }

  // ── Build ───────────────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SmartLocation Demo'),
        centerTitle: true,
      ),
      body: Column(
        children: [
          // Output box
          Container(
            width: double.infinity,
            margin: const EdgeInsets.all(16),
            padding: const EdgeInsets.all(14),
            decoration: BoxDecoration(
              color: Colors.indigo.shade50,
              borderRadius: BorderRadius.circular(12),
              border: Border.all(color: Colors.indigo.shade200),
            ),
            child: _loading
                ? const Center(child: CircularProgressIndicator())
                : Text(
                    _output,
                    style: const TextStyle(
                      fontFamily: 'monospace',
                      fontSize: 13,
                      height: 1.6,
                    ),
                  ),
          ),

          // Buttons
          Expanded(
            child: ListView(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              children: [
                _section('📍 Location'),
                _btn('locateOnce() — defaults', _locateOnce),
                _btn('locateOnce() — high accuracy + Hindi', _locateOnceCustom),
                _btn('locateAndWatch() — start stream', _startWatch),
                _btn('getLastKnownLocation()', _lastKnown),

                _section('🗺️ Geocoding'),
                _btn('reverseGeocode(18.92, 72.83)', _reverseGeocode),
                _btn('forwardGeocode("Gateway of India")', _forwardGeocode),

                _section('📡 Geofencing'),
                _btn('watchZone() — start (home + office)', _startGeofence),
                _btn('stopWatchingZones()', _stopGeofence),

                _section('🛠️ Utilities'),
                _btn('distanceBetween(home → office)', _distance),
                _btn('bearingBetween(home → office)', _bearing),
                _btn('isInsideZone()', _isInside),

                _section('🔐 Permissions'),
                _btn('checkPermission()', _checkPermission),
                _btn('openLocationSettings()', () async {
                  await SmartLocation.openLocationSettings();
                }),
                _btn('openAppSettings()', () async {
                  await SmartLocation.openAppSettings();
                }),

                const SizedBox(height: 24),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _section(String title) => Padding(
        padding: const EdgeInsets.only(top: 16, bottom: 4),
        child: Text(
          title,
          style: TextStyle(
            fontSize: 13,
            fontWeight: FontWeight.bold,
            color: Colors.indigo.shade700,
            letterSpacing: 0.5,
          ),
        ),
      );

  Widget _btn(String label, VoidCallback onTap) => Padding(
        padding: const EdgeInsets.only(bottom: 8),
        child: FilledButton.tonal(
          onPressed: _loading ? null : onTap,
          child: Align(
            alignment: Alignment.centerLeft,
            child: Text(label),
          ),
        ),
      );
}
2
likes
140
points
24
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A unified Flutter location package combining geolocator, geocoding, and native geofencing into one simple, powerful API. Battery-efficient, platform-native geofencing. Zero boilerplate, full customization.

Repository (GitHub)
View/report issues

Topics

#location #geocoding #geofencing #gps #tracker

License

MIT (license)

Dependencies

flutter, geocoding, geolocator, native_geofence

More

Packages that depend on smart_location_plus