Implement Background Sync Polling with Workmanager

This commit is contained in:
ssbg04 committed 2026-07-22 23:18:32 +08:00
1 parent 8c80bc0033
commit 14eefa1062
5 files changed
+121

No files matched your search

@@ -0,0 +1,63 @@
import 'dart:math' as math;
import 'package:dio/dio.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:workmanager/workmanager.dart';
import 'notification_service.dart';
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
try {
// 1. Retrieve the saved base URL and Auth Token
final prefs = await SharedPreferences.getInstance();
final baseUrl = prefs.getString('server_url');
if (baseUrl == null) return true; // No server known, wait till next run
const storage = FlutterSecureStorage();
final token = await storage.read(key: 'jwt_token');
if (token == null) return true; // Not logged in
// 2. Fetch recent notifications from server
final dio = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 5),
headers: {'Authorization': 'Bearer $token'},
));
final response = await dio.get('/notifications/recent');
final list = (response.data as List).cast<Map<String, dynamic>>();
if (list.isNotEmpty) {
// 3. Find the last seen notification ID
final highestOldId = prefs.getInt('last_seen_notification_id') ?? 0;
// Find new ones
final newNotes = list.where((e) => (e['id'] as int) > highestOldId).toList();
if (newNotes.isNotEmpty) {
final notificationService = NotificationService();
await notificationService.initialize(); // Ensure it's ready in this isolate
// 4. Show local notification for each new one
for (var note in newNotes) {
await notificationService.showNotification(
title: note['title'] ?? 'New Notification',
body: note['message'] ?? '',
);
}
// 5. Update highest seen ID in prefs
final newHighestId = list.map((e) => e['id'] as int).reduce(math.max);
await prefs.setInt('last_seen_notification_id', math.max(highestOldId, newHighestId));
}
}
return Future.value(true);
} catch (e) {
print('Background fetch failed: $e');
return Future.value(false); // Retries on false
}
});
}
+18
View File
@@ -12,7 +12,9 @@ import 'ui/screens/splash/splash_screen.dart';
import 'core/theme/app_theme.dart'; // Add this import
import 'core/constants/app_colors.dart';
import 'core/services/notification_service.dart';
import 'core/services/background_sync_service.dart';
import 'ui/shared/widgets/inactivity_wrapper.dart';
import 'package:workmanager/workmanager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -47,6 +49,22 @@ void main() async {
await NotificationService().initialize();
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
Workmanager().initialize(
callbackDispatcher,
isInDebugMode: false,
);
// Register periodic task
Workmanager().registerPeriodicTask(
"1",
"background_sync_task",
frequency: const Duration(minutes: 15),
constraints: Constraints(
networkType: NetworkType.connected,
),
);
}
runApp(const ProviderScope(child: TisRmsApp()));
}
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../domain/repositories/notification_repository.dart';
import '../../domain/entities/notification_model.dart';
import '../../core/services/notification_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math' as math;
final notificationRepositoryProvider = Provider<NotificationRepository>((ref) {
@@ -52,6 +53,12 @@ class NotificationNotifier extends AsyncNotifier<List<NotificationModel>> {
}
}
if (list.isNotEmpty) {
final highestId = list.map((e) => e.id).reduce(math.max);
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('last_seen_notification_id', highestId);
}
state = AsyncData(list);
} catch (e, st) {
state = AsyncError(e, st);
+32
View File
@@ -1549,6 +1549,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.11.0"
workmanager:
dependency: "direct main"
description:
name: workmanager
sha256: "065673b2a465865183093806925419d311a9a5e0995aa74ccf8920fd695e2d10"
url: "https://pub.dev"
source: hosted
version: "0.9.0+3"
workmanager_android:
dependency: transitive
description:
name: workmanager_android
sha256: "9ae744db4ef891f5fcd2fb8671fccc712f4f96489a487a1411e0c8675e5e8cb7"
url: "https://pub.dev"
source: hosted
version: "0.9.0+2"
workmanager_apple:
dependency: transitive
description:
name: workmanager_apple
sha256: "1cc12ae3cbf5535e72f7ba4fde0c12dd11b757caf493a28e22d684052701f2ca"
url: "https://pub.dev"
source: hosted
version: "0.9.1+2"
workmanager_platform_interface:
dependency: transitive
description:
name: workmanager_platform_interface
sha256: f40422f10b970c67abb84230b44da22b075147637532ac501729256fcea10a47
url: "https://pub.dev"
source: hosted
version: "0.9.1+1"
xdg_directories:
dependency: transitive
description:
+1
View File
@@ -60,6 +60,7 @@ dependencies:
pdf: ^3.12.0
intl: ^0.20.3
flutter_local_notifications: ^17.2.2
workmanager: ^0.9.0+3
dev_dependencies:
flutter_test: