Skip to content

Commit 04133c9

Browse files
enedclaude
andauthored
fix: resolve issue #622 - periodic tasks running at incorrect frequencies (#628)
* fix: resolve issue #622 - periodic tasks running at incorrect frequencies BREAKING CHANGE: Separate ExistingWorkPolicy and ExistingPeriodicWorkPolicy enums - registerPeriodicTask now requires ExistingPeriodicWorkPolicy instead of ExistingWorkPolicy - This mirrors Android's native WorkManager API design for better type safety Changes: - Change default periodic work policy from KEEP to UPDATE - UPDATE policy ensures new task configurations replace existing ones - Add comprehensive documentation with upstream Android links - Update example app with periodic task demo using UPDATE policy - Update all packages to use the new separated policy types Fixes #622 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: resolve mock generation issues and update dependencies - Add build_runner dependency to workmanager package - Fix .mocks.dart generation with proper ExistingPeriodicWorkPolicy types - Update generated mocks to reflect new API separation - Clean up documentation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * ci: add generated files check workflow and improve melos generate command - Add new GitHub Actions workflow to ensure generated files are up-to-date - Create parent 'generate' command that runs both pigeon and dart generation - Update generate:pigeon to use --depends-on filter for consistency - Workflow fails PR builds if generated files are not committed This ensures the repository always contains current generated files. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: update pubspec versions after dependency changes - Update example and platform interface pubspec files - Ensure version consistency across packages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: remove unnecessary build.yaml file The build.yaml file is not needed as mockito automatically discovers test files with @GenerateMocks annotations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: correct ExistingWorkPolicy type in integration test - Fix type error in workmanager_integration_test.dart - Use ExistingPeriodicWorkPolicy instead of ExistingWorkPolicy for periodic tasks - Add 'dart analyze' to pre-commit checklist to catch code errors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent c957b9a commit 04133c9

File tree

23 files changed

+628
-238
lines changed

23 files changed

+628
-238
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: generated-files
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
7+
jobs:
8+
check-generated-files:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: subosito/flutter-action@v2
13+
with:
14+
channel: 'stable'
15+
cache: true
16+
- uses: bluefireteam/melos-action@v3
17+
18+
- name: Generate files
19+
run: melos run generate
20+
21+
- name: Check for uncommitted changes
22+
run: |
23+
if ! git diff --quiet; then
24+
echo "❌ Generated files are not up-to-date!"
25+
echo "The following files have uncommitted changes after generation:"
26+
git diff --name-only
27+
echo ""
28+
echo "Please run 'melos run generate' locally and commit the updated files."
29+
echo ""
30+
echo "Changes detected:"
31+
git diff
32+
exit 1
33+
else
34+
echo "✅ All generated files are up-to-date"
35+
fi

CLAUDE.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
## Pre-Commit Requirements
22
**CRITICAL**: Always run from project root before ANY commit:
3-
1. `ktlint -F .`
4-
2. `find . -name "*.dart" ! -name "*.g.dart" ! -path "*/.*" -print0 | xargs -0 dart format --set-exit-if-changed`
5-
3. `flutter test` (all Dart tests)
6-
4. `cd example/android && ./gradlew :workmanager_android:test` (Android native tests)
3+
1. `dart analyze` (check for code errors)
4+
2. `ktlint -F .`
5+
3. `find . -name "*.dart" ! -name "*.g.dart" ! -path "*/.*" -print0 | xargs -0 dart format --set-exit-if-changed`
6+
4. `flutter test` (all Dart tests)
7+
5. `cd example/android && ./gradlew :workmanager_android:test` (Android native tests)
78

89
## Code Generation
910
- Regenerate Pigeon files: `melos run generate:pigeon`
11+
- Regenerate Dart files (including mocks): `melos run generate:dart`
1012
- Do not manually edit *.g.* files
1113

