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

Flutter中BottomNavigationBar返回后选中项不更新问题求助

问题:BottomNavigationBar返回页面后选中状态未同步更新
  • 处于Booking页面时,切换到Teams、Highlights或Stats等底部导航页面,按下返回键回到原页面,BottomNavigationBar的选中项未同步更新,仍保持之前页面的选中状态。
  • 示例:点击底部导航的Stats项进入Stats页面,按返回键回到Booking页面,但底部导航仍显示Stats为绿色选中状态,与当前页面不匹配。

原HomeView代码

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:sportifan_user/screens/main/bookings.dart';
import 'package:sportifan_user/screens/main/choose_match.dart';
import 'package:sportifan_user/screens/main/create_team.dart';
import 'package:sportifan_user/screens/main/edit_team.dart';
import 'package:sportifan_user/screens/main/highlights.dart';
import 'package:sportifan_user/screens/main/stats.dart';
import 'package:sportifan_user/screens/main/venue_screen.dart';
import 'package:sportifan_user/screens/main/your_teams.dart';
import 'package:sportifan_user/widgets/highlights_widget.dart';
import 'package:sportifan_user/widgets/sidebar.dart';
import 'package:sportifan_user/widgets/top_navigation_bar.dart';

class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> with RouteAware {
  int currentIndex = 0;

  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  void onTap(int index) {
    setState(() {
      currentIndex = index;
    });
    switch (index) {
      case 0:
        navigatorKey.currentState?.pushNamed('/');
        break;
      case 1:
        navigatorKey.currentState?.pushNamed('/teams');
        break;
      case 2:
        navigatorKey.currentState?.pushNamed('/highlights');
        break;
      case 3:
        navigatorKey.currentState?.pushNamed('/stats');
        break;
    }
  }

  final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPopNext() {
    _updateCurrentIndex();
  }

