diff --git a/super_editor/example/lib/demos/interaction_spot_checks/toolbar_following_content_in_layer.dart b/super_editor/example/lib/demos/interaction_spot_checks/toolbar_following_content_in_layer.dart index 9044b19cdd..26cf399706 100644 --- a/super_editor/example/lib/demos/interaction_spot_checks/toolbar_following_content_in_layer.dart +++ b/super_editor/example/lib/demos/interaction_spot_checks/toolbar_following_content_in_layer.dart @@ -23,92 +23,65 @@ class _ToolbarFollowingContentInLayerState extends State(0); - OverlayState? _ancestorOverlay; - late final OverlayEntry _toolbarEntry; + final OverlayPortalController _overlayPortalController = OverlayPortalController(); @override void initState() { super.initState(); - _toolbarEntry = OverlayEntry(builder: (_) { - return _buildToolbarOverlay(); - }); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - - // Any time our dependencies change, our ancestor Overlay may have changed. - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - final newOverlay = Overlay.of(context); - if (newOverlay == _ancestorOverlay) { - // Overlay didn't change. Nothing to do. - return; - } - - if (_ancestorOverlay != null) { - _toolbarEntry.remove(); - } - - _ancestorOverlay = newOverlay; - newOverlay.insert(_toolbarEntry); - }); - } - - @override - void dispose() { - _toolbarEntry.remove(); - - super.dispose(); + _overlayPortalController.show(); } @override Widget build(BuildContext context) { - return SpotCheckScaffold( - content: KeyedSubtree( - key: _viewportKey, - child: ContentLayers( - overlays: [ - (_) => LeaderLayoutLayer( - leaderLink: _leaderLink, - leaderBoundsKey: _leaderBoundsKey, - ), - ], - content: (_) => Center( - child: Column( - children: [ - const Spacer(), - ValueListenableBuilder( - valueListenable: _expansionExtent, - builder: (context, expansionExtent, _) { - return Container( - height: 12, - width: _baseContentWidth + (2 * expansionExtent) + 2, // +2 for border - decoration: BoxDecoration( - border: Border.all(color: Colors.white.withOpacity(0.1)), - ), - child: Align( - alignment: Alignment.centerLeft, - child: Container( - key: _leaderBoundsKey, - width: _baseContentWidth + expansionExtent, - height: 10, - color: Colors.white.withOpacity(0.2), + return OverlayPortal( + controller: _overlayPortalController, + overlayChildBuilder: _buildToolbarOverlay, + child: SpotCheckScaffold( + content: KeyedSubtree( + key: _viewportKey, + child: ContentLayers( + overlays: [ + (_) => LeaderLayoutLayer( + leaderLink: _leaderLink, + leaderBoundsKey: _leaderBoundsKey, + ), + ], + content: (_) => Center( + child: Column( + children: [ + const Spacer(), + ValueListenableBuilder( + valueListenable: _expansionExtent, + builder: (context, expansionExtent, _) { + return Container( + height: 12, + width: _baseContentWidth + (2 * expansionExtent) + 2, // +2 for border + decoration: BoxDecoration( + border: Border.all(color: Colors.white.withOpacity(0.1)), ), - ), - ); - }, - ), - const SizedBox(height: 96), - TextButton( - onPressed: () { - _expansionExtent.value = Random().nextDouble() * 200; - }, - child: Text("Change Size"), - ), - const Spacer(), - ], + child: Align( + alignment: Alignment.centerLeft, + child: Container( + key: _leaderBoundsKey, + width: _baseContentWidth + expansionExtent, + height: 10, + color: Colors.white.withOpacity(0.2), + ), + ), + ); + }, + ), + const SizedBox(height: 96), + TextButton( + onPressed: () { + _expansionExtent.value = Random().nextDouble() * 200; + }, + child: Text("Change Size"), + ), + const Spacer(), + ], + ), ), ), ), @@ -116,7 +89,7 @@ class _ToolbarFollowingContentInLayerState extends State { + final OverlayPortalController _overlayPortalController = OverlayPortalController(); SuperEditorIosControlsController? _controlsController; - OverlayEntry? _toolbarOverlayEntry; @override void didChangeDependencies() { super.didChangeDependencies(); _controlsController = SuperEditorIosControlsScope.rootOf(context); - - // Add our overlay on the next frame. If we did it immediately, it would - // cause a setState() to be called during didChangeDependencies, which is - // a framework violation. - onNextFrame((timeStamp) { - _addToolbarOverlay(); - }); - } - - @override - void dispose() { - _removeToolbarOverlay(); - super.dispose(); + _overlayPortalController.show(); } @visibleForTesting bool get wantsToDisplayToolbar => _controlsController!.shouldShowToolbar.value; - void _addToolbarOverlay() { - if (_toolbarOverlayEntry != null) { - return; - } - - _toolbarOverlayEntry = OverlayEntry(builder: (overlayContext) { - return IosFloatingToolbarOverlay( - shouldShowToolbar: _controlsController!.shouldShowToolbar, - toolbarFocalPoint: _controlsController!.toolbarFocalPoint, - floatingToolbarBuilder: - _controlsController!.toolbarBuilder ?? widget.defaultToolbarBuilder ?? (_, __, ___) => const SizedBox(), - createOverlayControlsClipper: _controlsController!.createOverlayControlsClipper, - showDebugPaint: false, - ); - }); - - Overlay.of(context).insert(_toolbarOverlayEntry!); - } - - void _removeToolbarOverlay() { - if (_toolbarOverlayEntry == null) { - return; - } - - _toolbarOverlayEntry!.remove(); - _toolbarOverlayEntry = null; - } - @override Widget build(BuildContext context) { - return widget.child ?? const SizedBox(); + return OverlayPortal( + controller: _overlayPortalController, + overlayChildBuilder: _buildToolbar, + child: widget.child ?? const SizedBox(), + ); + } + + Widget _buildToolbar(BuildContext context) { + return IosFloatingToolbarOverlay( + shouldShowToolbar: _controlsController!.shouldShowToolbar, + toolbarFocalPoint: _controlsController!.toolbarFocalPoint, + floatingToolbarBuilder: + _controlsController!.toolbarBuilder ?? widget.defaultToolbarBuilder ?? (_, __, ___) => const SizedBox(), + createOverlayControlsClipper: _controlsController!.createOverlayControlsClipper, + showDebugPaint: false, + ); } } @@ -1452,61 +1427,30 @@ class SuperEditorIosMagnifierOverlayManager extends StatefulWidget { @visibleForTesting class SuperEditorIosMagnifierOverlayManagerState extends State { + final OverlayPortalController _overlayPortalController = OverlayPortalController(); SuperEditorIosControlsController? _controlsController; - OverlayEntry? _magnifierOverlayEntry; - - @override - void initState() { - super.initState(); - - // Add our overlay on the next frame. If we did it immediately, it would - // cause a setState() to be called during didChangeDependencies, which is - // a framework violation. - onNextFrame((timeStamp) { - _addMagnifierOverlay(); - }); - } @override void didChangeDependencies() { super.didChangeDependencies(); _controlsController = SuperEditorIosControlsScope.rootOf(context); - } - - @override - void dispose() { - _removeMagnifierOverlay(); - super.dispose(); + _overlayPortalController.show(); } @visibleForTesting bool get wantsToDisplayMagnifier => _controlsController!.shouldShowMagnifier.value; - void _addMagnifierOverlay() { - if (_magnifierOverlayEntry != null) { - return; - } - - _magnifierOverlayEntry = OverlayEntry(builder: (_) => _buildMagnifier()); - Overlay.of(context).insert(_magnifierOverlayEntry!); - } - - void _removeMagnifierOverlay() { - if (_magnifierOverlayEntry == null) { - return; - } - - _magnifierOverlayEntry!.remove(); - _magnifierOverlayEntry = null; - } - @override Widget build(BuildContext context) { - return widget.child ?? const SizedBox(); + return OverlayPortal( + controller: _overlayPortalController, + overlayChildBuilder: _buildMagnifier, + child: widget.child ?? const SizedBox(), + ); } - Widget _buildMagnifier() { + Widget _buildMagnifier(BuildContext context) { // Display a magnifier that tracks a focal point. // // When the user is dragging an overlay handle, SuperEditor diff --git a/super_editor/lib/src/super_reader/read_only_document_ios_touch_interactor.dart b/super_editor/lib/src/super_reader/read_only_document_ios_touch_interactor.dart index fbccea1941..ee3ab2ed2b 100644 --- a/super_editor/lib/src/super_reader/read_only_document_ios_touch_interactor.dart +++ b/super_editor/lib/src/super_reader/read_only_document_ios_touch_interactor.dart @@ -1056,8 +1056,8 @@ class SuperReaderIosToolbarOverlayManager extends StatefulWidget { @visibleForTesting class SuperReaderIosToolbarOverlayManagerState extends State { + final OverlayPortalController _overlayPortalController = OverlayPortalController(); SuperReaderIosControlsController? _controlsContext; - OverlayEntry? _toolbarOverlayEntry; @visibleForTesting bool get wantsToDisplayToolbar => _controlsContext!.shouldShowToolbar.value; @@ -1067,52 +1067,27 @@ class SuperReaderIosToolbarOverlayManagerState extends State const SizedBox(), - createOverlayControlsClipper: _controlsContext!.createOverlayControlsClipper, - showDebugPaint: false, - ); - }); - - Overlay.of(context).insert(_toolbarOverlayEntry!); - } - - void _removeToolbarOverlay() { - if (_toolbarOverlayEntry == null) { - return; - } - - _toolbarOverlayEntry!.remove(); - _toolbarOverlayEntry = null; + Widget build(BuildContext context) { + return OverlayPortal( + controller: _overlayPortalController, + overlayChildBuilder: _buildToolbar, + child: widget.child ?? const SizedBox(), + ); } - @override - Widget build(BuildContext context) { - return widget.child ?? const SizedBox(); + Widget _buildToolbar(BuildContext context) { + return IosFloatingToolbarOverlay( + shouldShowToolbar: _controlsContext!.shouldShowToolbar, + toolbarFocalPoint: _controlsContext!.toolbarFocalPoint, + floatingToolbarBuilder: + _controlsContext!.toolbarBuilder ?? widget.defaultToolbarBuilder ?? (_, __, ___) => const SizedBox(), + createOverlayControlsClipper: _controlsContext!.createOverlayControlsClipper, + showDebugPaint: false, + ); } } @@ -1132,61 +1107,30 @@ class SuperReaderIosMagnifierOverlayManager extends StatefulWidget { @visibleForTesting class SuperReaderIosMagnifierOverlayManagerState extends State { + final OverlayPortalController _overlayPortalController = OverlayPortalController(); SuperReaderIosControlsController? _controlsContext; - OverlayEntry? _magnifierOverlayEntry; @visibleForTesting bool get wantsToDisplayMagnifier => _controlsContext!.shouldShowMagnifier.value; - @override - void initState() { - super.initState(); - - // Add our overlay on the next frame. If we did it immediately, it would - // cause a setState() to be called during initState(), which is - // a framework violation. - onNextFrame((timeStamp) { - _addMagnifierOverlay(); - }); - } - @override void didChangeDependencies() { super.didChangeDependencies(); _controlsContext = SuperReaderIosControlsScope.rootOf(context); - } - - @override - void dispose() { - _removeMagnifierOverlay(); - super.dispose(); - } - - void _addMagnifierOverlay() { - if (_magnifierOverlayEntry != null) { - return; - } - - _magnifierOverlayEntry = OverlayEntry(builder: (_) => _buildMagnifier()); - Overlay.of(context).insert(_magnifierOverlayEntry!); - } - - void _removeMagnifierOverlay() { - if (_magnifierOverlayEntry == null) { - return; - } - - _magnifierOverlayEntry!.remove(); - _magnifierOverlayEntry = null; + _overlayPortalController.show(); } @override Widget build(BuildContext context) { - return widget.child ?? const SizedBox(); + return OverlayPortal( + controller: _overlayPortalController, + overlayChildBuilder: _buildMagnifier, + child: widget.child ?? const SizedBox(), + ); } - Widget _buildMagnifier() { + Widget _buildMagnifier(BuildContext context) { // Display a magnifier that tracks a focal point. // // When the user is dragging an overlay handle, SuperEditor