Skip to content

Disposed AppLifecycleListener's onExitRequested is still called if disposed in another AppLifecycleListener #189472

Closed

Description

@hamaaja

Steps to reproduce

Found on Linux (Fedora 44 KDE). Not sure if it reproduces on other platforms.

  1. flutter create --platforms linux app_lc_test && cd app_lc_test
  2. Copy the reproducer code to main.dart.
  3. flutter run or flutter run --release
  4. Close the app.
  5. Observe assertion in debug mode or disposed listener getting called in release mode (AppLifecycleWidget: Exit requested output)

The issue was originally found when TextFields removed from widget tree in onExitRequested caused the assertion be thrown at exit. It was quite confusing at first because I only had one AppLifecycleListener in my code which certainly was not disposed. AppLifecycleWidget() in line 47 can be replaced with TextField() to reproduce that behavior in debug mode.

Expected results

No assertion thrown in debug mode and disposed listener is not called in release mode (no AppLifecycleWidget: Exit requested output).

Actual results

Assertion is thrown in debug mode and disposed listener is called in release mode (AppLifecycleWidget: Exit requested output).

Code sample

Code sample
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late final AppLifecycleListener listener;
  bool showLcWidget = true;

  @override
  void initState() {
    super.initState();
    listener = AppLifecycleListener(
      onExitRequested: () async {
        print('MyApp: Exit requested');
        setState(() {
          showLcWidget = false;
        });
        // Some exit procedure taking its time so that the other
        // AppLifecycleListener gets disposed.
        await Future.delayed(Duration(milliseconds: 200));
        return AppExitResponse.exit;
      },
    );
  }

  @override
  void dispose() {
    listener.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: showLcWidget ? const AppLifecycleWidget() : const SizedBox(),
      ),
    );
  }
}

class AppLifecycleWidget extends StatefulWidget {
  const AppLifecycleWidget({super.key});

  @override
  State<AppLifecycleWidget> createState() => _AppLifecycleWidgetState();
}

class _AppLifecycleWidgetState extends State<AppLifecycleWidget> {
  late final AppLifecycleListener listener;
  @override
  void initState() {
    super.initState();
    listener = AppLifecycleListener(
      onExitRequested: () async {
        print('AppLifecycleWidget: Exit requested');
        return AppExitResponse.exit;
      },
    );
  }

  @override
  void dispose() {
    listener.dispose();
    print('AppLifecycleWidget: listener disposed');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Text('AppLifecycleWidget here');
}

Screenshots or Video

N/A

Logs

`flutter run` output
MyApp: Exit requested
AppLifecycleWidget: listener disposed

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown while dispatching notifications for
WidgetsBindingObserver.didRequestAppExit:
A AppLifecycleListener was used after being disposed.
Once you have called dispose() on a AppLifecycleListener, it can no longer be used.

When the exception was thrown, this was the stack:
#0      AppLifecycleListener._debugAssertNotDisposed.<anonymous closure> (package:flutter/src/widgets/app_lifecycle_listener.dart:192:9)
#1      AppLifecycleListener._debugAssertNotDisposed (package:flutter/src/widgets/app_lifecycle_listener.dart:199:6)
#2      AppLifecycleListener.didRequestAppExit (package:flutter/src/widgets/app_lifecycle_listener.dart:205:12)
#3      WidgetsBinding.handleRequestAppExit (package:flutter/src/widgets/binding.dart:899:29)
<asynchronous suspension>
#4      ServicesBinding._handlePlatformMessage (package:flutter/src/services/binding.dart:441:47)
<asynchronous suspension>
#5      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:607:42)
<asynchronous suspension>
#6      _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:663:22)
<asynchronous suspension>
════════════════════════════════════════════════════════════════════════════════════════════════════
`flutter run --release` output
MyApp: Exit requested
AppLifecycleWidget: listener disposed
AppLifecycleWidget: Exit requested

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.44.6, on Fedora Linux 44 (KDE Plasma Desktop Edition) 7.1.3-200.fc44.x86_64, locale en_US.UTF-8) [88ms]
    • Flutter version 3.44.6 on channel stable at /home/user/dev-tools/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ee80f08bbf (6 days ago), 2026-07-08 15:02:06 -0700
    • Engine revision 83675ed276
    • Dart version 3.12.2
    • DevTools version 2.57.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations,
      enable-native-assets, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration

