Blog スタッフブログ

Android Flutter iOS システム開発

[Flutter]位置情報をバックグラウンドで取得する

こんにちは、株式会社MIXシステム開発担当のBloomです。

早速本題のFlutterで位置情報をバックグラウンドで取得する手順について、

お仕事の中で得た知見を共有させていただきたいと思います。

導入

まずはlocationパッケージを導入しましょう。pubコマンドを利用します。

flutter pub add location

さらに位置情報を取得するための権限について記述しましょう。AndroidManifest.xmlおよびinfo.plistへ追記します。


    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
	<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
	<string>バックグラウンドおよびアプリを使用中にGPS情報を利用する理由を書きます。</string>
	<key>NSLocationAlwaysUsageDescription</key>
	<string>バックグラウンドおよびアプリを使用中にGPS情報を利用する理由を書きます。</string>
	<key>NSLocationWhenInUseUsageDescription</key>
	<string>アプリを使用中にGPS情報を利用する理由を書きます。</string>
	<key>UIBackgroundModes</key>
	<array>
		<string>location</string>
		<string>fetch</string>
	</array>

実装

では、いよいよ位置情報を取得しましょう。今回は位置情報を取得するためのシングルトンクラスを作っています。

class LocationManager {
  // シングルトンインスタンス
  static final LocationManager _instance = LocationManager._internal();

  factory LocationManager() => _instance;

  LocationManager._internal();

  Future<void> initialize() async {}

  Future<bool> startLocationTracking() async {
    if (_locationChangedListen != null) {
      finishLocationTracking();
    }
    Location location = new Location();
    bool _serviceEnabled;
    PermissionStatus _permissionGranted;

    _serviceEnabled = await location.serviceEnabled();
    if (!_serviceEnabled) {
      _serviceEnabled = await location.requestService();
      if (!_serviceEnabled) {
        debugPrint("service not enabled");
        return false;
      }
    }

    _permissionGranted = await location.hasPermission();
    if (_permissionGranted == PermissionStatus.denied) {
      _permissionGranted = await location.requestPermission();
      if (_permissionGranted != PermissionStatus.granted) {
        debugPrint("permisson not enabled");
        return false;
      }
    }

    _serviceEnabled = await enableBackgroundMode(location);
    if (!_serviceEnabled) {
      debugPrint("background service not enabled");
    }
    _locationChangedListen = location.onLocationChanged.listen((LocationData result) async {
      debugPrint("lat: ${result.latitude}");
      debugPrint("lng: ${result.longitude}");
      debugPrint(result.toString());
    });
    return true;
  }
  void finishLocationTracking() {
    _locationChangedListen?.cancel();
    _locationChangedListen = null;
  }

  Future<bool> reloadLocationTracking() async {
    finishLocationTracking();
    return await startLocationTracking();
  }
  // https://stackoverflow.com/questions/70545730/flutter-location-unhandled-exception-platformexceptionpermission-denied-neve
  Future<bool> enableBackgroundMode(Location location) async {
    bool _bgModeEnabled = await location.isBackgroundModeEnabled();
    if (_bgModeEnabled) {
      return true;
    } else {
      try {
        await location.enableBackgroundMode();
      } catch (e) {
        debugPrint(e.toString());
      }
      try {
        _bgModeEnabled = await location.enableBackgroundMode();
      } catch (e) {
        debugPrint(e.toString());
      }
      print(_bgModeEnabled);
      return _bgModeEnabled;
    }
  }
  

ここで、enableBackgroundModeメソッド内においてlocation.enableBackgroundMode()を複数回実行しています。これはstackoverflowで報告のあった挙動を回避するために設定しています。

あとは位置情報の取得を行いたいタイミングでLocationManager().startLocationTracking();と実行するだけで位置情報を取得できます。良かったですね。

参考文献

location | Flutter package – pub.dev

flutter location , Unhandled Exception – stackoverflow