Fix beacon scanning reliability with warmup delay

- Add flutterBeacon.close before initializing to clear stale state
- Add 2-second Bluetooth warmup delay for radio initialization
- Extend scan duration to 5 seconds for reliable detection
- Ensure ACCESS_BACKGROUND_LOCATION permission is included

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-01-27 00:39:11 -08:00
parent c71432e91d
commit 0adb172b9e
3 changed files with 26 additions and 22 deletions

View file

@ -18,9 +18,8 @@
<!-- Allow basic foreground service (needed by beacon library for scanning) --> <!-- Allow basic foreground service (needed by beacon library for scanning) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Remove location-specific foreground service and background location (these require video documentation) --> <!-- Background location required for beacon scanning -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" tools:node="remove" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove" />
<application <application
android:label="Payfrit" android:label="Payfrit"

View file

@ -30,22 +30,19 @@ class PayfritApp extends StatelessWidget {
ChangeNotifierProvider<AppState>(create: (_) => AppState()), ChangeNotifierProvider<AppState>(create: (_) => AppState()),
], ],
child: Api.isDev child: Api.isDev
? Banner( ? Directionality(
message: "DEV", textDirection: TextDirection.ltr,
location: BannerLocation.topEnd, child: Banner(
color: Colors.orange, message: "DEV",
textStyle: const TextStyle( location: BannerLocation.topEnd,
fontSize: 10, color: Colors.orange,
fontWeight: FontWeight.bold, child: MaterialApp(
color: Colors.white, scaffoldMessengerKey: rootScaffoldMessengerKey,
), debugShowCheckedModeBanner: false,
layoutDirection: TextDirection.ltr, title: "Payfrit DEV",
child: MaterialApp( initialRoute: AppRoutes.splash,
scaffoldMessengerKey: rootScaffoldMessengerKey, routes: AppRoutes.routes,
debugShowCheckedModeBanner: false, ),
title: "Payfrit DEV",
initialRoute: AppRoutes.splash,
routes: AppRoutes.routes,
), ),
) )
: MaterialApp( : MaterialApp(

View file

@ -246,11 +246,18 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
// Initialize beacon scanning // Initialize beacon scanning
try { try {
// Close any existing ranging streams first
await flutterBeacon.close;
await flutterBeacon.initializeScanning; await flutterBeacon.initializeScanning;
// Only add delay if permissions were freshly granted (Bluetooth subsystem needs warmup) // Always add warmup delay - Bluetooth adapter needs time to initialize
print('[Splash] 🔄 Bluetooth warmup...');
await Future.delayed(const Duration(milliseconds: 2000));
// Extra delay if permissions were freshly granted
if (_permissionsWereFreshlyGranted) { if (_permissionsWereFreshlyGranted) {
print('[Splash] 🔄 Fresh permissions - adding Bluetooth warmup delay'); print('[Splash] 🆕 Fresh permissions - adding extra warmup');
await Future.delayed(const Duration(milliseconds: 1500)); await Future.delayed(const Duration(milliseconds: 1500));
} }
@ -274,7 +281,8 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
} }
}); });
await Future.delayed(const Duration(milliseconds: 2000)); // Scan for 5 seconds to ensure we catch beacons
await Future.delayed(const Duration(milliseconds: 5000));
await subscription.cancel(); await subscription.cancel();
// Now lookup business info for found beacons // Now lookup business info for found beacons