feat(app): stabilize recommendation flow

This commit is contained in:
JiWoong Sul
2025-12-01 17:22:21 +09:00
parent d05e378569
commit c1aa16c521
12 changed files with 422 additions and 260 deletions

View File

@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:lunchpick/core/utils/app_logger.dart';
@@ -69,13 +70,31 @@ final currentLocationProvider = FutureProvider<Position?>((ref) async {
});
/// 위치 스트림 Provider
final locationStreamProvider = StreamProvider<Position>((ref) {
return Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 10, // 10미터 이상 이동 시 업데이트
),
);
final locationStreamProvider = StreamProvider<Position>((ref) async* {
if (kIsWeb) {
AppLogger.debug('[location] web detected, emit fallback immediately');
yield defaultPosition();
return;
}
final status = await Permission.location.status;
if (!status.isGranted) {
AppLogger.debug('[location] permission not granted, emit fallback');
yield defaultPosition();
return;
}
try {
yield* Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 10, // 10미터 이상 이동 시 업데이트
),
);
} catch (_) {
AppLogger.error('[location] position stream failed, emit fallback');
yield defaultPosition();
}
});
/// 초기 3초 내 위치를 가져오지 못하면 기본 좌표를 우선 반환하고,
@@ -83,20 +102,30 @@ final locationStreamProvider = StreamProvider<Position>((ref) {
final currentLocationWithFallbackProvider = StreamProvider<Position>((
ref,
) async* {
final initial = await Future.any([
ref
.watch(currentLocationProvider.future)
.then((pos) => pos ?? defaultPosition()),
Future<Position>.delayed(
const Duration(seconds: 3),
() => defaultPosition(),
),
]).catchError((_) => defaultPosition());
AppLogger.debug('[location] emit fallback immediately (safe start)');
// 웹/권한 거부 상황에서는 즉시 기본 좌표를 먼저 흘려보내 리스트 로딩을 막는다.
final fallback = defaultPosition();
yield fallback;
yield initial;
final initial = await Future.any([
ref.watch(currentLocationProvider.future).then((pos) => pos ?? fallback),
Future<Position>.delayed(const Duration(seconds: 3), () => fallback),
]).catchError((_) => fallback);
if (initial.latitude != fallback.latitude ||
initial.longitude != fallback.longitude) {
AppLogger.debug(
'[location] resolved initial position: '
'${initial.latitude}, ${initial.longitude}',
);
yield initial;
} else {
AppLogger.debug('[location] initial resolved to fallback');
}
yield* ref.watch(locationStreamProvider.stream).handleError((_) {
// 스트림 오류는 무시하고 마지막 위치를 유지
AppLogger.error('[location] stream error, keeping last position');
});
});