  void _updateCurrentIndex() {
    // Define this method to update currentIndex based on the current route
    switch (ModalRoute.of(context)?.settings.name) {
      case '/':
        setState(() {
          currentIndex = 0;
        });
        break;
      case '/teams':
        setState(() {
          currentIndex = 1;
        });
        break;
      case '/highlights':
        setState(() {
          currentIndex = 2;
        });
        break;
      case '/stats':
        setState(() {
          currentIndex = 3;
        });
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    TextEditingController teamController = TextEditingController();
    TextEditingController uidController = TextEditingController();
    TextEditingController controller = TextEditingController();
    return WillPopScope(
      onWillPop: () async {
        if (navigatorKey.currentState?.canPop() ?? false) {
          navigatorKey.currentState?.pop();
          Future.delayed(Duration.zero, () {
            _updateCurrentIndex(); // Add this line
          });
          return false; // Prevent the default back button behavior
        }
        return true; // Allow the default back button behavior if there's no route to pop
      },
      child: Container(
        color: Color(0xFF238F20),
        child: SafeArea(
          child: Scaffold(
            key: _scaffoldKey,
            appBar: PreferredSize(
              preferredSize: Size.fromHeight(72.0), // Set the height here
              child: TopNavigationBar(
                controller: controller,
                onNotificationIconTapped: () {
                  _scaffoldKey.currentState?.openDrawer();
                },
              ),
            ),
            drawer: SideBar(
              notificationExists: true,
            ),
            body: NotificationListener<PopNavigatorNotification>(
              onNotification: (notification) {
                _updateCurrentIndex();
                return true;
              },
              child: Navigator(
                key: navigatorKey,
                initialRoute: '/',
                onGenerateRoute: (RouteSettings settings) {
                  WidgetBuilder builder;
                  switch (settings.name) {
                    case '/':
                      builder = (BuildContext _) => BookingView();
                      break;
                    case '/teams':
                      builder = (BuildContext _) => YourTeams();
                      break;
                    case '/highlights':
                      builder = (BuildContext _) =>
                          HighlightsView(highlightsExists: true);
                      break;
                    case '/stats':
                      builder =
                          (BuildContext _) => StatsView(statsExists: true);
                      break;
                    default:
                      throw Exception('Invalid route: ${settings.name}');
                  }
                  return MaterialPageRoute(
                      builder: builder, settings: settings);
                },
              ),
            ),
            bottomNavigationBar: Theme(
              data: Theme.of(context).copyWith(
                canvasColor: Colors.white,
                primaryColor: Color(0xFF238F20),
                textTheme: Theme.of(context).textTheme.copyWith(
                      caption: TextStyle(color: Colors.grey),
                    ),
                bottomNavigationBarTheme: BottomNavigationBarThemeData(
                  backgroundColor: Colors.white,
                  elevation: 0,
                ),
              ),
              child: BottomNavigationBar(
                currentIndex: currentIndex,
                onTap: onTap,
                items: [
                  BottomNavigationBarItem(
                    icon: Icon(
                      Icons.book,
                    ),
                    label: 'Booking',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.people),
                    label: 'Teams',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.sports_cricket),
                    label: 'Highlights',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.graphic_eq),
                    label: 'Stats',
                  ),
                ],
                selectedItemColor:
                    Color(0xFF238F20), // change this to your preferred color
                unselectedItemColor: Colors.grey,
                showSelectedLabels: true,
                showUnselectedLabels: true,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class PopNavigatorNotification extends Notification {}

问题分析

  1. 路由监听对象错误:RouteObserver订阅的是HomeView自身的ModalRoute,而非内部嵌套Navigator的路由,因此内部页面的pop操作无法触发HomeView的didPopNext回调。
  2. 路由名称获取错误:_updateCurrentIndex中使用ModalRoute.of(context)获取的是HomeView的路由名称,不是内部Navigator当前显示页面的路由名称,导致无法正确匹配页面。
  3. 自定义通知未触发:PopNavigatorNotification没有在内部页面执行pop时发送,因此NotificationListener无法收到更新信号。

修复方案

修复后的代码

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:sportifan_user/screens/main/bookings.dart';
import 'package:sportifan_user/screens/main/your_teams.dart';
import 'package:sportifan_user/screens/main/highlights.dart';
import 'package:sportifan_user/screens/main/stats.dart';
import 'package:sportifan_user/widgets/sidebar.dart';
import 'package:sportifan_user/widgets/top_navigation_bar.dart';

class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  int currentIndex = 0;

  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  // 优化:避免重复push相同路由
  void onTap(int index) {
    // 获取当前内部Navigator的路由名称
    final currentRouteName = navigatorKey.currentState?.currentRoute?.settings.name;
    String targetRoute = '';
    switch (index) {
      case 0:
        targetRoute = '/';
        break;
      case 1:
        targetRoute = '/teams';
        break;
      case 2:
        targetRoute = '/highlights';
        break;
      case 3:
        targetRoute = '/stats';
        break;
    }

    // 如果当前路由和目标路由相同,不执行push
    if (currentRouteName != targetRoute) {
      navigatorKey.currentState?.pushNamed(targetRoute);
    }
    setState(() {
      currentIndex = index;
    });
  }

  @override
  void initState() {
    super.initState();
    // 监听内部Navigator的路由变化
    navigatorKey.currentState?.addListener(_onRouteChanged);
  }

  @override
  void dispose() {
    navigatorKey.currentState?.removeListener(_onRouteChanged);
    super.dispose();
  }

  // 路由变化时更新currentIndex
  void _onRouteChanged() {
    final routeName = navigatorKey.currentState?.currentRoute?.settings.name;
    setState(() {
      switch (routeName) {
        case '/':
          currentIndex = 0;
          break;
        case '/teams':
          currentIndex = 1;
          break;
        case '/highlights':
          currentIndex = 2;
          break;
        case '/stats':
          currentIndex = 3;
          break;
        default:
          currentIndex = 0;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    TextEditingController controller = TextEditingController();
    return WillPopScope(
      onWillPop: () async {
        if (navigatorKey.currentState?.canPop() ?? false) {
          navigatorKey.currentState?.pop();
          return false;
        }
        return true;
      },
      child: Container(
        color: Color(0xFF238F20),
        child: SafeArea(
          child: Scaffold(
            key: _scaffoldKey,
            appBar: PreferredSize(
              preferredSize: Size.fromHeight(72.0),
              child: TopNavigationBar(
                controller: controller,
                onNotificationIconTapped: () {
                  _scaffoldKey.currentState?.openDrawer();
                },
              ),
            ),
            drawer: SideBar(
              notificationExists: true,
            ),
            body: Navigator(
              key: navigatorKey,
              initialRoute: '/',
              onGenerateRoute: (RouteSettings settings) {
                WidgetBuilder builder;
                switch (settings.name) {
                  case '/':
                    builder = (BuildContext _) => BookingView();
                    break;
                  case '/teams':
                    builder = (BuildContext _) => YourTeams();
                    break;
                  case '/highlights':
                    builder = (BuildContext _) => HighlightsView(highlightsExists: true);
                    break;
                  case '/stats':
                    builder = (BuildContext _) => StatsView(statsExists: true);
                    break;
                  default:
                    throw Exception('Invalid route: ${settings.name}');
                }
                return MaterialPageRoute(builder: builder, settings: settings);
              },
            ),
            bottomNavigationBar: Theme(
              data: Theme.of(context).copyWith(
                canvasColor: Colors.white,
                primaryColor: Color(0xFF238F20),
                textTheme: Theme.of(context).textTheme.copyWith(
                      caption: TextStyle(color: Colors.grey),
                    ),
                bottomNavigationBarTheme: BottomNavigationBarThemeData(
                  backgroundColor: Colors.white,
                  elevation: 0,
                ),
              ),
              child: BottomNavigationBar(
                currentIndex: currentIndex,
                onTap: onTap,
                items: [
                  BottomNavigationBarItem(
                    icon: Icon(Icons.book),
                    label: 'Booking',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.people),
                    label: 'Teams',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.sports_cricket),
                    label: 'Highlights',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.graphic_eq),
                    label: 'Stats',
                  ),
                ],
                selectedItemColor: Color(0xFF238F20),
                unselectedItemColor: Colors.grey,
                showSelectedLabels: true,
                showUnselectedLabels: true,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

关键改动点

  1. 移除无效的RouteObserver和自定义通知:删除原代码中未生效的RouteAware相关逻辑和PopNavigatorNotification,简化代码。
  2. 监听内部Navigator的路由变化:在initState中给内部Navigator添加addListener,路由变化时触发_onRouteChanged更新currentIndex。
  3. 正确获取内部路由名称:通过navigatorKey.currentState?.currentRoute?.settings.name获取当前显示页面的路由,确保匹配正确。
  4. 优化onTap逻辑:判断当前路由是否与目标路由一致,避免重复push相同页面,提升交互体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 00:49:55