본문 바로가기
Flutter/Study

위치 추적앱 안드로이드와 iOS, 한 코드로 통합할 수 있을까?

by Maccrey Coding 2025. 2. 12.
반응형

Flutter에서는 dart:io의 Platform.isAndroid와 Platform.isIOS를 사용하여 플랫폼별로 다른 로직을 실행할 수 있습니다.
즉, 하나의 코드에서 안드로이드와 iOS의 백그라운드 GPS 로직을 구분해서 실행할 수 있습니다.

🏗️ 플랫폼별 구현 방식

기능안드로이드 (Android)iOS

백그라운드 실행 방식 Foreground Service + WorkManager Background Modes + Significant Location Change
권한 요청 ACCESS_BACKGROUND_LOCATION 필요 NSLocationAlwaysUsageDescription 필요
위치 업데이트 방법 getPositionStream() 사용 getPositionStream() 또는 Region Monitoring 사용
백그라운드 유지 Foreground Service로 강제 유지 가능 시스템이 자동으로 종료할 수 있음
배터리 절약 서비스 유지로 배터리 사용 높음 이동 거리를 기반으로 업데이트

📌 안드로이드 & iOS 통합 코드 (MVVM + Riverpod)

플랫폼에 따라 WorkManager + Foreground Service를 사용할지, Background Modes + Significant Location Change를 사용할지를 분기 처리해야 합니다.

1️⃣ 권한 요청 함수

import 'dart:io';
import 'package:geolocator/geolocator.dart';

class LocationPermissions {
  static Future<bool> requestPermissions() async {
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('위치 서비스가 비활성화됨');
    }

    LocationPermission permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('위치 권한 거부됨');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      return Future.error('위치 권한이 영구적으로 거부됨. 설정에서 변경해야 함.');
    }

    return true;
  }
}

설명

  • Geolocator.checkPermission()을 사용하여 플랫폼에 관계없이 위치 권한을 확인하고 요청
  • iOS의 경우 NSLocationAlwaysUsageDescription이 필요
  • 안드로이드는 ACCESS_BACKGROUND_LOCATION이 추가 필요

2️⃣ 플랫폼별 GPS 업데이트 방식 선택

import 'dart:io';
import 'package:geolocator/geolocator.dart';
import 'package:workmanager/workmanager.dart';

class LocationService {
  static void startTracking() {
    if (Platform.isAndroid) {
      _startAndroidTracking();
    } else if (Platform.isIOS) {
      _startIOSTracking();
    }
  }

  // ✅ 안드로이드 - WorkManager + Foreground Service
  static void _startAndroidTracking() {
    Workmanager().initialize(
      callbackDispatcher,
      isInDebugMode: true,
    );
    Workmanager().registerPeriodicTask(
      "1",
      "fetchLocation",
      frequency: Duration(minutes: 15), // 15분마다 실행
    );
  }

  // ✅ iOS - 백그라운드 위치 업데이트
  static void _startIOSTracking() {
    Geolocator.getPositionStream(
      locationSettings: LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 50, // 50m 이상 이동 시 업데이트
      ),
    ).listen((Position position) {
      print('iOS 위치 업데이트: ${position.latitude}, ${position.longitude}');
      // Hive 또는 서버에 저장
    });
  }
}

// ✅ 안드로이드 백그라운드에서 실행될 WorkManager 콜백
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    Position position = await Geolocator.getCurrentPosition();
    print('안드로이드 위치 업데이트: ${position.latitude}, ${position.longitude}');
    return Future.value(true);
  });
}

설명

  • Platform.isAndroid일 경우 WorkManager를 활용한 Foreground Service 실행
  • Platform.isIOS일 경우 백그라운드 위치 업데이트 (Significant Location Change) 방식 실행
  • WorkManager를 이용해 15분마다 위치를 업데이트 (안드로이드의 백그라운드 제한 우회)

3️⃣ MVVM + Riverpod으로 상태 관리

위치 데이터를 Riverpod을 사용해 관리하고, Hive에 저장합니다.

(1) 모델 생성

import 'package:hive/hive.dart';

@HiveType(typeId: 1)
class LocationModel {
  @HiveField(0)
  final double latitude;
  @HiveField(1)
  final double longitude;
  @HiveField(2)
  final DateTime timestamp;

  LocationModel({required this.latitude, required this.longitude, required this.timestamp});
}

 (2) ViewModel (위치 관리)

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive/hive.dart';
import 'package:geolocator/geolocator.dart';

final locationProvider = StateNotifierProvider<LocationNotifier, List<LocationModel>>((ref) {
  return LocationNotifier();
});

class LocationNotifier extends StateNotifier<List<LocationModel>> {
  LocationNotifier() : super([]);

  Future<void> addLocation(Position position) async {
    final location = LocationModel(
      latitude: position.latitude,
      longitude: position.longitude,
      timestamp: DateTime.now(),
    );

    final box = await Hive.openBox<LocationModel>('locationBox');
    box.add(location);

    state = [...state, location];
  }
}

설명

  • 안드로이드와 iOS 모두에서 위치 데이터를 Hive에 저장하고 Riverpod으로 관리
  • 상태가 변경될 때마다 UI 업데이트 가능

하나의 코드로 안드로이드 & iOS 대응

🎯 방법 정리

기능안드로이드iOS

권한 요청 ACCESS_BACKGROUND_LOCATION NSLocationAlwaysUsageDescription
백그라운드 위치 추적 WorkManager + Foreground Service Significant Location Change
GPS 업데이트 getPositionStream() + WorkManager getPositionStream()
배터리 최적화 Foreground 유지 백그라운드 위치 변경 감지

코드 통합 방법

  1. Platform.isAndroid와 Platform.isIOS로 분기 처리
  2. 안드로이드는 WorkManager + Foreground Service 활용
  3. iOS는 Significant Location Change 활용하여 배터리 절약
  4. Riverpod과 Hive를 사용해 데이터 관리

이제 하나의 코드에서 안드로이드 & iOS를 모두 지원하는 조깅 앱을 만들 수 있습니다! 

 

구독!! 공감과 댓글,

광고 클릭은 저에게 큰 힘이 됩니다.

 

Starting Google Play App Distribution! "Tester Share" for Recruiting 20 Testers for a Closed Test.

 

Tester Share [테스터쉐어] - Google Play 앱

Tester Share로 Google Play 앱 등록을 단순화하세요.

play.google.com

 

반응형