From b30a0dc7eb428ec3b83e04691673f6d9699ff4e8 Mon Sep 17 00:00:00 2001 From: wm Date: Thu, 30 Jul 2026 09:53:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B2=89=E6=B5=B8=E5=85=89?= =?UTF-8?q?=E6=84=9F=E5=BA=95=E6=A0=8F=E4=B8=8E=20Flutter=20=E5=BA=95?= =?UTF-8?q?=E6=A0=8F=E9=87=8D=E5=8F=A0=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MainController.useNativeTabs 由 _initHdsBar 异步赋值(需等 getDeviceInfo 走完 method channel),首帧一定是按 false 构建的,此时 Flutter 底栏已经建出来了。 为规避 ObxError 去掉了 Scaffold.bottomNavigationBar 外层的 Obx,useNativeTabs 就绪后不再有任何东西触发重建,Flutter 底栏就一直留在原生 HDS底栏下面,两条底栏同时显示(冷启动进首页即可复现,直到有别的原因触发重建——切页面、转屏——才会消失)。 - main/view.dart:initState 中的 ever 改为在 useNativeTabs 置真时 setState,与通知原生显示底栏是同一时机,两条底栏一进一出不留空档;worker 存起来在 dispose 中释放。_bottomNav 保持非响应式读取(提前返回分支不读 Rx,包 Obx 会抛 ObxError),在注释里写明重建来源,避免以后又被改回 Obx。 - 顺带补发首帧因 useNativeTabs 未就绪而跳过的显隐同步:横屏(侧栏布局)启动或就绪前已有子页面覆盖主页时,原生底栏不该跟着冒出来。 - shell_bars_observer.dart:onOrientationChanged 去掉“有子页面时直接返回”与 didPop/didRemove 里清零 _orientationHidden 的兜底——两者配合会让方向标记过期:横屏下进子页面再返回,标记被清成竖屏,原生底栏又和侧栏一起显示。现在方向变化一律记录,显隐由“路由栈深度 > 1 || 非底栏布局”统一决定。 行为变化:横屏(侧栏布局)下原生 HDS 底栏统一不显示,导航交给左侧 NavigationRail。此前只在横屏冷启动、或横屏下进子页面再返回这两条路径会漏出底栏,属于状态不一致,现统一为隐藏。 --- lib/harmony_adapt/shell_bars_observer.dart | 15 ++++--------- lib/pages/main/view.dart | 26 +++++++++++++++++----- pubspec.yaml | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/harmony_adapt/shell_bars_observer.dart b/lib/harmony_adapt/shell_bars_observer.dart index cfff72f377..6aa2d49324 100644 --- a/lib/harmony_adapt/shell_bars_observer.dart +++ b/lib/harmony_adapt/shell_bars_observer.dart @@ -16,19 +16,12 @@ class ShellBarsObserver extends NavigatorObserver { @override void didPop(Route route, Route? previousRoute) { _activeRoutes.remove(route); - // 回到主页时清除方向隐藏标记,由 _sync 决定最终状态 - if (_activeRoutes.length <= 1) { - _orientationHidden = false; - } _sync(); } @override void didRemove(Route route, Route? previousRoute) { _activeRoutes.remove(route); - if (_activeRoutes.length <= 1) { - _orientationHidden = false; - } _sync(); } @@ -39,10 +32,10 @@ class ShellBarsObserver extends NavigatorObserver { _sync(); } - /// 由 didChangeDependencies 调用:横屏时隐藏底栏(仅在主页时生效) - void onOrientationChanged(bool isPortrait) { - if (_activeRoutes.length > 1) return; // 有子页面时由路由控制 - _orientationHidden = !isPortrait; + /// 由 MainApp.didChangeDependencies 调用:非竖屏底栏布局(横屏侧栏)时隐藏底栏。 + /// 子页面覆盖期间也照常记录,避免返回主页后用的是过期方向。 + void onOrientationChanged(bool useBottomNav) { + _orientationHidden = !useBottomNav; _sync(); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index c8589ef38f..94e18503e3 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -53,6 +53,7 @@ class _MainAppState extends PopScopeState int _primaryColorValue = 0; late ThemeData theme; Brightness? _brightness; + Worker? _nativeTabsWorker; @override bool get initCanPop => false; @@ -61,9 +62,19 @@ class _MainAppState extends PopScopeState void initState() { super.initState(); addObserverMobile(this); - // 监听 useNativeTabs 异步赋值(_initHdsBar 完成时触发) - ever(_mainController.useNativeTabs, (_) { - if (_mainController.useNativeTabs.value && _primaryColorValue != 0) { + // 监听 useNativeTabs 异步赋值(_initHdsBar 完成时触发)。 + // 首帧是按 false 构建的,此时 Flutter 底栏已经建出来了,而 _bottomNav 中 + // 的提前返回分支不读任何 Rx(不能用 Obx 包裹,否则抛 ObxError),所以必须 + // 在这里主动重建把它移除,否则会与原生 HDS 底栏重叠显示。 + _nativeTabsWorker = ever(_mainController.useNativeTabs, (useNativeTabs) { + if (!mounted || !useNativeTabs) return; + setState(() {}); + // 补发首帧时因 useNativeTabs 未就绪而跳过的原生底栏状态同步: + // 横屏(侧栏布局)或已有子页面覆盖主页时,原生底栏不应显示 + MyApp.shellBarsObserver.onOrientationChanged( + _mainController.useBottomNav, + ); + if (_primaryColorValue != 0) { HarmonyChannel.setTabSelectedColor( '#${_primaryColorValue.toRadixString(16).padLeft(8, '0').substring(2)}', ); @@ -103,7 +114,9 @@ class _MainAppState extends PopScopeState // 横竖屏切换时同步原生 HDS 沉浸底栏显隐 // 由 ShellBarsObserver 统一管理,避免与路由观察者冲突 if (_mainController.useNativeTabs.value) { - MyApp.shellBarsObserver.onOrientationChanged(_mainController.useBottomNav); + MyApp.shellBarsObserver.onOrientationChanged( + _mainController.useBottomNav, + ); } // 缓存主题主色,供 ever 回调在 useNativeTabs 异步就绪后补发 _primaryColorValue = Theme.of(context).colorScheme.primary.value; @@ -143,6 +156,7 @@ class _MainAppState extends PopScopeState @override void dispose() { + _nativeTabsWorker?.dispose(); if (PlatformUtils.isDesktop) { trayManager.removeListener(this); windowManager.removeListener(this); @@ -317,7 +331,9 @@ class _MainAppState extends PopScopeState if (!_mainController.useBottomNav) { return const SizedBox.shrink(); } - // 开启鸿蒙沉浸光感后需返回空组件 + // 开启鸿蒙沉浸光感后需返回空组件(底栏由原生 HDS 渲染)。 + // 这里是非响应式读取,异步就绪后的重建由 initState 中的 _nativeTabsWorker + // 触发;上面的提前返回分支不读 Rx,不能改成 Obx 包裹(会抛 ObxError) if (_mainController.useNativeTabs.value) { return const SizedBox.shrink(); } diff --git a/pubspec.yaml b/pubspec.yaml index 60b6c72b99..dd09c4233f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. # update when release -version: 2.1.0+5540 +version: 2.1.0+5543 environment: sdk: ">=3.11.1"