14+
## Running Tests
15+
- Use melos to run all tests: `melos run test`
16+
- Or run tests in individual packages:
17+
- `cd workmanager_android && flutter test`
18+
- `cd workmanager_apple && flutter test`
19+
- `cd workmanager && flutter test`
20+
- Before running tests in workmanager package, ensure mocks are up-to-date: `melos run generate:dart`
21+
1222
## Test Quality Requirements
1323
- **NEVER create useless tests**: No `assert(true)`, `expect(true, true)`, or compilation-only tests
1424
- **Test real logic**: Exercise actual methods with real inputs and verify meaningful outputs

example/integration_test/workmanager_integration_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ void main() {
288288
requiresBatteryNotLow: false,
289289
requiresCharging: true,
290290
),
291-
existingWorkPolicy: ExistingWorkPolicy.keep,
291+
existingWorkPolicy: ExistingPeriodicWorkPolicy.keep,
292292
backoffPolicy: BackoffPolicy.linear,
293293
backoffPolicyDelay: const Duration(seconds: 10),
294294
tag: 'periodic-tag',

example/lib/main.dart

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const iOSBackgroundAppRefresh =
2525
"dev.fluttercommunity.workmanagerExample.iOSBackgroundAppRefresh";
2626
const iOSBackgroundProcessingTask =
2727
"dev.fluttercommunity.workmanagerExample.iOSBackgroundProcessingTask";
28+
const periodicUpdatePolicyTask =
29+
"dev.fluttercommunity.workmanagerExample.periodicUpdatePolicyTask";
2830

