mirror of
https://github.com/flutter/flutter
synced 2024-09-29 21:14:10 +00:00
Reland "Update FocusManager
platform check to include iOS" (#148984)
It looks like removing `kIsWeb` from the `FocusManager._appLifecycleListener` platform check is causing [memory leaks](https://github.com/flutter/flutter/issues/148985) and test failures. This pull request fixes #148475 and prevents the test failures shown in #148978.
This commit is contained in:
parent
5beac73cfa
commit
f419177057
|
@ -1532,19 +1532,29 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
|
|||
if (kFlutterMemoryAllocationsEnabled) {
|
||||
ChangeNotifier.maybeDispatchObjectCreation(this);
|
||||
}
|
||||
if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) {
|
||||
// It appears that some Android keyboard implementations can cause
|
||||
// app lifecycle state changes: adding this listener would cause the
|
||||
// text field to unfocus as the user is trying to type.
|
||||
//
|
||||
// Until this is resolved, we won't be adding the listener to Android apps.
|
||||
// https://github.com/flutter/flutter/pull/142930#issuecomment-1981750069
|
||||
if (_respondToWindowFocus) {
|
||||
_appLifecycleListener = _AppLifecycleListener(_appLifecycleChange);
|
||||
WidgetsBinding.instance.addObserver(_appLifecycleListener!);
|
||||
}
|
||||
rootScope._manager = this;
|
||||
}
|
||||
|
||||
/// It appears that some Android keyboard implementations can cause
|
||||
/// app lifecycle state changes: adding the app lifecycle listener would
|
||||
/// cause the text field to unfocus as the user is trying to type.
|
||||
///
|
||||
/// Additionally, on iOS, input fields aren't automatically populated
|
||||
/// with relevant data when using autofill.
|
||||
///
|
||||
/// Until these are resolved, we won't be adding the listener to mobile platforms.
|
||||
/// https://github.com/flutter/flutter/issues/148475#issuecomment-2118407411
|
||||
/// https://github.com/flutter/flutter/pull/142930#issuecomment-1981750069
|
||||
bool get _respondToWindowFocus => kIsWeb || switch (defaultTargetPlatform) {
|
||||
TargetPlatform.android || TargetPlatform.iOS => false,
|
||||
TargetPlatform.fuchsia || TargetPlatform.linux => true,
|
||||
TargetPlatform.windows || TargetPlatform.macOS => true,
|
||||
};
|
||||
|
||||
/// Registers global input event handlers that are needed to manage focus.
|
||||
///
|
||||
/// This calls the [HardwareKeyboard.addHandler] on the shared instance of
|
||||
|
@ -1875,7 +1885,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
|
|||
|
||||
/// Enables this [FocusManager] to listen to changes of the application
|
||||
/// lifecycle if it does not already have an application lifecycle listener
|
||||
/// active, and the current platform is detected as [kIsWeb] or non-Android.
|
||||
/// active, and the app isn't running on a native mobile platform.
|
||||
///
|
||||
/// Typically, the application lifecycle listener for this [FocusManager] is
|
||||
/// setup at construction, but sometimes it is necessary to manually initialize
|
||||
|
@ -1889,13 +1899,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
|
|||
/// supported.
|
||||
@visibleForTesting
|
||||
void listenToApplicationLifecycleChangesIfSupported() {
|
||||
if (_appLifecycleListener == null && (kIsWeb || defaultTargetPlatform != TargetPlatform.android)) {
|
||||
// It appears that some Android keyboard implementations can cause
|
||||
// app lifecycle state changes: adding this listener would cause the
|
||||
// text field to unfocus as the user is trying to type.
|
||||
//
|
||||
// Until this is resolved, we won't be adding the listener to Android apps.
|
||||
// https://github.com/flutter/flutter/pull/142930#issuecomment-1981750069
|
||||
if (_appLifecycleListener == null && _respondToWindowFocus) {
|
||||
_appLifecycleListener = _AppLifecycleListener(_appLifecycleChange);
|
||||
WidgetsBinding.instance.addObserver(_appLifecycleListener!);
|
||||
}
|
||||
|
|
|
@ -354,44 +354,39 @@ void main() {
|
|||
logs.clear();
|
||||
}, variant: KeySimulatorTransitModeVariant.all());
|
||||
|
||||
testWidgets('FocusManager ignores app lifecycle changes on Android.', (WidgetTester tester) async {
|
||||
final bool shouldRespond = kIsWeb || defaultTargetPlatform != TargetPlatform.android;
|
||||
if (shouldRespond) {
|
||||
return;
|
||||
}
|
||||
testWidgets(
|
||||
'FocusManager ignores app lifecycle changes on Android and iOS.',
|
||||
(WidgetTester tester) async {
|
||||
Future<void> setAppLifecycleState(AppLifecycleState state) async {
|
||||
final ByteData? message = const StringCodec().encodeMessage(state.toString());
|
||||
await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.handlePlatformMessage('flutter/lifecycle', message, (_) {});
|
||||
}
|
||||
|
||||
Future<void> setAppLifecycleState(AppLifecycleState state) async {
|
||||
final ByteData? message = const StringCodec().encodeMessage(state.toString());
|
||||
await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.handlePlatformMessage('flutter/lifecycle', message, (_) {});
|
||||
}
|
||||
final BuildContext context = await setupWidget(tester);
|
||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||
addTearDown(scope.dispose);
|
||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||
final FocusNode focusNode = FocusNode(debugLabel: 'Focus Node');
|
||||
addTearDown(focusNode.dispose);
|
||||
final FocusAttachment focusNodeAttachment = focusNode.attach(context);
|
||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||
focusNodeAttachment.reparent(parent: scope);
|
||||
focusNode.requestFocus();
|
||||
await tester.pump();
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
|
||||
final BuildContext context = await setupWidget(tester);
|
||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||
addTearDown(scope.dispose);
|
||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||
final FocusNode focusNode = FocusNode(debugLabel: 'Focus Node');
|
||||
addTearDown(focusNode.dispose);
|
||||
final FocusAttachment focusNodeAttachment = focusNode.attach(context);
|
||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||
focusNodeAttachment.reparent(parent: scope);
|
||||
focusNode.requestFocus();
|
||||
await tester.pump();
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
await setAppLifecycleState(AppLifecycleState.paused);
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
|
||||
await setAppLifecycleState(AppLifecycleState.paused);
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
|
||||
await setAppLifecycleState(AppLifecycleState.resumed);
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
});
|
||||
await setAppLifecycleState(AppLifecycleState.resumed);
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
},
|
||||
skip: kIsWeb, // [intended]
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.android, TargetPlatform.iOS}),
|
||||
);
|
||||
|
||||
testWidgets('FocusManager responds to app lifecycle changes.', (WidgetTester tester) async {
|
||||
final bool shouldRespond = kIsWeb || defaultTargetPlatform != TargetPlatform.android;
|
||||
if (!shouldRespond) {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> setAppLifecycleState(AppLifecycleState state) async {
|
||||
final ByteData? message = const StringCodec().encodeMessage(state.toString());
|
||||
await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
|
|
Loading…
Reference in a new issue