mirror of
https://github.com/flutter/flutter
synced 2024-07-16 10:29:14 +00:00
Add platform check to FocusManager
app lifecycle listener (#144718)
This PR implements a temporary fix for the mobile device keyboard bug reported in [this comment](https://github.com/flutter/flutter/pull/142930#issuecomment-1981750069). CC @gspencergoog
This commit is contained in:
parent
1ca88730a0
commit
61812ca3eb
|
@ -1677,8 +1677,16 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
|
|||
if (kFlutterMemoryAllocationsEnabled) {
|
||||
ChangeNotifier.maybeDispatchObjectCreation(this);
|
||||
}
|
||||
_appLifecycleListener = _AppLifecycleListener(_appLifecycleChange);
|
||||
WidgetsBinding.instance.addObserver(_appLifecycleListener);
|
||||
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
|
||||
_appLifecycleListener = _AppLifecycleListener(_appLifecycleChange);
|
||||
WidgetsBinding.instance.addObserver(_appLifecycleListener!);
|
||||
}
|
||||
rootScope._manager = this;
|
||||
}
|
||||
|
||||
|
@ -1695,7 +1703,9 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(_appLifecycleListener);
|
||||
if (_appLifecycleListener != null) {
|
||||
WidgetsBinding.instance.removeObserver(_appLifecycleListener!);
|
||||
}
|
||||
_highlightManager.dispose();
|
||||
rootScope.dispose();
|
||||
super.dispose();
|
||||
|
@ -1856,7 +1866,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
|
|||
|
||||
// Allows FocusManager to respond to app lifecycle state changes,
|
||||
// temporarily suspending the primaryFocus when the app is inactive.
|
||||
late final _AppLifecycleListener _appLifecycleListener;
|
||||
_AppLifecycleListener? _appLifecycleListener;
|
||||
|
||||
// Stores the node that was focused before the app lifecycle changed.
|
||||
// Will be restored as the primary focus once app is resumed.
|
||||
|
|
|
@ -354,7 +354,44 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
await setAppLifecycleState(AppLifecycleState.paused);
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
|
||||
await setAppLifecycleState(AppLifecycleState.resumed);
|
||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
});
|
||||
|
||||
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
|
||||
|
@ -402,8 +439,6 @@ void main() {
|
|||
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||
|
||||
await setAppLifecycleState(AppLifecycleState.paused);
|
||||
expect(focusNode.hasPrimaryFocus, isFalse);
|
||||
|
||||
focusNodeAttachment.detach();
|
||||
expect(focusNode.hasPrimaryFocus, isFalse);
|
||||
|
||||
|
|
Loading…
Reference in a new issue