2931
final List<String> allTasks = [
3032
simpleTaskKey,
@@ -35,6 +37,7 @@ final List<String> allTasks = [
3537
simplePeriodic1HourTask,
3638
iOSBackgroundAppRefresh,
3739
iOSBackgroundProcessingTask,
40+
periodicUpdatePolicyTask,
3841
];
3942

4043
// Pragma is mandatory if the App is obfuscated or using Flutter 3.1+
@@ -92,6 +95,11 @@ void callbackDispatcher() {
9295
await Future<void>.delayed(Duration(seconds: 40));
9396
print("$task finished");
9497
break;
98+
case periodicUpdatePolicyTask:
99+
final frequency = inputData?['frequency'] ?? 'unknown';
100+
print(
101+
"$periodicUpdatePolicyTask executed with frequency: $frequency minutes at ${DateTime.now()}");
102+
break;
95103
default:
96104
return Future.value(false);
97105
}
@@ -110,6 +118,7 @@ class MyApp extends StatefulWidget {
110118
class _MyAppState extends State<MyApp> {
111119
bool workmanagerInitialized = false;
112120
String _prefsString = "empty";
121+
int _selectedFrequency = 15; // Default to 15 minutes
113122

114123
@override
115124
Widget build(BuildContext context) {
@@ -244,6 +253,66 @@ class _MyAppState extends State<MyApp> {
244253
}
245254
: null),
246255

256+
SizedBox(height: 16),
257+
Text(
258+
"Test Periodic Task with UPDATE Policy (Android)",
259+
style: Theme.of(context).textTheme.headlineSmall,
260+
),
261+
Text(
262+
"Demonstrates issue #622 fix - changing frequency updates the existing task",
263+
style: Theme.of(context).textTheme.bodySmall,
264+
),
265+
SizedBox(height: 8),
266+
if (Platform.isAndroid) ...[
267+
Row(
268+
children: [
269+
Text("Frequency: "),
270+
Expanded(
271+
child: DropdownButton<int>(
272+
value: _selectedFrequency,
273+
items: [
274+
DropdownMenuItem(
275+
value: 15, child: Text("15 minutes")),
276+
DropdownMenuItem(
277+
value: 30, child: Text("30 minutes")),
278+
DropdownMenuItem(value: 60, child: Text("1 hour")),
279+
],
280+
onChanged: (value) {
281+
setState(() {
282+
_selectedFrequency = value!;
283+
});
284+
},
285+
),
286+
),
287+
],
288+
),
289+
SizedBox(height: 8),
290+
ElevatedButton(
291+
child: Text("Register Periodic Task with UPDATE Policy"),
292+
onPressed: () {
293+
Workmanager().registerPeriodicTask(
294+
periodicUpdatePolicyTask,
295+
periodicUpdatePolicyTask,
296+
frequency: Duration(minutes: _selectedFrequency),
297+
initialDelay: Duration(seconds: 10),
298+
existingWorkPolicy: ExistingPeriodicWorkPolicy.update,
299+
inputData: <String, dynamic>{
300+
'frequency': _selectedFrequency,
301+
'timestamp': DateTime.now().toIso8601String(),
302+
},
303+
);
304+
ScaffoldMessenger.of(context).showSnackBar(
305+
SnackBar(
306+
content: Text(
307+
"Registered periodic task with ${_selectedFrequency}min frequency using UPDATE policy"),
308+
duration: Duration(seconds: 3),
309+
),
310+
);
311+
},
312+
),
313+
],
314+
315+
SizedBox(height: 16),
247316
// Currently we cannot provide frequency for iOS, hence it will be
248317
// minimum 15 minutes after which iOS will reschedule
249318
ElevatedButton(

example/pubspec.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ dependencies:
1818
dev_dependencies:
1919
integration_test:
2020
sdk: flutter
21-
flutter_test:
22-
sdk: flutter
2321
flutter_lints: ^6.0.0
2422

2523
flutter:

melos.yaml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ scripts:
99
get: melos exec -- dart pub get
1010

1111
test:
12-
run: melos exec --depends-on="flutter_test" -- "flutter test"
12+
run: melos exec -c 1 --depends-on="flutter_test" -- "flutter test"
1313
description: Run tests for all packages with flutter_test dependency.
1414

15+
generate:
16+
run: |
17+
melos run generate:pigeon
18+
melos run generate:dart
19+
description: Generate all code (Pigeon files and Dart mocks/generated files).
20+
1521
generate:dart:
16-
run: melos exec -c 1 --depends-on="build_runner" --no-flutter -- "dart run build_runner build --delete-conflicting-outputs"
22+
run: melos exec -c 1 --depends-on="build_runner" -- "dart run build_runner build --delete-conflicting-outputs"
1723
description: Build all generated files for Dart packages in this project.
1824

1925
generate:pigeon:
20-
run: cd workmanager_platform_interface && dart run pigeon --input pigeons/workmanager_api.dart
21-
description: Generate Pigeon type-safe platform channel code for workmanager_platform_interface.
26+
run: melos exec -c 1 --depends-on="pigeon" -- "dart run pigeon --input pigeons/workmanager_api.dart"
27+
description: Generate Pigeon type-safe platform channel code for packages with pigeon dependency.

workmanager/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Future
22

3+
## Breaking Changes
4+
* **BREAKING**: Separate `ExistingWorkPolicy` and `ExistingPeriodicWorkPolicy` enums for better type safety and API clarity
5+
* `registerPeriodicTask` now requires `ExistingPeriodicWorkPolicy` instead of `ExistingWorkPolicy`
6+
* This mirrors Android's native WorkManager API design for better consistency
7+
38
## Bug Fixes & Improvements
9+
* Fix issue #622: Periodic tasks running at incorrect frequencies when re-registered with different intervals
10+
* Changed default policy from `KEEP` to `UPDATE` for periodic tasks
11+
* `UPDATE` policy ensures new task configurations replace existing ones without disruption
412
* Fix null cast to map bug in executeTask when inputData contains null keys or values (thanks to @Dr-wgy)
513
* Internal improvements to development and testing infrastructure
614

workmanager/lib/src/workmanager_impl.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class Workmanager {
220220
Map<String, dynamic>? inputData,
221221
Duration? initialDelay,
222222
Constraints? constraints,
223-
ExistingWorkPolicy? existingWorkPolicy,
223+
ExistingPeriodicWorkPolicy? existingWorkPolicy,
224224
BackoffPolicy? backoffPolicy,
225225
Duration? backoffPolicyDelay,
226226
String? tag,

workmanager/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies:
2020
dev_dependencies:
2121
test: ^1.25.0
2222
mockito: ^5.4.4
23+
build_runner: ^2.4.9
2324
get_it: ^8.0.0
2425
flutter_test:
2526
sdk: flutter

0 commit comments

Comments
 (0)