From e68fde7297f3aa0065afe5a7491fe803a0c81843 Mon Sep 17 00:00:00 2001 From: Dan Chevalier Date: Thu, 26 Oct 2023 11:11:47 -0400 Subject: [PATCH 1/9] change clipboard copy so that it fallsback to a postMessage to the parent frame --- .../lib/src/framework/settings_dialog.dart | 2 +- .../src/screens/network/network_screen.dart | 1 + .../lib/src/shared/common_widgets.dart | 1 + .../_copy_to_clipboard_desktop.dart | 7 +++ .../_copy_to_clipboard_stub.dart | 7 +++ .../_copy_to_clipboard_web.dart | 19 ++++++++ .../copy_to_clipboard/copy_to_clipboard.dart | 44 +++++++++++++++++++ .../devtools_app/lib/src/shared/dialogs.dart | 2 +- .../lib/src/shared/editable_list.dart | 2 +- .../devtools_app/lib/src/shared/utils.dart | 16 ------- 10 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart diff --git a/packages/devtools_app/lib/src/framework/settings_dialog.dart b/packages/devtools_app/lib/src/framework/settings_dialog.dart index 3705255a32b..0e850ef7082 100644 --- a/packages/devtools_app/lib/src/framework/settings_dialog.dart +++ b/packages/devtools_app/lib/src/framework/settings_dialog.dart @@ -11,10 +11,10 @@ import 'package:provider/provider.dart'; import '../shared/analytics/analytics_controller.dart'; import '../shared/analytics/constants.dart' as gac; import '../shared/common_widgets.dart'; +import '../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import '../shared/config_specific/server/server.dart'; import '../shared/globals.dart'; import '../shared/log_storage.dart'; -import '../shared/utils.dart'; class OpenSettingsAction extends ScaffoldAction { OpenSettingsAction({super.key, Color? color}) diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart index 4ddd3f63d14..9ce787c78fc 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -13,6 +13,7 @@ import 'package:provider/provider.dart'; import '../../shared/analytics/analytics.dart' as ga; import '../../shared/analytics/constants.dart' as gac; import '../../shared/common_widgets.dart'; +import '../../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import '../../shared/globals.dart'; import '../../shared/http/curl_command.dart'; import '../../shared/http/http_request_data.dart'; diff --git a/packages/devtools_app/lib/src/shared/common_widgets.dart b/packages/devtools_app/lib/src/shared/common_widgets.dart index c16bfd49bad..4f32da4eef8 100644 --- a/packages/devtools_app/lib/src/shared/common_widgets.dart +++ b/packages/devtools_app/lib/src/shared/common_widgets.dart @@ -17,6 +17,7 @@ import '../screens/debugger/debugger_controller.dart'; import '../screens/inspector/layout_explorer/ui/theme.dart'; import 'analytics/analytics.dart' as ga; import 'analytics/constants.dart' as gac; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import 'config_specific/launch_url/launch_url.dart'; import 'console/widgets/expandable_variable.dart'; import 'diagnostics/dart_object_node.dart'; diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart new file mode 100644 index 00000000000..04bef782f20 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart @@ -0,0 +1,7 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +void copyToClipboardVSCode(String _) { + // Do nothing +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart new file mode 100644 index 00000000000..04bef782f20 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart @@ -0,0 +1,7 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +void copyToClipboardVSCode(String _) { + // Do nothing +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart new file mode 100644 index 00000000000..5a247b00747 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart @@ -0,0 +1,19 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +// ignore: avoid_web_libraries_in_flutter, as designed +import 'dart:html'; + +void copyToClipboardVSCode(String data) { + // This post message is only relevant in VSCode. If the parent frame is + // listening for this command, then it will attempt to copy the contents + // to the clipboard in the context of the parent frame. + window.parent?.postMessage( + { + 'command': 'clipboard-write', + 'data': data, + }, + '*', + ); +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart new file mode 100644 index 00000000000..dde5221b550 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart @@ -0,0 +1,44 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; + +import '../../globals.dart'; +import '_copy_to_clipboard_stub.dart' + if (dart.library.js_interop) '_copy_to_clipboard_web.dart' + if (dart.library.io) '_copy_to_clipboard_desktop.dart'; + +final _log = Logger('copy_to_clipboard'); + +/// Attempts to copy a String of `data` to the clipboard. +/// +/// Shows a `successMessage` [Notification] on the passed in `context`. +Future copyToClipboard( + String data, + String? successMessage, +) async { + try { + await Clipboard.setData( + ClipboardData( + text: data, + ), + ); + + if (successMessage != null) notificationService.push(successMessage); + } catch (e) { + _log.warning( + 'DevTools copy failed. This may be as a result of a known bug in VSCode. ' + 'See https://github.com/Dart-Code/Dart-Code/issues/4540 for more ' + 'information. DevTools will now attempt to use a fallback method of ' + 'copying the contents.', + ); + // Trying to use Clipboard.setData to copy in vscode will not work as a + // result of a bug. So we should fallback to `copyToClipboardVSCode` which + // delegates the copy to the frame above DevTools. + // See https://github.com/Dart-Code/Dart-Code/issues/4540 for more + // information. + copyToClipboardVSCode(data); + } +} diff --git a/packages/devtools_app/lib/src/shared/dialogs.dart b/packages/devtools_app/lib/src/shared/dialogs.dart index 6b4abd22e66..f510379aabf 100644 --- a/packages/devtools_app/lib/src/shared/dialogs.dart +++ b/packages/devtools_app/lib/src/shared/dialogs.dart @@ -8,8 +8,8 @@ import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/material.dart'; import '../shared/config_specific/launch_url/launch_url.dart'; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import 'globals.dart'; -import 'utils.dart'; /// A dialog, that reports unexpected error and allows to copy details and create issue. class UnexpectedErrorDialog extends StatelessWidget { diff --git a/packages/devtools_app/lib/src/shared/editable_list.dart b/packages/devtools_app/lib/src/shared/editable_list.dart index c6c08ba4a40..6d013120a67 100644 --- a/packages/devtools_app/lib/src/shared/editable_list.dart +++ b/packages/devtools_app/lib/src/shared/editable_list.dart @@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'common_widgets.dart'; -import 'utils.dart'; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; /// A widget that displays the contents of [entries]. /// diff --git a/packages/devtools_app/lib/src/shared/utils.dart b/packages/devtools_app/lib/src/shared/utils.dart index 3c691b9c361..1baae135377 100644 --- a/packages/devtools_app/lib/src/shared/utils.dart +++ b/packages/devtools_app/lib/src/shared/utils.dart @@ -25,22 +25,6 @@ import 'globals.dart'; final _log = Logger('lib/src/shared/utils'); -/// Attempts to copy a String of `data` to the clipboard. -/// -/// Shows a `successMessage` [Notification] on the passed in `context`. -Future copyToClipboard( - String data, - String? successMessage, -) async { - await Clipboard.setData( - ClipboardData( - text: data, - ), - ); - - if (successMessage != null) notificationService.push(successMessage); -} - /// Logging to debug console only in debug runs. void debugLogger(String message) { assert( From c565fc6f807d20c568b5a713780af27fdf40adc8 Mon Sep 17 00:00:00 2001 From: Dan Chevalier Date: Thu, 26 Oct 2023 11:17:33 -0400 Subject: [PATCH 2/9] better Dart Doc --- .../config_specific/copy_to_clipboard/copy_to_clipboard.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart index dde5221b550..7fed6534f4c 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart @@ -14,7 +14,10 @@ final _log = Logger('copy_to_clipboard'); /// Attempts to copy a String of `data` to the clipboard. /// -/// Shows a `successMessage` [Notification] on the passed in `context`. +/// Shows a `successMessage` [Notification] on the passed in `context`, if the +/// copy is successfully done using the [Clipboard.setData] api. Otherwise it +/// attempts to post the [data] to the parent frame where the parent frame will +/// try to complete the copy (this fallback will only work in VSCode). Future copyToClipboard( String data, String? successMessage, From a34fcef4b7569b9553c67358ce331dabf7227978 Mon Sep 17 00:00:00 2001 From: Dan Chevalier Date: Thu, 26 Oct 2023 11:28:12 -0400 Subject: [PATCH 3/9] release notes --- packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 7ee156f7697..cab39b3ad49 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -19,7 +19,8 @@ video provides a brief tutorial for each DevTools screen. * Fix a bug with service extension states not being cleared on app disconnect. [#6547](https://github.com/flutter/devtools/pull/6547) -- Improved styling of bottom status bar when connected to an app. [#6525](https://github.com/flutter/devtools/pull/6525) +* Improved styling of bottom status bar when connected to an app. [#6525](https://github.com/flutter/devtools/pull/6525) +* Added a work around to fix copy button functionality in VSCode. [#6598](https://github.com/flutter/devtools/pull/6598) ## Inspector updates From b59d13494dcd9366a11005e6decfb3fb817ce936 Mon Sep 17 00:00:00 2001 From: Dan Chevalier Date: Thu, 26 Oct 2023 11:45:23 -0400 Subject: [PATCH 4/9] lint --- packages/devtools_app/lib/src/shared/utils.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/devtools_app/lib/src/shared/utils.dart b/packages/devtools_app/lib/src/shared/utils.dart index 1baae135377..29cd8d3d73b 100644 --- a/packages/devtools_app/lib/src/shared/utils.dart +++ b/packages/devtools_app/lib/src/shared/utils.dart @@ -6,13 +6,10 @@ // other libraries in this package. // Utils, that do not have dependencies, should go to primitives/utils.dart. -import 'dart:async'; - import 'package:devtools_app_shared/service.dart'; import 'package:devtools_app_shared/ui.dart'; import 'package:devtools_app_shared/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; From 74771df3ba35fb62daec75a5b22833f2cb89bf37 Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Sun, 29 Oct 2023 10:03:35 -0700 Subject: [PATCH 5/9] Make SemanticsNode.isMergedIntoParent Readonly Follow up from https://github.com/flutter/flutter/pull/137304 --- .../lib/src/shared/primitives/custom_pointer_scroll_view.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart index d3559224d48..fa46b9e1e05 100644 --- a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart +++ b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart @@ -812,9 +812,7 @@ class _RenderScrollSemantics extends RenderProxyBox { } _innerNode ??= SemanticsNode(showOnScreen: showOnScreen); - _innerNode! - ..isMergedIntoParent = node.isPartOfNodeMerging - ..rect = Offset.zero & node.rect.size; + _innerNode!..rect = Offset.zero & node.rect.size; int? firstVisibleIndex; final List excluded = [_innerNode!]; From 734482265cfefa111b65c2535894310fd1101063 Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Sun, 29 Oct 2023 17:20:04 -0700 Subject: [PATCH 6/9] Simplify unnecessary cascade operator --- .../lib/src/shared/primitives/custom_pointer_scroll_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart index fa46b9e1e05..075e8ffaab9 100644 --- a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart +++ b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart @@ -812,7 +812,7 @@ class _RenderScrollSemantics extends RenderProxyBox { } _innerNode ??= SemanticsNode(showOnScreen: showOnScreen); - _innerNode!..rect = Offset.zero & node.rect.size; + _innerNode!.rect = Offset.zero & node.rect.size; int? firstVisibleIndex; final List excluded = [_innerNode!]; From 8bd5bf0c74b95d6e6c820062dfdaf6ba5af309c9 Mon Sep 17 00:00:00 2001 From: Dan Chevalier Date: Tue, 31 Oct 2023 13:34:52 -0400 Subject: [PATCH 7/9] kenzie cleanup --- .../_copy_to_clipboard_stub.dart | 7 ---- .../_copy_to_clipboard_web.dart | 16 +++++---- .../copy_to_clipboard/copy_to_clipboard.dart | 35 +++++++++++-------- 3 files changed, 29 insertions(+), 29 deletions(-) delete mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart deleted file mode 100644 index 04bef782f20..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_stub.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2023 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -void copyToClipboardVSCode(String _) { - // Do nothing -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart index 5a247b00747..3abbe555819 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart @@ -2,18 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; +import 'dart:js_interop'; +import 'package:js/js_util.dart' as js; +import 'package:web/web.dart'; void copyToClipboardVSCode(String data) { + final message = js.newObject(); + js.setProperty(message, 'command', 'clipboard-write'); + js.setProperty(message, 'data', data); + // This post message is only relevant in VSCode. If the parent frame is // listening for this command, then it will attempt to copy the contents // to the clipboard in the context of the parent frame. window.parent?.postMessage( - { - 'command': 'clipboard-write', - 'data': data, - }, - '*', + message, + '*'.toJS, ); } diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart index 7fed6534f4c..455414e7898 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. +import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; import '../../globals.dart'; -import '_copy_to_clipboard_stub.dart' - if (dart.library.js_interop) '_copy_to_clipboard_web.dart' - if (dart.library.io) '_copy_to_clipboard_desktop.dart'; +import '_copy_to_clipboard_desktop.dart' + if (dart.library.js_interop) '_copy_to_clipboard_web.dart'; final _log = Logger('copy_to_clipboard'); @@ -31,17 +31,22 @@ Future copyToClipboard( if (successMessage != null) notificationService.push(successMessage); } catch (e) { - _log.warning( - 'DevTools copy failed. This may be as a result of a known bug in VSCode. ' - 'See https://github.com/Dart-Code/Dart-Code/issues/4540 for more ' - 'information. DevTools will now attempt to use a fallback method of ' - 'copying the contents.', - ); - // Trying to use Clipboard.setData to copy in vscode will not work as a - // result of a bug. So we should fallback to `copyToClipboardVSCode` which - // delegates the copy to the frame above DevTools. - // See https://github.com/Dart-Code/Dart-Code/issues/4540 for more - // information. - copyToClipboardVSCode(data); + if (ideTheme.embed) { + _log.warning( + 'DevTools copy failed. This may be as a result of a known bug in VSCode. ' + 'See https://github.com/Dart-Code/Dart-Code/issues/4540 for more ' + 'information. DevTools will now attempt to use a fallback method of ' + 'copying the contents.', + ); + // Trying to use Clipboard.setData to copy in vscode will not work as a + // result of a bug. So we should fallback to `copyToClipboardVSCode` which + // delegates the copy to the frame above DevTools. + // Sending this message in other environments will just have no effect. + // See https://github.com/Dart-Code/Dart-Code/issues/4540 for more + // information. + copyToClipboardVSCode(data); + } else { + rethrow; + } } } From a36090964ab474a8b5f4f0d6da736a1519ed805f Mon Sep 17 00:00:00 2001 From: Dan Chevalier Date: Tue, 31 Oct 2023 14:54:25 -0400 Subject: [PATCH 8/9] no unsafe plz --- .../copy_to_clipboard/_copy_to_clipboard_web.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart index 3abbe555819..e00b987428e 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart @@ -3,19 +3,17 @@ // in the LICENSE file. import 'dart:js_interop'; -import 'package:js/js_util.dart' as js; import 'package:web/web.dart'; void copyToClipboardVSCode(String data) { - final message = js.newObject(); - js.setProperty(message, 'command', 'clipboard-write'); - js.setProperty(message, 'data', data); - // This post message is only relevant in VSCode. If the parent frame is // listening for this command, then it will attempt to copy the contents // to the clipboard in the context of the parent frame. window.parent?.postMessage( - message, + { + 'command': 'clipboard-write', + 'data': data, + }.jsify(), '*'.toJS, ); } From ea260990ad3bbfad1e208330a7bde2a87a53d141 Mon Sep 17 00:00:00 2001 From: Daniel Chevalier Date: Tue, 31 Oct 2023 15:19:11 -0400 Subject: [PATCH 9/9] Kenzie Recomend --- .../lib/src/shared/primitives/custom_pointer_scroll_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart index 075e8ffaab9..2727b0f41dd 100644 --- a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart +++ b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart @@ -811,8 +811,8 @@ class _RenderScrollSemantics extends RenderProxyBox { return; } - _innerNode ??= SemanticsNode(showOnScreen: showOnScreen); - _innerNode!.rect = Offset.zero & node.rect.size; + (_innerNode ??= SemanticsNode(showOnScreen: showOnScreen)).rect = node.rect; + int? firstVisibleIndex; final List excluded = [_innerNode!];