rrousselGit / flutter_hooks

React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.
MIT License
3.06k stars 175 forks source link

Inconsistency in AppLifecycleState Detection Between iOS and Android in Flutter Hooks #409

Closed motucraft closed 5 months ago

motucraft commented 5 months ago

Describe the bug When transitioning a Flutter app to the background, the AppLifecycleState.paused state is not detected on Android devices, while it is correctly detected on iOS. On Android, the lifecycle only reaches AppLifecycleState.inactive, omitting the paused state. This inconsistency between platforms can lead to issues in handling app lifecycle states in Flutter applications using flutter_hooks.

To Reproduce

Use the following Flutter code to monitor app lifecycle states:

Sample Code ```dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; void main() { runApp(const ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp(home: Sample()); } } class Sample extends HookWidget { const Sample({super.key}); @override Widget build(BuildContext context) { final appLifecycle = useAppLifecycleState(); print('appLifecycle=$appLifecycle'); return Scaffold(body: Center(child: Text('$appLifecycle'))); } } ```
pubspec.yaml ```yaml name: app_lifecycle description: "A new Flutter project." publish_to: 'none' version: 1.0.0+1 environment: sdk: '>=3.2.3 <4.0.0' dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 hooks_riverpod: ^2.4.9 flutter_hooks: ^0.20.4 riverpod_annotation: ^2.3.3 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 riverpod_generator: ^2.3.9 build_runner: ^2.4.7 custom_lint: ^0.5.7 riverpod_lint: ^2.3.7 flutter: uses-material-design: true ```
console(Android) ```bash Launching lib/main.dart on sdk gphone64 arm64 in debug mode... Running Gradle task 'assembleDebug'... ✓ Built build/app/outputs/flutter-apk/app-debug.apk. Debug service listening on ws://127.0.0.1:50008/retjf42mpLQ=/ws Syncing files to device sdk gphone64 arm64... I/flutter ( 9338): appLifecycle=AppLifecycleState.resumed I/flutter ( 9338): appLifecycle=AppLifecycleState.inactive ```
console(iOS) ```bash Launching lib/main.dart on iPhone 15 Pro Max in debug mode... Running Xcode build... Xcode build done. 18.5s Debug service listening on ws://127.0.0.1:64621/OFX7kmx7qO0=/ws Syncing files to device iPhone 15 Pro Max... flutter: appLifecycle=AppLifecycleState.resumed flutter: appLifecycle=AppLifecycleState.inactive flutter: appLifecycle=AppLifecycleState.paused ```
  1. Run the app on an iOS device and transition the app to the background. The console logs will correctly show the lifecycle states transitioning through resumed, inactive, and paused.
  2. Run the same app on an Android device and repeat the process. The console logs will only show resumed and inactive, missing the paused state.

https://github.com/rrousselGit/flutter_hooks/assets/35750184/7e68f630-e40e-4208-9e3f-949e714121df

Expected behavior The expected behavior is that on both iOS and Android platforms, transitioning the app to the background should consistently trigger AppLifecycleState.resumed, AppLifecycleState.inactive, and AppLifecycleState.paused in that order.

motucraft commented 5 months ago

Would the following issues be relevant? https://github.com/flutter/flutter/issues/114756

I would like to detect AppLifecycleState because I want to cancel the Firestore snapshot once the app has moved to the background. (I'm using cloud_firestore_odm and riverpod.)

rrousselGit commented 5 months ago

The expected behavior is that on both iOS and Android platforms, transitioning the app to the background should consistently trigger AppLifecycleState.resumed, AppLifecycleState.inactive, and AppLifecycleState.paused in that order.

It is not possible to make this guarantee Flutter decides when it invoked build. It could very well be that two life-cycle changes happen at once, before build could be invoked. This would swallow the first event.

motucraft commented 5 months ago

I see, thanks your reply.

I would like to detect AppLifecycleState because I want to cancel the Firestore snapshot once the app has moved to the background. (I'm using cloud_firestore_odm and riverpod.)

Any ideas on this?

rrousselGit commented 5 months ago

You could use useOnAppLifecycleStateChange

motucraft commented 5 months ago

That build may not be called means that it can only be handled within a callback, right? I decided to respond in this way.

    useOnAppLifecycleStateChange((_, current) {
      switch (current) {
        case AppLifecycleState.resumed || AppLifecycleState.inactive:
          FirebaseFirestore.instance.enableNetwork();
          break;
        default:
          FirebaseFirestore.instance.disableNetwork();
      }
    });