[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [2.9s]
    • Android SDK at /home/user/dev-tools/android/sdk
    • Emulator version 36.6.11.0 (build_id 15507667) (CL:N/A)
    • Platform android-36, build-tools 36.1.0
    • Java binary at: /home/user/dev-tools/android/android-studio/jbr/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)
    • All Android licenses accepted.

[✓] Chrome - develop for the web [19ms]
    • CHROME_EXECUTABLE = /usr/bin/chromium-browser

[✓] Linux toolchain - develop for Linux desktop [408ms]
    • clang version 22.1.8 (Fedora 22.1.8-1.fc44)
    • cmake version 4.3.0
    • ninja version 1.13.2
    • pkg-config version 2.5.1
    • OpenGL core renderer: AMD Radeon RX 7600 (radeonsi, navi33, ACO, DRM 3.64, 7.1.3-200.fc44.x86_64)
    • OpenGL core version: 4.6 (Core Profile) Mesa 26.1.4
    • OpenGL core shading language version: 4.60
    • OpenGL ES renderer: AMD Radeon RX 7600 (radeonsi, navi33, ACO, DRM 3.64, 7.1.3-200.fc44.x86_64)
    • OpenGL ES version: OpenGL ES 3.2 Mesa 26.1.4
    • OpenGL ES shading language version: OpenGL ES GLSL ES 3.20
    • GL_EXT_framebuffer_blit: yes
    • GL_EXT_texture_format_BGRA8888: yes

[✓] Connected device (2 available) [218ms]
    • Linux (desktop) • linux  • linux-x64      • Fedora Linux 44 (KDE Plasma Desktop Edition) 7.1.3-200.fc44.x86_64
    • Chrome (web)    • chrome • web-javascript • Chromium 150.0.7871.114 Built from source for Fedora release 44 (Forty Four)

[✓] Network resources [902ms]
    • All expected network resources are available.

• No issues found!

Activity

  1. suryaKommana2662 commented on Jul 15, 2026

    @suryaKommana2662

    @hamaaja Thanks for the detailed report and the reproducible sample!

    I was able to reproduce this issue on Linux (Debian GNU/Linux 13) using the provided sample.

    Observed behavior:
    After closing the application, the following output is observed:

    MyApp: Exit requested
    AppLifecycleWidget: listener disposed
    

    This is followed by the assertion:

    A AppLifecycleListener was used after being disposed.
    

    Environment:

    • Flutter 3.44.4 (stable)
    • Linux (Debian GNU/Linux 13)

    Also verified the behavior on macOS:

    In debug mode (flutter run -d macos), the same assertion is thrown after MyApp: Exit requested and AppLifecycleWidget: listener disposed.
    In release mode (flutter run -d macos --release), the disposed AppLifecycleListener still receives the onExitRequested callback (AppLifecycleWidget: Exit requested), matching the behavior reported on Linux.

    The behavior matches the reported issue. The disposed AppLifecycleListener still receives the onExitRequested callback during application exit. Routing this to team-framework for further investigation.

  2. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    platform-linuxBuilding on or for Linux specifically
    and removed
    in triagePresently being triaged by the triage team
    on Jul 15, 2026
  3. added
    P2Important issues not at the top of the work list
    on Jul 21, 2026
  4. added a commit that references this issue on Aug 25, 2026
    0cb3b7e
  5. github-actions commented on Sep 8, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  6. locked as resolved and limited conversation to collaborators on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listframeworkflutter/packages/flutter repository. See also f: labels.platform-linuxBuilding on or for Linux specificallyplatform-macosBuilding on or for macOS specificallyteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions