Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9fd0082

Browse files
authored
Adding app lifecycle notification for macOS and Linux, add hidden state. (#40542)
## Description This adds app lifecycle notification for macOS and Linux, and adds a new `hidden` state to the `AppLifecycleState` enum. To be functional, this needs a corresponding framework change: flutter/flutter#123274 ## Related Issues - flutter/flutter#30735 ## Tests - Added tests for state changes.
1 parent 7b55ec9 commit 9fd0082

28 files changed

+1113
-135
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2463,6 +2463,7 @@ ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.cc + ../../.
24632463
ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.h + ../../../flutter/LICENSE
24642464
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.cc + ../../../flutter/LICENSE
24652465
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h + ../../../flutter/LICENSE
2466+
ORIGIN: ../../../flutter/shell/platform/common/app_lifecycle_state.h + ../../../flutter/LICENSE
24662467
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h + ../../../flutter/LICENSE
24672468
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h + ../../../flutter/LICENSE
24682469
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc + ../../../flutter/LICENSE
@@ -2680,6 +2681,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm + ../../
26802681
ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h + ../../../flutter/LICENSE
26812682
ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm + ../../../flutter/LICENSE
26822683
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h + ../../../flutter/LICENSE
2684+
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h + ../../../flutter/LICENSE
26832685
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h + ../../../flutter/LICENSE
26842686
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h + ../../../flutter/LICENSE
26852687
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h + ../../../flutter/LICENSE
@@ -2691,6 +2693,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibil
26912693
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm + ../../../flutter/LICENSE
26922694
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm + ../../../flutter/LICENSE
26932695
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h + ../../../flutter/LICENSE
2696+
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate.mm + ../../../flutter/LICENSE
2697+
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegateTest.mm + ../../../flutter/LICENSE
26942698
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h + ../../../flutter/LICENSE
26952699
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm + ../../../flutter/LICENSE
26962700
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h + ../../../flutter/LICENSE
@@ -5130,6 +5134,7 @@ FILE: ../../../flutter/shell/platform/common/accessibility_bridge.cc
51305134
FILE: ../../../flutter/shell/platform/common/accessibility_bridge.h
51315135
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.cc
51325136
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h
5137+
FILE: ../../../flutter/shell/platform/common/app_lifecycle_state.h
51335138
FILE: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h
51345139
FILE: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h
51355140
FILE: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc
@@ -5349,6 +5354,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm
53495354
FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h
53505355
FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm
53515356
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h
5357+
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h
53525358
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h
53535359
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h
53545360
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h
@@ -5361,6 +5367,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibilit
53615367
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm
53625368
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm
53635369
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h
5370+
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate.mm
5371+
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegateTest.mm
53645372
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h
53655373
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm
53665374
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h

lib/ui/platform_dispatcher.dart

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,13 +1679,15 @@ class FrameTiming {
16791679
}
16801680
}
16811681

1682-
/// States that an application can be in.
1682+
/// States that an application can be in once it is running.
16831683
///
1684-
/// The values below describe notifications from the operating system.
1685-
/// Applications should not expect to always receive all possible notifications.
1686-
/// For example, if the users pulls out the battery from the device, no
1687-
/// notification will be sent before the application is suddenly terminated,
1688-
/// along with the rest of the operating system.
1684+
/// States not supported on a platform will be synthesized by the framework when
1685+
/// transitioning between states which are supported, so that all
1686+
/// implementations share the same state machine.
1687+
///
1688+
/// The initial value for the state is the [detached] state, updated to the
1689+
/// current state (usually [resumed]) as soon as the first lifecycle update is
1690+
/// received from the platform.
16891691
///
16901692
/// For historical and name collision reasons, Flutter's application state names
16911693
/// do not correspond one to one with the state names on all platforms. On
@@ -1696,15 +1698,52 @@ class FrameTiming {
16961698
/// Flutter enters the [paused] state. See the individual state's documentation
16971699
/// for descriptions of what they mean on each platform.
16981700
///
1701+
/// The current application state can be obtained from
1702+
/// [SchedulerBinding.instance.lifecycleState], and changes to the state can be
1703+
/// observed by creating an [AppLifecycleListener], or by using a
1704+
/// [WidgetsBindingObserver] by overriding the
1705+
/// [WidgetsBindingObserver.didChangeAppLifecycleState] method.
1706+
///
1707+
/// Applications should not rely on always receiving all possible notifications.
1708+
///
1709+
/// For example, if the application is killed with a task manager, a kill
1710+
/// signal, the user pulls the power from the device, or there is a rapid
1711+
/// unscheduled disassembly of the device, no notification will be sent before
1712+
/// the application is suddenly terminated, and some states may be skipped.
1713+
///
16991714
/// See also:
17001715
///
1716+
/// * [AppLifecycleListener], an object used observe the lifecycle state that
1717+
/// provides state transition callbacks.
17011718
/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state
17021719
/// from the widgets layer.
1703-
/// * iOS's [IOKit activity lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc) documentation.
1704-
/// * Android's [activity lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle) documentation.
1705-
/// * macOS's [AppKit activity lifecycle](https://developer.apple.com/documentation/appkit/nsapplicationdelegate?language=objc) documentation.
1720+
/// * iOS's [IOKit activity
1721+
/// lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc)
1722+
/// documentation.
1723+
/// * Android's [activity
1724+
/// lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle)
1725+
/// documentation.
1726+
/// * macOS's [AppKit activity
1727+
/// lifecycle](https://developer.apple.com/documentation/appkit/nsapplicationdelegate?language=objc)
1728+
/// documentation.
17061729
enum AppLifecycleState {
1707-
/// The application is visible and responsive to user input.
1730+
/// The application is still hosted by a Flutter engine but is detached from
1731+
/// any host views.
1732+
///
1733+
/// The application defaults to this state before it initializes, and can be
1734+
/// in this state (on Android and iOS only) after all views have been
1735+
/// detached.
1736+
///
1737+
/// When the application is in this state, the engine is running without a
1738+
/// view.
1739+
///
1740+
/// This state is only entered on iOS and Android, although on all platforms
1741+
/// it is the default state before the application begins running.
1742+
detached,
1743+
1744+
/// On all platforms, this state indicates that the application is in the
1745+
/// default running mode for a running application that has input focus and is
1746+
/// visible.
17081747
///
17091748
/// On Android, this state corresponds to the Flutter host view having focus
17101749
/// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean))
@@ -1717,54 +1756,71 @@ enum AppLifecycleState {
17171756
/// was called with false), but hasn't had
17181757
/// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
17191758
/// called on it.
1759+
///
1760+
/// On iOS and macOS, this corresponds to the app running in the foreground
1761+
/// active state.
17201762
resumed,
17211763

1722-
/// The application is in an inactive state and is not receiving user input.
1764+
/// At least one view of the application is visible, but none have input
1765+
/// focus. The application is otherwise running normally.
1766+
///
1767+
/// On non-web desktop platforms, this corresponds to an application that is
1768+
/// not in the foreground, but still has visible windows.
1769+
///
1770+
/// On the web, this corresponds to an application that is running in a
1771+
/// window or tab that does not have input focus.
17231772
///
1724-
/// On iOS, this state corresponds to an app or the Flutter host view running
1725-
/// in the foreground inactive state. Apps transition to this state when in a
1726-
/// phone call, responding to a TouchID request, when entering the app
1727-
/// switcher or the control center, or when the UIViewController hosting the
1728-
/// Flutter app is transitioning.
1773+
/// On iOS and macOS, this state corresponds to the Flutter host view running in the
1774+
/// foreground inactive state. Apps transition to this state when in a phone
1775+
/// call, when responding to a TouchID request, when entering the app switcher
1776+
/// or the control center, or when the UIViewController hosting the Flutter
1777+
/// app is transitioning.
17291778
///
1730-
/// On Android, this corresponds to an app or the Flutter host view running in
1731-
/// Android's paused state (i.e.
1779+
/// On Android, this corresponds to the Flutter host view running in Android's
1780+
/// paused state (i.e.
17321781
/// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause())
17331782
/// has been called), or in Android's "resumed" state (i.e.
17341783
/// [`Activity.onResume`](https://developer.android.com/reference/android/app/Activity#onResume())
1735-
/// has been called) but it has lost window focus. Examples of when apps
1784+
/// has been called) but does not have window focus. Examples of when apps
17361785
/// transition to this state include when the app is partially obscured or
1737-
/// another activity is focused, such as: a split-screen app, a phone call, a
1738-
/// picture-in-picture app, a system dialog, another view, when the
1786+
/// another activity is focused, a app running in a split screen that isn't
1787+
/// the current app, an app interrupted by a phone call, a picture-in-picture
1788+
/// app, a system dialog, another view. It will also be inactive when the
17391789
/// notification window shade is down, or the application switcher is visible.
17401790
///
1741-
/// Apps in this state should assume that they may be [paused] at any time.
1791+
/// On Android and iOS, apps in this state should assume that they may be
1792+
/// [hidden] and [paused] at any time.
17421793
inactive,
17431794

1744-
/// The application is not currently visible to the user, not responding to
1745-
/// user input, and running in the background.
1795+
/// All views of an application are hidden, either because the application is
1796+
/// about to be paused (on iOS and Android), or because it has been minimized
1797+
/// or placed on a desktop that is no longer visible (on non-web desktop), or
1798+
/// is running in a window or tab that is no longer visible (on the web).
1799+
///
1800+
/// On iOS and Android, in order to keep the state machine the same on all
1801+
/// platforms, a transition to this state is synthesized before the [paused]
1802+
/// state is entered when coming from [inactive], and before the [inactive]
1803+
/// state is entered when coming from [paused]. This allows cross-platform
1804+
/// implementations that want to know when an app is conceptually "hidden" to
1805+
/// only write one handler.
1806+
hidden,
1807+
1808+
/// The application is not currently visible to the user, and not responding
1809+
/// to user input.
17461810
///
17471811
/// When the application is in this state, the engine will not call the
17481812
/// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame]
17491813
/// callbacks.
1750-
paused,
1751-
1752-
/// The application is still hosted on a flutter engine but is detached from
1753-
/// any host views.
17541814
///
1755-
/// When the application is in this state, the engine is running without
1756-
/// a view. It can either be in the progress of attaching a view when engine
1757-
/// was first initializes, or after the view being destroyed due to a Navigator
1758-
/// pop.
1759-
detached,
1815+
/// This state is only entered on iOS and Android.
1816+
paused,
17601817
}
17611818

17621819
/// The possible responses to a request to exit the application.
17631820
///
1764-
/// The request is typically responded to by a [WidgetsBindingObserver].
1765-
// TODO(gspencergoog): Insert doc references here to AppLifecycleListener and to
1766-
// the actual function called on WidgetsBindingObserver once those have landed
1767-
// in the framework. https://github.com/flutter/flutter/issues/121721
1821+
/// The request is typically responded to by creating an [AppLifecycleListener]
1822+
/// and supplying an [AppLifecycleListener.onExitRequested] callback, or by
1823+
/// overriding [WidgetsBindingObserver.didRequestAppExit].
17681824
enum AppExitResponse {
17691825
/// Exiting the application can proceed.
17701826
exit,
@@ -1773,10 +1829,7 @@ enum AppExitResponse {
17731829
}
17741830

17751831
/// The type of application exit to perform when calling
1776-
/// `ServicesBinding.exitApplication`.
1777-
// TODO(gspencergoog): Insert doc references here to
1778-
// ServicesBinding.exitApplication that has landed in the framework.
1779-
// https://github.com/flutter/flutter/issues/121721
1832+
/// [ServicesBinding.exitApplication].
17801833
enum AppExitType {
17811834
/// Requests that the application start an orderly exit, sending a request
17821835
/// back to the framework through the [WidgetsBinding]. If that responds

lib/web_ui/lib/platform_dispatcher.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ abstract class PlatformDispatcher {
104104
VoidCallback? get onLocaleChanged;
105105
set onLocaleChanged(VoidCallback? callback);
106106

107-
String get initialLifecycleState => 'AppLifecycleState.resumed';
107+
String get initialLifecycleState => '';
108108

109109
bool get alwaysUse24HourFormat;
110110

@@ -247,10 +247,11 @@ class FrameTiming {
247247
}
248248

249249
enum AppLifecycleState {
250+
detached,
250251
resumed,
251252
inactive,
253+
hidden,
252254
paused,
253-
detached,
254255
}
255256

256257
enum AppExitResponse {

lib/web_ui/lib/src/engine/platform_dispatcher.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
104104
_addFontSizeObserver();
105105
_addLocaleChangedListener();
106106
registerHotRestartListener(dispose);
107+
_setAppLifecycleState(ui.AppLifecycleState.resumed);
107108
}
108109

109110
/// The [EnginePlatformDispatcher] singleton.
@@ -1005,6 +1006,14 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
10051006
_fontSizeObserver = null;
10061007
}
10071008

1009+
void _setAppLifecycleState(ui.AppLifecycleState state) {
1010+
sendPlatformMessage(
1011+
'flutter/lifecycle',
1012+
Uint8List.fromList(utf8.encode(state.toString())).buffer.asByteData(),
1013+
null,
1014+
);
1015+
}
1016+
10081017
/// A callback that is invoked whenever [textScaleFactor] changes value.
10091018
///
10101019
/// The framework invokes this callback in the same zone in which the

0 commit comments

Comments
 (0)