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

Flutter切换深浅主题时Bottom Navigation Bar无法即时变色问题求解

问题根因

  • 组件嵌套逻辑错误:BottomNavBar是父组件,包裹了MainPage,但你把MaterialApp放在了子组件MainPage内部,全局theme变量变更时只会触发MainPage的setState重绘,父组件BottomNavBar没有被通知更新,所以底部导航栏只有点击触发自身setState时才会读取新的theme值刷新样式。
  • 你把pages列表定义成了全局变量,只会在初始化时创建一次MainPage和FavoritePage实例,不会随状态更新重建。

修复步骤

步骤1:调整根组件结构,把MaterialApp移到最外层

把MaterialApp放在应用入口(通常是main.dart的runApp对应的组件中),BottomNavBar作为MaterialApp的home参数,将主题状态提升到最外层统一管理:

// 示例 main.dart
void main() {
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // 主题状态移到最外层统一管理
  bool theme = false;
  final darkTheme = ThemeData.dark();
  final lightTheme = ThemeData.light();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: theme ? darkTheme : lightTheme,
      // 将主题状态和切换方法传给下层组件
      home: BottomNavBar(
        theme: theme,
        onThemeChanged: (val) {
          setState(() {
            theme = val;
          });
        },
      ),
    );
  }
}

步骤2:修改MainPage代码,移除MaterialApp,接收主题相关参数

MainPage不再包裹MaterialApp,直接返回Scaffold,主题状态和切换方法从父组件传入:

class MainPage extends StatefulWidget {
  final bool theme;
  final Function(bool) onThemeChanged;
  const MainPage({super.key, required this.theme, required this.onThemeChanged});

  @override
  State<MainPage> createState() => MainPageState();
}

class MainPageState extends State<MainPage> {
  @override
  Widget build(BuildContext context) {
    // 直接返回Scaffold即可,不需要再套MaterialApp
    return Scaffold(
      backgroundColor: widget.theme ? Colors.blueGrey[900] : Colors.white,
      appBar: AppBar(
        centerTitle: true,
        title: const Text(
          "DSC World",
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
      ),
      // 中间body代码保持原有逻辑不变
      body: ListView(
        padding: EdgeInsets.fromLTRB(15, 20, 15, 0),
        shrinkWrap: true,
        physics: ScrollPhysics(),
        children: [
          GridView.builder(
            physics: NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              mainAxisSpacing: 25,
              crossAxisSpacing: 25,
              crossAxisCount: 2,
            ),
            itemBuilder: (context, index) => myCard(
                context,
                photo1[index]["photo"],
                names[index]["name"],
                func[index]["func"]),
            itemCount: photo1.length,
          ),
          SizedBox(
            height: 25,
          ),
          Container(
            margin: EdgeInsets.fromLTRB(85, 0, 85, 0),
            height: 275,
            child: GridView.builder(
              physics: NeverScrollableScrollPhysics(),
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 1,
              ),
              itemBuilder: (context, index) =>
                  myCard(context, 'images/anc_g2.png', "Antarctica", Antr()),
              itemCount: 1,
            ),
          ),
        ],
      ),
      drawer: Drawer(
        child: ListView(
          children: [
            Card(
              color: widget.theme ? Colors.white30 : Colors.blueGrey,
              child: DrawerHeader(
                child: Column(
                  children: [
                    Expanded(child: Image.asset('images/logo1.png')),
                    const Text(
                      "The Earth",
                      style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                    )
                  ],
                ),
              ),
            ),
            Column(
              children: [
                const SizedBox(height: 50),
                Card(
                  elevation: 5,
                  color: widget.theme ? Colors.white30 : Colors.blueGrey,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      const Text(
                        "  Dark Theme",
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      Switch(
                        value: widget.theme,
                        onChanged: (x) {
                          // 调用外层传入的方法更新全局主题
                          widget.onThemeChanged(x);
                        },
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

步骤3:修改BottomNavBar代码,接收主题参数,动态构建页面列表

class BottomNavBar extends StatefulWidget {
  final bool theme;
  final Function(bool) onThemeChanged;
  const BottomNavBar({super.key, required this.theme, required this.onThemeChanged});

  @override
  _BottomNavBarState createState() => _BottomNavBarState();
}

class _BottomNavBarState extends State<BottomNavBar> {
  int currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    // pages放在build方法内,每次重绘都会生成最新的页面实例
    List pages = [
      MainPage(theme: widget.theme, onThemeChanged: widget.onThemeChanged),
      const FavoritePage()
    ];
    return Scaffold(
      body: pages[currentIndex],
      bottomNavigationBar: BottomNavyBar(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        // 直接用传入的theme参数判断颜色,外层主题变更时会触发当前组件重绘
        backgroundColor: widget.theme ? Colors.blueGrey[800] : const Color(0xff5B7BA6),
        onItemSelected: (index) {
          setState(() {
            currentIndex = index;
          });
        },
        selectedIndex: currentIndex,
        items: [
          BottomNavyBarItem(
            activeColor: Colors.white,
            inactiveColor: Colors.black54,
            textAlign: TextAlign.center,
            title: const Text("Home"),
            icon: const Icon(Icons.home),
          ),
          BottomNavyBarItem(
            activeColor: Colors.white,
            inactiveColor: Colors.black54,
            textAlign: TextAlign.center,
            title: const Text("Favorite"),
            icon: const Icon(Icons.star),
          ),
        ],
      ),
    );
  }
}

生效原理

主题状态提升到最顶层统一管理后,切换主题时顶层setState会触发整棵组件树重绘,BottomNavBar和MainPage都会拿到最新的theme值,底部导航栏的颜色会和其他组件同步更新,不需要再点击触发刷新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 05:18:02