From dade589075da95c735058a7b0090b03d5859b8b0 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 23 Aug 2023 23:29:15 +0800 Subject: [PATCH 1/8] fix, enable menu before image Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 84 ++++++++++++---------- src/client.rs | 3 + src/common.rs | 2 +- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 0d51cabdd..8084b230d 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -228,49 +228,61 @@ class _RemotePageState extends State removeSharedStates(widget.id); } + Widget emptyOverlay() => GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + bind.sessionInputOsPassword(sessionId: sessionId, value: ''); + }, + child: BlockableOverlay( + state: _blockableOverlayState, + underlying: Container( + color: Colors.transparent, + ), + ), + ); + Widget buildBody(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay /// see override build() in [BlockableOverlay] - body: BlockableOverlay( - state: _blockableOverlayState, - underlying: Container( - color: Colors.black, - child: RawKeyFocusScope( - focusNode: _rawKeyFocusNode, - onFocusChange: (bool imageFocused) { - debugPrint( - "onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); - // See [onWindowBlur]. - if (Platform.isWindows) { - if (_isWindowBlur) { - imageFocused = false; - Future.delayed(Duration.zero, () { - _rawKeyFocusNode.unfocus(); - }); + body: Stack( + children: [ + Container( + color: Colors.black, + child: RawKeyFocusScope( + focusNode: _rawKeyFocusNode, + onFocusChange: (bool imageFocused) { + debugPrint( + "onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); + // See [onWindowBlur]. + if (Platform.isWindows) { + if (_isWindowBlur) { + imageFocused = false; + Future.delayed(Duration.zero, () { + _rawKeyFocusNode.unfocus(); + }); + } + if (imageFocused) { + _ffi.inputModel.enterOrLeave(true); + } else { + _ffi.inputModel.enterOrLeave(false); + } } - if (imageFocused) { - _ffi.inputModel.enterOrLeave(true); - } else { - _ffi.inputModel.enterOrLeave(false); - } - } - }, - inputModel: _ffi.inputModel, - child: getBodyForDesktop(context))), - upperLayer: [ - OverlayEntry( - builder: (context) => RemoteToolbar( - id: widget.id, - ffi: _ffi, - state: widget.toolbarState, - onEnterOrLeaveImageSetter: (func) => - _onEnterOrLeaveImage4Toolbar = func, - onEnterOrLeaveImageCleaner: () => - _onEnterOrLeaveImage4Toolbar = null, - )) + }, + inputModel: _ffi.inputModel, + child: getBodyForDesktop(context))), + emptyOverlay(), + RemoteToolbar( + id: widget.id, + ffi: _ffi, + state: widget.toolbarState, + onEnterOrLeaveImageSetter: (func) => + _onEnterOrLeaveImage4Toolbar = func, + onEnterOrLeaveImageCleaner: () => + _onEnterOrLeaveImage4Toolbar = null, + ), ], ), ); diff --git a/src/client.rs b/src/client.rs index 9e3479da8..17a62ed54 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2100,6 +2100,9 @@ fn _input_os_password(p: String, activate: bool, interface: impl Interface) { activate_os(&interface); std::thread::sleep(Duration::from_millis(1200)); } + if p.is_empty() { + return; + } let mut key_event = KeyEvent::new(); key_event.press = true; let mut msg_out = Message::new(); diff --git a/src/common.rs b/src/common.rs index 5ad92d914..36ca972b2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -25,7 +25,7 @@ use hbb_common::{ protobuf::Enum, protobuf::Message as _, rendezvous_proto::*, - sleep, socket_client, + socket_client, tcp::FramedStream, tokio, ResultType, }; From f2d96b895f1d29efa0eb3d2504c17f53c952ef7e Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 23 Aug 2023 23:57:09 +0800 Subject: [PATCH 2/8] hide empty waiting layer after images are reached Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 11 +++++++---- flutter/lib/models/model.dart | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 8084b230d..339ecb3f2 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -234,6 +234,8 @@ class _RemotePageState extends State bind.sessionInputOsPassword(sessionId: sessionId, value: ''); }, child: BlockableOverlay( + /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay + /// see override build() in [BlockableOverlay] state: _blockableOverlayState, underlying: Container( color: Colors.transparent, @@ -244,9 +246,6 @@ class _RemotePageState extends State Widget buildBody(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, - - /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay - /// see override build() in [BlockableOverlay] body: Stack( children: [ Container( @@ -273,7 +272,11 @@ class _RemotePageState extends State }, inputModel: _ffi.inputModel, child: getBodyForDesktop(context))), - emptyOverlay(), + Obx( + () => _ffi.ffiModel.waitForFirstImage.isTrue + ? emptyOverlay() + : Offstage(), + ), RemoteToolbar( id: widget.id, ffi: _ffi, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 202216b59..25ba22c70 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -38,8 +38,6 @@ import 'platform_model.dart'; typedef HandleMsgBox = Function(Map evt, String id); typedef ReconnectHandle = Function(OverlayDialogManager, SessionID, bool); -final _waitForImageDialogShow = {}; -final _waitForFirstImage = {}; final _constSessionId = Uuid().v4obj(); class CachedPeerData { @@ -100,6 +98,9 @@ class FfiModel with ChangeNotifier { WeakReference parent; late final SessionID sessionId; + RxBool waitForImageDialogShow = true.obs; + RxBool waitForFirstImage = true.obs; + Map get permissions => _permissions; Display get display => _display; @@ -498,7 +499,7 @@ class FfiModel with ChangeNotifier { closeConnection(); } - if (_waitForFirstImage[sessionId] == false) return; + if (waitForFirstImage.isFalse) return; dialogManager.show( (setState, close, context) => CustomAlertDialog( title: null, @@ -509,7 +510,7 @@ class FfiModel with ChangeNotifier { onCancel: onClose), tag: '$sessionId-waiting-for-image', ); - _waitForImageDialogShow[sessionId] = true; + waitForImageDialogShow.value = true; bind.sessionOnWaitingForImageDialogShow(sessionId: sessionId); } @@ -578,7 +579,7 @@ class FfiModel with ChangeNotifier { } if (displays.isNotEmpty) { _reconnects = 1; - _waitForFirstImage[sessionId] = true; + waitForFirstImage.value = true; } Map features = json.decode(evt['features']); _pi.features.privacyMode = features['privacy_mode'] == 1; @@ -1814,12 +1815,12 @@ class FFI { } void onEvent2UIRgba() async { - if (_waitForImageDialogShow[sessionId] == true) { - _waitForImageDialogShow[sessionId] = false; + if (ffiModel.waitForImageDialogShow.isTrue) { + ffiModel.waitForImageDialogShow.value = false; clearWaitingForImage(dialogManager, sessionId); } - if (_waitForFirstImage[sessionId] == true) { - _waitForFirstImage[sessionId] = false; + if (ffiModel.waitForFirstImage.value == true) { + ffiModel.waitForFirstImage.value = false; dialogManager.dismissAll(); await canvasModel.updateViewStyle(); await canvasModel.updateScrollStyle(); From e17002c6da0b69fb93a80f4ae9153cc409f82a32 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 24 Aug 2023 00:00:18 +0800 Subject: [PATCH 3/8] set right menu duration from 4s to 300s Signed-off-by: fufesou --- flutter/lib/desktop/widgets/tabbar_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 0eb59f51b..148135a81 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -77,7 +77,7 @@ CancelFunc showRightMenu(ToastBuilder builder, targetContext: context, verticalOffset: 0, horizontalOffset: 0, - duration: Duration(seconds: 4), + duration: Duration(seconds: 300), animationDuration: Duration(milliseconds: 0), animationReverseDuration: Duration(milliseconds: 0), preferDirection: PreferDirection.rightTop, From 0e838d59d5cba5ba533b7fd4a9d7acebc0957f69 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 24 Aug 2023 12:03:29 +0800 Subject: [PATCH 4/8] Check if peer info is set Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 24 ++++++++--------- .../lib/desktop/pages/remote_tab_page.dart | 7 ++++- flutter/lib/models/model.dart | 13 +++++++++- src/client.rs | 26 ++++++++++++++----- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 339ecb3f2..f8151e059 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -228,18 +228,12 @@ class _RemotePageState extends State removeSharedStates(widget.id); } - Widget emptyOverlay() => GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - bind.sessionInputOsPassword(sessionId: sessionId, value: ''); - }, - child: BlockableOverlay( - /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay - /// see override build() in [BlockableOverlay] - state: _blockableOverlayState, - underlying: Container( - color: Colors.transparent, - ), + Widget emptyOverlay() => BlockableOverlay( + /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay + /// see override build() in [BlockableOverlay] + state: _blockableOverlayState, + underlying: Container( + color: Colors.transparent, ), ); @@ -273,7 +267,8 @@ class _RemotePageState extends State inputModel: _ffi.inputModel, child: getBodyForDesktop(context))), Obx( - () => _ffi.ffiModel.waitForFirstImage.isTrue + () => _ffi.ffiModel.pi.isSet.isTrue && + _ffi.ffiModel.waitForFirstImage.isTrue ? emptyOverlay() : Offstage(), ), @@ -286,6 +281,9 @@ class _RemotePageState extends State onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Toolbar = null, ), + Obx( + () => _ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(), + ), ], ), ); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 51bcec51d..1dd291e3b 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -20,6 +20,7 @@ import 'package:get/get.dart'; import 'package:bot_toast/bot_toast.dart'; import '../../common/widgets/dialog.dart'; +import '../../models/model.dart'; import '../../models/platform_model.dart'; class _MenuTheme { @@ -266,7 +267,11 @@ class _ConnectionTabPageState extends State { if (e.kind != ui.PointerDeviceKind.mouse) { return; } - if (e.buttons == 2) { + final remotePage = tabController.state.value.tabs + .firstWhere((tab) => tab.key == key) + .page as RemotePage; + if (remotePage.ffi.ffiModel.pi.isSet.isTrue && + e.buttons == 2) { showRightMenu( (CancelFunc cancelFunc) { return _tabMenuBuilder(key, cancelFunc); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 25ba22c70..00b344e13 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -99,6 +99,7 @@ class FfiModel with ChangeNotifier { late final SessionID sessionId; RxBool waitForImageDialogShow = true.obs; + Timer? waitForImageTimer; RxBool waitForFirstImage = true.obs; Map get permissions => _permissions; @@ -159,6 +160,7 @@ class FfiModel with ChangeNotifier { _timer?.cancel(); _timer = null; clearPermissions(); + waitForImageTimer?.cancel(); } setConnectionType(String peerId, bool secure, bool direct) { @@ -511,6 +513,11 @@ class FfiModel with ChangeNotifier { tag: '$sessionId-waiting-for-image', ); waitForImageDialogShow.value = true; + waitForImageTimer = Timer(Duration(milliseconds: 1500), () { + if (waitForFirstImage.isTrue) { + bind.sessionInputOsPassword(sessionId: sessionId, value: ''); + } + }); bind.sessionOnWaitingForImageDialogShow(sessionId: sessionId); } @@ -603,6 +610,7 @@ class FfiModel with ChangeNotifier { } } + _pi.isSet.value = true; stateGlobal.resetLastResolutionGroupValues(peerId); notifyListeners(); @@ -1817,6 +1825,7 @@ class FFI { void onEvent2UIRgba() async { if (ffiModel.waitForImageDialogShow.isTrue) { ffiModel.waitForImageDialogShow.value = false; + ffiModel.waitForImageTimer?.cancel(); clearWaitingForImage(dialogManager, sessionId); } if (ffiModel.waitForFirstImage.value == true) { @@ -1935,7 +1944,7 @@ class Features { bool privacyMode = false; } -class PeerInfo { +class PeerInfo with ChangeNotifier { String version = ''; String username = ''; String hostname = ''; @@ -1947,6 +1956,8 @@ class PeerInfo { List resolutions = []; Map platform_additions = {}; + RxBool isSet = false.obs; + bool get is_wayland => platform_additions['is_wayland'] == true; bool get is_headless => platform_additions['headless'] == true; } diff --git a/src/client.rs b/src/client.rs index 17a62ed54..253c293ee 100644 --- a/src/client.rs +++ b/src/client.rs @@ -57,7 +57,10 @@ use scrap::{ ImageFormat, ImageRgb, }; -use crate::is_keyboard_mode_supported; +use crate::{ + common::input::{MOUSE_BUTTON_LEFT, MOUSE_TYPE_DOWN, MOUSE_TYPE_UP}, + is_keyboard_mode_supported, +}; #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -2057,13 +2060,20 @@ pub fn send_pointer_device_event( /// # Arguments /// /// * `interface` - The interface for sending data. -fn activate_os(interface: &impl Interface) { +/// * `send_click` - Whether to send a click event. +fn activate_os(interface: &impl Interface, send_click: bool) { + let left_down = MOUSE_BUTTON_LEFT << 3 | MOUSE_TYPE_DOWN; + let left_up = MOUSE_BUTTON_LEFT << 3 | MOUSE_TYPE_UP; + send_mouse(left_up, 0, 0, false, false, false, false, interface); + std::thread::sleep(Duration::from_millis(50)); send_mouse(0, 0, 0, false, false, false, false, interface); std::thread::sleep(Duration::from_millis(50)); send_mouse(0, 3, 3, false, false, false, false, interface); - std::thread::sleep(Duration::from_millis(50)); - send_mouse(1 | 1 << 3, 0, 0, false, false, false, false, interface); - send_mouse(2 | 1 << 3, 0, 0, false, false, false, false, interface); + if send_click { + std::thread::sleep(Duration::from_millis(50)); + send_mouse(left_down, 0, 0, false, false, false, false, interface); + send_mouse(left_up, 0, 0, false, false, false, false, interface); + } /* let mut key_event = KeyEvent::new(); // do not use Esc, which has problem with Linux @@ -2096,11 +2106,13 @@ pub fn input_os_password(p: String, activate: bool, interface: impl Interface) { /// * `activate` - Whether to activate OS. /// * `interface` - The interface for sending data. fn _input_os_password(p: String, activate: bool, interface: impl Interface) { + let input_password = !p.is_empty(); if activate { - activate_os(&interface); + // Click event is used to bring up the password input box. + activate_os(&interface, input_password); std::thread::sleep(Duration::from_millis(1200)); } - if p.is_empty() { + if !input_password { return; } let mut key_event = KeyEvent::new(); From 257227920d7da51d0c4c5d38cfdd56051572bb56 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 24 Aug 2023 12:24:08 +0800 Subject: [PATCH 5/8] Fix missing menu action Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_tab_page.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 1dd291e3b..4467adad4 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -20,7 +20,6 @@ import 'package:get/get.dart'; import 'package:bot_toast/bot_toast.dart'; import '../../common/widgets/dialog.dart'; -import '../../models/model.dart'; import '../../models/platform_model.dart'; class _MenuTheme { @@ -359,7 +358,15 @@ class _ConnectionTabPageState extends State { )); } - if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) {} + if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) { + menu.add(RemoteMenuEntry.insertLock(sessionId, padding, + dismissFunc: cancelFunc)); + + if (pi.platform == kPeerPlatformLinux || pi.sasEnabled) { + menu.add(RemoteMenuEntry.insertCtrlAltDel(sessionId, padding, + dismissFunc: cancelFunc)); + } + } menu.addAll([ MenuEntryDivider(), From c1a577797aef7b23fa4d80d34b6d7fca0dffc33d Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 24 Aug 2023 12:50:39 +0800 Subject: [PATCH 6/8] Revert Ctrl+Alt+Del in toolbar right menu Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_tab_page.dart | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 4467adad4..bc4c6b243 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -358,15 +358,7 @@ class _ConnectionTabPageState extends State { )); } - if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) { - menu.add(RemoteMenuEntry.insertLock(sessionId, padding, - dismissFunc: cancelFunc)); - - if (pi.platform == kPeerPlatformLinux || pi.sasEnabled) { - menu.add(RemoteMenuEntry.insertCtrlAltDel(sessionId, padding, - dismissFunc: cancelFunc)); - } - } + if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) {} menu.addAll([ MenuEntryDivider(), From 56ff88934f238416d6c4ddd996191ddf55f30981 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 24 Aug 2023 13:07:36 +0800 Subject: [PATCH 7/8] Add RemoteToolbar to Obx(()) to rebuild after pi is recved Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index f8151e059..091b0afaf 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -266,24 +266,24 @@ class _RemotePageState extends State }, inputModel: _ffi.inputModel, child: getBodyForDesktop(context))), - Obx( - () => _ffi.ffiModel.pi.isSet.isTrue && - _ffi.ffiModel.waitForFirstImage.isTrue - ? emptyOverlay() - : Offstage(), - ), - RemoteToolbar( - id: widget.id, - ffi: _ffi, - state: widget.toolbarState, - onEnterOrLeaveImageSetter: (func) => - _onEnterOrLeaveImage4Toolbar = func, - onEnterOrLeaveImageCleaner: () => - _onEnterOrLeaveImage4Toolbar = null, - ), - Obx( - () => _ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(), - ), + Obx(() => Stack( + children: [ + _ffi.ffiModel.pi.isSet.isTrue && + _ffi.ffiModel.waitForFirstImage.isTrue + ? emptyOverlay() + : Offstage(), + RemoteToolbar( + id: widget.id, + ffi: _ffi, + state: widget.toolbarState, + onEnterOrLeaveImageSetter: (func) => + _onEnterOrLeaveImage4Toolbar = func, + onEnterOrLeaveImageCleaner: () => + _onEnterOrLeaveImage4Toolbar = null, + ), + _ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(), + ], + )), ], ), ); From 9937650062e4185ba18b13625738b98550eb4e8d Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 24 Aug 2023 14:40:02 +0800 Subject: [PATCH 8/8] Use Overlay to wrap RemoteToolbar to enable rebuild everytime on click Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 091b0afaf..7e6e8ff86 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -238,6 +238,14 @@ class _RemotePageState extends State ); Widget buildBody(BuildContext context) { + remoteToolbar(BuildContext context) => RemoteToolbar( + id: widget.id, + ffi: _ffi, + state: widget.toolbarState, + onEnterOrLeaveImageSetter: (func) => + _onEnterOrLeaveImage4Toolbar = func, + onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Toolbar = null, + ); return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, body: Stack( @@ -272,15 +280,12 @@ class _RemotePageState extends State _ffi.ffiModel.waitForFirstImage.isTrue ? emptyOverlay() : Offstage(), - RemoteToolbar( - id: widget.id, - ffi: _ffi, - state: widget.toolbarState, - onEnterOrLeaveImageSetter: (func) => - _onEnterOrLeaveImage4Toolbar = func, - onEnterOrLeaveImageCleaner: () => - _onEnterOrLeaveImage4Toolbar = null, - ), + // Use Overlay to enable rebuild every time on menu button click. + _ffi.ffiModel.pi.isSet.isTrue + ? Overlay(initialEntries: [ + OverlayEntry(builder: remoteToolbar) + ]) + : remoteToolbar(context), _ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(), ], )),