You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter全局持久化栏位置适配:按需置于bottomNavigationBar上方或底部

解决Flutter全局底部栏自适应页面底部导航的问题

方案一:通过NavigatorObserver监听页面状态+状态管理实现动态定位

核心思路是监听页面的入栈出栈,判断当前页面的Scaffold是否包含bottomNavigationBar,然后动态调整全局栏的bottom值。

  1. 定义状态管理类,保存当前页面是否有底部导航的状态:
class GlobalBarPositionNotifier extends ChangeNotifier {
  bool _hasBottomNav = false;

  bool get hasBottomNav => _hasBottomNav;

  set hasBottomNav(bool value) {
    _hasBottomNav = value;
    notifyListeners();
  }
}
  1. 自定义NavigatorObserver,监听页面变化并更新状态:
class PageNavObserver extends NavigatorObserver {
  final GlobalBarPositionNotifier notifier;

  PageNavObserver(this.notifier);

  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    super.didPush(route, previousRoute);
    _checkBottomNav(route);
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    super.didPop(route, previousRoute);
    if (previousRoute != null) {
      _checkBottomNav(previousRoute);
    }
  }

  void _checkBottomNav(Route<dynamic> route) {
    if (route is MaterialPageRoute) {
      final widget = route.builder(navigator!.context);
      if (widget is Scaffold) {
        notifier.hasBottomNav = widget.bottomNavigationBar != null;
      } else {
        // 页面非直接返回Scaffold时,延迟查找子树中的Scaffold
        WidgetsBinding.instance.addPostFrameCallback((_) {
          final scaffoldState = Scaffold.maybeOf(navigator!.context);
          notifier.hasBottomNav = scaffoldState?.widget.bottomNavigationBar != null;
        });
      }
    }
  }
}
  1. 在MaterialApp中配置使用:
void main() {
  final positionNotifier = GlobalBarPositionNotifier();
  runApp(
    ChangeNotifierProvider.value(
      value: positionNotifier,
      child: MyApp(positionNotifier: positionNotifier),
    ),
  );
}

class MyApp extends StatelessWidget {
  final GlobalBarPositionNotifier positionNotifier;

  const MyApp({super.key, required this.positionNotifier});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [PageNavObserver(positionNotifier)],
      builder: (context, child) {
        return Stack(
          children: [
            child!,
            Consumer<GlobalBarPositionNotifier>(
              builder: (context, notifier, _) {
                return Positioned(
                  left: 0,
                  right: 0,
                  bottom: notifier.hasBottomNav ? kBottomNavigationBarHeight : 0,
                  child: Container(
                    height: 50,
                    color: Colors.blue,
                    child: const Center(child: Text('全局底部栏')),
                  ),
                );
              },
            ),
          ],
        );
      },
      home: const HomePage(), // 带bottomNavigationBar的页面
    );
  }
}

方案二:通过InheritedWidget在页面间传递状态

如果不想引入状态管理库,可通过InheritedWidget传递当前页面的底部导航状态:

  1. 定义InheritedWidget:
class PageBottomNavConfig extends InheritedWidget {
  final bool hasBottomNav;

  const PageBottomNavConfig({
    super.key,
    required this.hasBottomNav,
    required super.child,
  });

  static PageBottomNavConfig? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<PageBottomNavConfig>();
  }

  @override
  bool updateShouldNotify(PageBottomNavConfig oldWidget) {
    return hasBottomNav != oldWidget.hasBottomNav;
  }
}
  1. 在每个页面的Scaffold外层包裹该组件:
// 带底部导航的页面
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return PageBottomNavConfig(
      hasBottomNav: true,
      child: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: const [
            BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
            BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          ],
          currentIndex: 0,
        ),
        body: const Center(child: Text('首页')),
      ),
    );
  }
}

// 不带底部导航的页面
class DetailPage extends StatelessWidget {
  const DetailPage({super.key});

  @override
  Widget build(BuildContext context) {
    return PageBottomNavConfig(
      hasBottomNav: false,
      child: Scaffold(
        body: const Center(child: Text('详情页')),
      ),
    );
  }
}
  1. 在全局栏中获取配置并调整位置:
builder: (context, child) {
  return Stack(
    children: [
      child!,
      Builder(
        builder: (context) {
          final config = PageBottomNavConfig.of(context);
          final bottomOffset = config?.hasBottomNav ?? false ? kBottomNavigationBarHeight : 0;
          return Positioned(
            left: 0,
            right: 0,
            bottom: bottomOffset,
            child: Container(
              height: 50,
              color: Colors.blue,
              child: const Center(child: Text('全局底部栏')),
            ),
          );
        },
      ),
    ],
  );
},

注意事项

  • 方案一中,若页面是嵌套Scaffold,可扩展_checkBottomNav方法递归查找子树中的Scaffold,保证判断准确。
  • 方案二需要每个页面手动包裹PageBottomNavConfig,适合页面数量较少的场景。

内容的提问来源于stack exchange,提问作者Ufder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:30:54