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

Flutter中Text组件无法正常显示问题求助

Flutter健身应用标签页名称不显示问题修复

问题说明

开发Flutter健身应用时,已实现支持用户添加/移除的TabView训练页面,但训练名称无法正常显示。

原代码

import 'package:flutter/material.dart';
import 'package:workout_app/Screens/Components/Widgets/footer.dart';

class workout_page extends StatefulWidget {
  @override
  _WorkoutPageState createState() => _WorkoutPageState();
}

class _WorkoutPageState extends State<workout_page>
    with TickerProviderStateMixin {
  late TabController _tabController;
  List<Map<String, dynamic>> workoutMap = [
    {
      'name': 'My First Workout',
      'exercises': [
        {
          'name': 'Bicep Curl',
          'musclesworked': ['bicep', 'tricep']
        },
        {
          'name': 'Preacher Curl',
          'musclesworked': ['bicep', 'tricep']
        }
      ]
    },
  ];

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: workoutMap.length + 1, vsync: this);
  }

  TextEditingController _newTabController = TextEditingController();

  void _addTab() {
    setState(() {
      String newTabName = _newTabController.text;
      Map<String, dynamic> newTab = {
        'name': newTabName,
        'exercises': [],
      };
      workoutMap.add(newTab);
      _newTabController.clear();
      _tabController = TabController(
        length: workoutMap.length + (workoutMap.length < 4 ? 1 : 0),
        vsync: this,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Container(
      decoration: BoxDecoration(color: Color.fromARGB(255, 67, 67, 67)),
      child: DefaultTabController(
        length: workoutMap.length + (workoutMap.length < 4 ? 1 : 0),
        child: Scaffold(
          backgroundColor: Color.fromRGBO(79, 79, 79, 1),
          body: Stack(
            children: [
              CustomScrollView(
                physics: NeverScrollableScrollPhysics(),
                slivers: [
                  SliverAppBar(
                    expandedHeight: 30,
                    titleSpacing: 15,
                    pinned: true,
                    elevation: 0,
                    backgroundColor: Color.fromARGB(255, 19, 19, 19),
                    centerTitle: false,
                    bottom: PreferredSize(
                        preferredSize: const Size.fromHeight(0),
                        child: Column(children: [
                          TabBar(
                            physics: NeverScrollableScrollPhysics(),
                            tabs: [
                              ...workoutMap.map(
                                  (tab) => Tab(text: tab['name'] as String)),
                              if (workoutMap.length < 4)
                                Container(
                                  width: 40,
                                  height: 40,
                                  alignment: Alignment.center,
                                  child: IconButton(
                                    icon: Icon(Icons.add),
                                    onPressed: () {
                                      showDialog(
                                        context: context,
                                        builder: (context) => AlertDialog(
                                          title: Text('Add Tab'),
                                          content: TextField(
                                            controller: _newTabController,
                                            decoration: InputDecoration(
                                              hintText: 'Enter tab name',
                                            ),
                                          ),
                                          actions: [
                                            TextButton(
                                              child: Text('Cancel'),
                                              onPressed: () {
                                                Navigator.pop(context);
                                              },
                                            ),
                                            TextButton(
                                              child: Text('Add'),
                                              onPressed: () {
                                                _addTab();
                                                Navigator.pop(context);
                                              },
                                            ),
                                          ],
                                        ),
                                      );
                                    },
                                  ),
                                ),
                            ],
                          ),
                        ])),
                  ),
                  SliverToBoxAdapter(
                    child: Container(
                      height: size.height,
                      child: TabBarView(
                        physics: NeverScrollableScrollPhysics(),
                        controller: _tabController,
                        children: [
                          ...workoutMap.map((tab) => **RIGHT HERE** Text(tab['name'])),
                          if (workoutMap.length < 4) Container(),
                        ],
                      ),
                    ),
                  )
                ],
              ),
              Footer(tab: 'Workout'),
            ],
          ),
        ),
      ),
    );
  }
}

问题原因及修复方案

核心问题

  1. 双重TabController冲突:同时使用DefaultTabController和自定义_tabController,导致TabBar与TabBarView控制器不同步,标签内容无法正确关联。
  2. 控制器长度计算不一致:initState和_addTab中计算TabController长度的逻辑不统一,导致标签数量与内容数量不匹配。
  3. 无效代码残留:TabBarView子组件中的**RIGHT HERE**标记属于语法错误,阻碍组件渲染。

修复后的代码

import 'package:flutter/material.dart';
import 'package:workout_app/Screens/Components/Widgets/footer.dart';

class WorkoutPage extends StatefulWidget {
  @override
  _WorkoutPageState createState() => _WorkoutPageState();
}

class _WorkoutPageState extends State<WorkoutPage>
    with TickerProviderStateMixin {
  late TabController _tabController;
  List<Map<String, dynamic>> workoutMap = [
    {
      'name': 'My First Workout',
      'exercises': [
        {
          'name': 'Bicep Curl',
          'musclesworked': ['bicep', 'tricep']
        },
        {
          'name': 'Preacher Curl',
          'musclesworked': ['bicep', 'tricep']
        }
      ]
    },
  ];

  @override
  void initState() {
    super.initState();
    // 统一长度计算逻辑
    _tabController = TabController(
      length: workoutMap.length + (workoutMap.length < 4 ? 1 : 0),
      vsync: this,
    );
  }

  TextEditingController _newTabController = TextEditingController();

  void _addTab() {
    setState(() {
      String newTabName = _newTabController.text;
      if (newTabName.isNotEmpty) { // 避免添加空标签
        Map<String, dynamic> newTab = {
          'name': newTabName,
          'exercises': [],
        };
        workoutMap.add(newTab);
        _newTabController.clear();
        _tabController = TabController(
          length: workoutMap.length + (workoutMap.length < 4 ? 1 : 0),
          vsync: this,
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Container(
      decoration: const BoxDecoration(color: Color.fromARGB(255, 67, 67, 67)),
      // 移除DefaultTabController,统一使用自定义控制器
      child: Scaffold(
        backgroundColor: const Color.fromRGBO(79, 79, 79, 1),
        body: Stack(
          children: [
            CustomScrollView(
              physics: const NeverScrollableScrollPhysics(),
              slivers: [
                SliverAppBar(
                  expandedHeight: 30,
                  titleSpacing: 15,
                  pinned: true,
                  elevation: 0,
                  backgroundColor: const Color.fromARGB(255, 19, 19, 19),
                  centerTitle: false,
                  bottom: PreferredSize(
                    preferredSize: const Size.fromHeight(0),
                    child: Column(
                      children: [
                        TabBar(
                          controller: _tabController, // 绑定自定义控制器
                          physics: const NeverScrollableScrollPhysics(),
                          tabs: [
                            ...workoutMap.map(
                              (tab) => Tab(text: tab['name'] as String),
                            ),
                            if (workoutMap.length < 4)
                              Container(
                                width: 40,
                                height: 40,
                                alignment: Alignment.center,
                                child: IconButton(
                                  icon: const Icon(Icons.add),
                                  onPressed: () {
                                    showDialog(
                                      context: context,
                                      builder: (context) => AlertDialog(
                                        title: const Text('Add Tab'),
                                        content: TextField(
                                          controller: _newTabController,
                                          decoration: const InputDecoration(
                                            hintText: 'Enter tab name',
                                          ),
                                        ),
                                        actions: [
                                          TextButton(
                                            child: const Text('Cancel'),
                                            onPressed: () {
                                              Navigator.pop(context);
                                            },
                                          ),
                                          TextButton(
                                            child: const Text('Add'),
                                            onPressed: () {
                                              _addTab();
                                              Navigator.pop(context);
                                            },
                                          ),
                                        ],
                                      ),
                                    );
                                  },
                                ),
                              ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
                SliverToBoxAdapter(
                  child: Container(
                    height: size.height,
                    child: TabBarView(
                      controller: _tabController,
                      physics: const NeverScrollableScrollPhysics(),
                      children: [
                        // 移除无效标记,添加文本样式提升可读性
                        ...workoutMap.map((tab) => Center(
                              child: Text(
                                tab['name'],
                                style: const TextStyle(color: Colors.white, fontSize: 20),
                              ),
                            )),
                        if (workoutMap.length < 4) const SizedBox(),
                      ],
                    ),
                  ),
                )
              ],
            ),
            const Footer(tab: 'Workout'),
          ],
        ),
      ),
    );
  }
}

关键修改点

  • 移除DefaultTabController,让TabBar与TabBarView绑定同一自定义_tabController,确保标签与内容同步。
  • 统一TabController长度计算逻辑,初始化和添加标签时保持一致。
  • 移除TabBarView中的无效标记,添加文本样式优化显示效果。
  • 增加空值判断,避免用户添加空名称标签。
  • 修正类名格式(workout_page改为WorkoutPage,符合Dart命名规范)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:45:17