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

Flutter底部导航栏开发遇StackOverflowError问题求助

Flutter BottomNavigationBar 触发StackOverflowError排查修复

问题重现

为Flutter项目添加BottomNavigationBar后,运行时触发StackOverflowError,报错指向Builder组件,完整报错信息如下:

Launching lib\main.dart on sdk gphone64 x86 64 in debug mode...
lib\main.dart:1
√  Built build\app\outputs\flutter-apk\app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:62216/ZIcDnqzIkLk=/ws
E/SurfaceSyncer( 7593): Failed to find sync for id=0
W/Parcel  ( 7593): Expecting binder but got null!
D/EGL_emulation( 7593): app_time_stats: avg=7.90ms min=2.99ms max=180.93ms count=62
D/EGL_emulation( 7593): app_time_stats: avg=15.06ms min=3.37ms max=21.43ms count=60

════════ Exception caught by widgets library ═══════════════════════════════════
The following StackOverflowError was thrown building Builder:
Stack Overflow

The relevant error-causing widget was
Builder
lib\…\homepage\home_page.dart:31
When the exception was thrown, this was the stack

错误原因分析

从完整代码中定位到两个核心问题:

  1. 无限递归创建组件:全局_widgetOptions列表第一个元素是const HomePage(),而HomePage的build方法又会加载_widgetOptions。默认选中索引_selectedIndex为0时,会无限递归创建HomePage实例,直接导致栈溢出。
  2. 页面结构逻辑错误:Scaffold的body中同时放置了_widgetOptions.elementAt(_selectedIndex)和body(),重复渲染内容的同时加剧了递归问题。

修复方案

  1. 替换递归组件:将_widgetOptions中的HomePage替换为首页内容组件(即原body()返回的Widget,单独抽成组件)。
  2. 调整页面结构:Scaffold的body只显示当前选中的页面,移除重复的body()调用。
  3. 优化状态管理:将_selectedIndex移到_HomePageState类内部,避免全局变量带来的状态混乱。

修复后完整代码

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0;
  final List<Widget> _widgetOptions = const <Widget>[
    HomeContentPage(), // 替换原HomePage为首页内容组件
    AcademicPage(),
    CreditPage(),
    ProfilePage(),
  ];

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          selectedFontSize: 12,
          unselectedItemColor: Colors.grey,
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: 'Beranda',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.school),
              label: 'Akademik',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.credit_card),
              label: 'Pembayaran',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person),
              label: 'Profile',
            ),
          ],
          currentIndex: _selectedIndex,
          selectedItemColor: primaryColor,
          onTap: (index) {
            setState(() {
              _selectedIndex = index;
            });
          },
        ),
        body: _widgetOptions.elementAt(_selectedIndex), // 仅显示选中页面
      ),
    );
  }
}

// 抽离首页内容为单独组件
class HomeContentPage extends StatelessWidget {
  const HomeContentPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const SizedBox(height: 30),
          Container(
            child: FutureBuilder<UserBiodata>(
              future: AuthProvider().getDataMahasiswa(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            '${snapshot.data!.data.name}',
                            style: bold4,
                          ),
                          Text(
                            '${snapshot.data!.data.prodi}',
                            style: bold6,
                          ),
                          Text(
                            'Semester ${snapshot.data!.data.smt}',
                            style: regular6,
                          )
                        ],
                      ),
                      Image.asset(
                        'assets/images/user.png',
                        width: 60,
                      ),
                    ],
                  );
                } else if (snapshot.hasError) {
                  return Text('${snapshot.error}');
                }
                return const CircularProgressIndicator();
              },
            ),
          ),
          const SizedBox(height: 30),
          Image.asset('assets/images/statis.png'),
          const SizedBox(height: 30),
          Text('Akses Cepat', style: bold4),
          const SizedBox(height: 18),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              _buildQuickAccessItem('Jadwal Kuliah', 'assets/images/icon_jadwal.png'),
              _buildQuickAccessItem('Jadwal Kuliah', 'assets/images/icon_jadwal.png'),
              _buildQuickAccessItem('Jadwal Kuliah', 'assets/images/icon_jadwal.png'),
            ],
          ),
          const SizedBox(height: 18),
          Text('Dosen', style: bold4),
          const SizedBox(height: 18),
          _buildLecturerCard(context),
          const SizedBox(height: 18),
          _buildLecturerCard(context),
        ],
      ),
    );
  }

  Widget _buildQuickAccessItem(String label, String iconPath) {
    return Container(
      height: 90,
      width: 90,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: const BorderRadius.all(Radius.circular(20)),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.2),
            spreadRadius: 1,
            blurRadius: 9,
            offset: const Offset(1, 2),
          ),
        ],
      ),
      child: Column(
        children: [
          const SizedBox(height: 10),
          Image.asset(iconPath, height: 40),
          const SizedBox(height: 12),
          Text(label, style: regular7),
        ],
      ),
    );
  }

  Widget _buildLecturerCard(BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: const BorderRadius.all(Radius.circular(8)),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.2),
            spreadRadius: 1,
            blurRadius: 9,
            offset: const Offset(1, 2),
          ),
        ],
      ),
      child: Padding(
        padding: const EdgeInsets.all(14),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text('Muhamad Fahmi Nugraha M.Kom', style: bold6),
                Text('kaprodi Fakultas Komputer', style: bold7),
                const SizedBox(height: 12),
                Row(
                  children: [
                    SizedBox(
                      width: 94,
                      height: 32,
                      child: TextButton(
                        onPressed: () => showDialog(
                          context: context,
                          builder: (BuildContext context) => _signOutDialog(context),
                        ),
                        style: TextButton.styleFrom(
                          backgroundColor: primaryColor,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                        child: Text("WhatsApp", style: boldButton1),
                      ),
                    ),
                    const SizedBox(width: 8),
                    SizedBox(
                      width: 94,
                      height: 32,
                      child: TextButton(
                        onPressed: () {
                          Navigator.pushNamed(context, AppRoute.biodataeRoute);
                        },
                        style: TextButton.styleFrom(
                          backgroundColor: button,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                        child: Text("Detail", style: boldButton2),
                      ),
                    ),
                  ],
                )
              ],
            ),
            Image.asset('assets/images/user.png', width: 50)
          ],
        ),
      ),
    );
  }
}

Widget _signOutDialog(BuildContext context) {
  return AlertDialog(
    title: Column(
      children: [
        Image.asset('assets/images/ic_lgt.png'),
      ],
    ),
    content: const Text('Apakah kamu yakin ingin logout?'),
    actions: <Widget>[
      TextButton(
        onPressed: () => AuthProvider().signOut(context),
        child: const Text('Ya'),
      ),
      TextButton(
        onPressed: () => Navigator.pop(context),
        child: const Text(
          'TIDAK',
          style: TextStyle(color: Colors.red),
        ),
      ),
    ],
  );
}

额外说明

  • 抽离HomeContentPage组件让代码结构更清晰,避免主页面堆积过多UI代码。
  • 提取重复UI元素为单独方法,减少代码冗余。
  • 移除了原代码中多余的Builder组件和嵌套Padding,优化了布局层级。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:55:16