-
Notifications
You must be signed in to change notification settings - Fork 170
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Required Reading
- Confirmed
Plugin Version
^1.5.0
Flutter Doctor
[✓] Flutter (Channel stable, 3.38.5, on macOS 15.5 24F74 darwin-arm64, locale en-GB)
[!] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 16.4)
[✓] Chrome - develop for the web
[✓] Connected device (4 available)
! Error: Browsing on the local area network for Development iPad. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
! Error: Browsing on the local area network for Apple Watch. Ensure the device is unlocked and discoverable via Bluetooth. (code -27)
! Error: Browsing on the local area network for Dev phone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resourcesMobile operating-system(s)
- iOS
- Android
Device Manufacturer(s) and Model(s)
SM-S921B 47% SM-X730 46%
Device operating-systems(s)
Android 16
What happened?
Not sure, this is happening in production and I can't reproduce.
Plugin Code and/or Config
/// Background Refresh Handler - Daily widget snapshot rebuild via background_fetch
///
/// ## Purpose
///
/// Ensures widget snapshots are regenerated daily so "today" queries reflect
/// the correct date. This is separate from FCM sync - FCM handles data freshness,
/// this handles date boundary changes.
///
/// ## Execution Context
///
/// background_fetch runs in the same process as the main app (similar to FCM).
/// Uses global database singletons to avoid lock conflicts.
///
/// ## When This Runs
///
/// - iOS: ~every 15 minutes (OS controlled, cannot be more frequent)
/// - Android: configurable minimum interval
/// - Only rebuilds if the day has changed since last refresh
library;
import 'dart:async';
import 'package:background_fetch/background_fetch.dart';
import 'package:bullet_common/common.dart';
import 'package:home_widget/home_widget.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'init_services.dart';
import 'src/config/bullet_env.dart';
import 'src/features/widget/widget_snapshot_coordinator.dart';
/// Initialize background fetch - call before runApp()
Future<void> initBackgroundRefresh() async {
if (!kIsMobile) return;
try {
await BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15, // iOS minimum, Android can be lower
stopOnTerminate: false,
enableHeadless: true,
startOnBoot: true,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.NONE,
),
_onBackgroundFetch,
_onBackgroundFetchTimeout,
);
// Register Android headless task for background fetch after app termination
BackgroundFetch.registerHeadlessTask(_headlessTask);
} catch (e, stackTrace) {
// Background fetch initialization can fail on beta OS versions or
// devices that don't support it - log and continue since it's optional
await ErrorMonitor.captureException(
e,
stackTrace,
handler: 'background_refresh_init',
level: SentryLevel.warning,
);
}
}
/// Headless task for Android - runs even after app termination
@pragma('vm:entry-point')
void _headlessTask(HeadlessTask task) async {
final taskId = task.taskId;
if (task.timeout) {
BackgroundFetch.finish(taskId);
return;
}
await _handleBackgroundRefresh(taskId);
}
/// Event callback - called when background fetch fires
Future<void> _onBackgroundFetch(String taskId) async {
await _handleBackgroundRefresh(taskId);
}
/// Timeout callback - must finish immediately
Future<void> _onBackgroundFetchTimeout(String taskId) async {
BackgroundFetch.finish(taskId);
}
/// Core refresh logic - shared by foreground and headless handlers
Future<void> _handleBackgroundRefresh(String taskId) async {
try {
// Check if day changed since last refresh
final lastRefreshDate = await HomeWidget.getWidgetData<String>(
'last_refresh_date',
);
final today = Date.today().toString();
if (lastRefreshDate == today) {
// Already refreshed today, skip
BackgroundFetch.finish(taskId);
return;
}
final env = BulletEnv();
// Create container with background-safe PowerSync override
final container = ProviderContainer(
overrides: [
envProvider.overrideWithValue(env),
powersyncInitializerProvider.overrideWith((ref) async {
final authRepo = ref.watch(authRepositoryProvider);
final packageInfo = ref
.watch(packageInfoInitializerProvider)
.requireValue;
final connection = PowersyncConnection.instance;
await connection.initBackground(
app: env.app,
authRepo: authRepo,
packageInfo: packageInfo,
);
return connection;
}),
],
);
try {
await initServices(container: container);
final coordinator = await container.read(
widgetSnapshotCoordinatorProvider.future,
);
await coordinator.rebuildAllSnapshots(shouldNotify: true);
// Mark today as refreshed
await HomeWidget.saveWidgetData('last_refresh_date', today);
} finally {
container.dispose();
}
} catch (e, stackTrace) {
await ErrorMonitor.captureException(
e,
stackTrace,
handler: 'background_widget_refresh',
level: SentryLevel.warning,
);
}
BackgroundFetch.finish(taskId);
}Relevant log output
Crashed in non-app: platform_channel.dart in MethodChannel._invokeMethod within flutter
Show 1 more frame
background_fetch.dart in BackgroundFetch.finish at line 582 within background_fetch
In App
No additional details are available for this frame.Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working