Unverified Commit d1b8527f authored by hellohuanlin's avatar hellohuanlin Committed by GitHub

[platform_view]fix iOS platform view's focus node leakage (#124066)

[platform_view]fix iOS platform view's focus node leakage
parent 85624dd0
...@@ -1378,6 +1378,7 @@ class UiKitViewController { ...@@ -1378,6 +1378,7 @@ class UiKitViewController {
Future<void> dispose() async { Future<void> dispose() async {
_debugDisposed = true; _debugDisposed = true;
await SystemChannels.platform_views.invokeMethod<void>('dispose', id); await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
PlatformViewsService._instance._focusCallbacks.remove(id);
} }
} }
......
...@@ -503,6 +503,8 @@ class _AndroidViewState extends State<AndroidView> { ...@@ -503,6 +503,8 @@ class _AndroidViewState extends State<AndroidView> {
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
_focusNode?.dispose();
_focusNode = null;
super.dispose(); super.dispose();
} }
...@@ -562,7 +564,9 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -562,7 +564,9 @@ class _UiKitViewState extends State<UiKitView> {
UiKitViewController? _controller; UiKitViewController? _controller;
TextDirection? _layoutDirection; TextDirection? _layoutDirection;
bool _initialized = false; bool _initialized = false;
late FocusNode _focusNode;
@visibleForTesting
FocusNode? focusNode;
static final Set<Factory<OneSequenceGestureRecognizer>> _emptyRecognizersSet = static final Set<Factory<OneSequenceGestureRecognizer>> _emptyRecognizersSet =
<Factory<OneSequenceGestureRecognizer>>{}; <Factory<OneSequenceGestureRecognizer>>{};
...@@ -574,7 +578,7 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -574,7 +578,7 @@ class _UiKitViewState extends State<UiKitView> {
return const SizedBox.expand(); return const SizedBox.expand();
} }
return Focus( return Focus(
focusNode: _focusNode, focusNode: focusNode,
onFocusChange: (bool isFocused) => _onFocusChange(isFocused, controller), onFocusChange: (bool isFocused) => _onFocusChange(isFocused, controller),
child: _UiKitPlatformView( child: _UiKitPlatformView(
controller: _controller!, controller: _controller!,
...@@ -634,6 +638,9 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -634,6 +638,9 @@ class _UiKitViewState extends State<UiKitView> {
@override @override
void dispose() { void dispose() {
_controller?.dispose(); _controller?.dispose();
_controller = null;
focusNode?.dispose();
focusNode = null;
super.dispose(); super.dispose();
} }
...@@ -646,7 +653,7 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -646,7 +653,7 @@ class _UiKitViewState extends State<UiKitView> {
creationParams: widget.creationParams, creationParams: widget.creationParams,
creationParamsCodec: widget.creationParamsCodec, creationParamsCodec: widget.creationParamsCodec,
onFocus: () { onFocus: () {
_focusNode.requestFocus(); focusNode?.requestFocus();
} }
); );
if (!mounted) { if (!mounted) {
...@@ -656,7 +663,7 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -656,7 +663,7 @@ class _UiKitViewState extends State<UiKitView> {
widget.onPlatformViewCreated?.call(id); widget.onPlatformViewCreated?.call(id);
setState(() { setState(() {
_controller = controller; _controller = controller;
_focusNode = FocusNode(debugLabel: 'UiKitView(id: $id)'); focusNode = FocusNode(debugLabel: 'UiKitView(id: $id)');
}); });
} }
...@@ -938,6 +945,8 @@ class _PlatformViewLinkState extends State<PlatformViewLink> { ...@@ -938,6 +945,8 @@ class _PlatformViewLinkState extends State<PlatformViewLink> {
void dispose() { void dispose() {
_controller?.dispose(); _controller?.dispose();
_controller = null; _controller = null;
_focusNode?.dispose();
_focusNode = null;
super.dispose(); super.dispose();
} }
} }
......
...@@ -2150,6 +2150,34 @@ void main() { ...@@ -2150,6 +2150,34 @@ void main() {
expect(channelArguments['platformViewId'], currentViewId + 1); expect(channelArguments['platformViewId'], currentViewId + 1);
}); });
testWidgets('FocusNode is disposed on UIView dispose', (WidgetTester tester) async {
final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController();
viewsController.registerViewType('webview');
await tester.pumpWidget(
const Center(
child: SizedBox(
width: 200.0,
height: 100.0,
child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr),
),
),
);
// casting to dynamic is required since the state class is private.
// ignore: avoid_dynamic_calls, invalid_assignment
final FocusNode node = (tester.state(find.byType(UiKitView)) as dynamic).focusNode;
expect(() => ChangeNotifier.debugAssertNotDisposed(node), isNot(throwsAssertionError));
await tester.pumpWidget(
const Center(
child: SizedBox(
width: 200.0,
height: 100.0,
),
),
);
expect(() => ChangeNotifier.debugAssertNotDisposed(node), throwsAssertionError);
});
testWidgets('UiKitView has correct semantics', (WidgetTester tester) async { testWidgets('UiKitView has correct semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment