Flutter中fl_chart(v0.70)柱状图显示异常问题求助
解决fl_chart v0.70柱状图溢出与轴标签不显示问题
问题描述
- 柱状图超出容器范围,高度撑满当前窗口
- 无任何轴标签显示,即使保留了示例中的标签代码
- 直接复制示例代码(仅替换AppColors为Colors、移除AppUtils)时,图表不会溢出但仍无标签
问题代码
class BarChartSample5 extends StatefulWidget { List<Expenditure> expenditures; bool displayMonthly; DateTime date; bool displayAverage; BarChartSample5( {super.key, required this.expenditures, required this.displayMonthly, required this.date, required this.displayAverage}); @override State<StatefulWidget> createState() => BarChartSample5State(); } class BarChartSample5State extends State<BarChartSample5> { static const double barWidth = 22; static const shadowOpacity = 0.2; int touchedIndex = -1; Map<int, double> expendituresAcrossTheYear = <int, double>{ //first element is the variable income, second is the fixed income, third is the variable expense, fourth is the fixed expense }; @override void initState() { super.initState(); } Widget bottomTitles(double value, TitleMeta meta) { const style = TextStyle(color: Colors.white, fontSize: 10); String text; switch (value.toInt()) { case 0: text = 'Mon'; break; case 1: text = 'Tue'; break; case 2: text = 'Wed'; break; case 3: text = 'Thu'; break; case 4: text = 'Fri'; break; case 5: text = 'Sat'; break; case 6: text = 'Sun'; break; default: text = ''; break; } return SideTitleWidget( axisSide: meta.axisSide, child: Text(text, style: style), ); } Widget topTitles(double value, TitleMeta meta) { const style = TextStyle(color: Colors.white, fontSize: 10); String text; switch (value.toInt()) { case 0: text = 'Mon'; break; case 1: text = 'Tue'; break; case 2: text = 'Wed'; break; case 3: text = 'Thu'; break; case 4: text = 'Fri'; break; case 5: text = 'Sat'; break; case 6: text = 'Sun'; break; default: return Container(); } return SideTitleWidget( axisSide: meta.axisSide, child: Text(text, style: style), ); } Widget leftTitles(double value, TitleMeta meta) { const style = TextStyle(color: Colors.white, fontSize: 10); String text; if (value == 0) { text = '0'; } else { text = '${value.toInt()}0k'; } return SideTitleWidget( axisSide: meta.axisSide, space: 4, child: Text( text, style: style, textAlign: TextAlign.center, ), ); } Widget rightTitles(double value, TitleMeta meta) { const style = TextStyle(color: Colors.white, fontSize: 10); String text; if (value == 0) { text = '0'; } else { text = '${value.toInt()}0k'; } return SideTitleWidget( axisSide: meta.axisSide, space: 0, child: Text( text, style: style, textAlign: TextAlign.center, ), ); } BarChartGroupData generateGroup(int x, double value1) { final isTop = value1 > 0; final sum = value1; final isTouched = touchedIndex == x; return BarChartGroupData( x: x, groupVertically: true, showingTooltipIndicators: isTouched ? [0] : [], barRods: [ BarChartRodData( toY: sum, width: barWidth, borderRadius: isTop ? const BorderRadius.only( topLeft: Radius.circular(6), topRight: Radius.circular(6), ) : const BorderRadius.only( bottomLeft: Radius.circular(6), bottomRight: Radius.circular(6), ), rodStackItems: [ BarChartRodStackItem( 0, value1, Colors.green, BorderSide( color: Colors.white, width: isTouched ? 2 : 0, ), ), ], ), BarChartRodData( toY: -sum, width: barWidth, color: Colors.transparent, borderRadius: isTop ? const BorderRadius.only( bottomLeft: Radius.circular(6), bottomRight: Radius.circular(6), ) : const BorderRadius.only( topLeft: Radius.circular(6), topRight: Radius.circular(6), ), rodStackItems: [], ), ], ); } bool isShadowBar(int rodIndex) => rodIndex == 1; @override Widget build(BuildContext context) { populateBarChartData(); return AspectRatio( aspectRatio: 0.8, child: Padding( padding: const EdgeInsets.only(top: 16), child: BarChart( BarChartData( alignment: BarChartAlignment.spaceEvenly, maxY: 20, minY: -20, groupsSpace: 12, barTouchData: BarTouchData( handleBuiltInTouches: false, touchCallback: (FlTouchEvent event, barTouchResponse) { if (!event.isInterestedForInteractions || barTouchResponse == null || barTouchResponse.spot == null) { setState(() { touchedIndex = -1; }); return; } final rodIndex = barTouchResponse.spot!.touchedRodDataIndex; if (isShadowBar(rodIndex)) { setState(() { touchedIndex = -1; }); return; } setState(() { touchedIndex = barTouchResponse.spot!.touchedBarGroupIndex; }); }, ), titlesData: FlTitlesData( show: true, topTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 32, getTitlesWidget: topTitles, ), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 32, getTitlesWidget: bottomTitles, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: leftTitles, interval: 5, reservedSize: 42, ), ), rightTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: rightTitles, interval: 5, reservedSize: 42, ), ), ), gridData: FlGridData( show: true, checkToShowHorizontalLine: (value) => value % 5 == 0, getDrawingHorizontalLine: (value) { if (value == 0) { return FlLine( strokeWidth: 3, ); } return FlLine( strokeWidth: 0.8, ); }, ), borderData: FlBorderData( show: false, ), barGroups: expendituresAcrossTheYear.entries .map( (e) => generateGroup(e.key, e.value), ) .toList(), ), ), ), ); } void populateBarChartData() { getTrueValueOfExpenditures( DateTime(widget.date.year, 1, 1), DateTime(widget.date.year, 12, 31), widget.expenditures); //This will be for yearly data } void getTrueValueOfExpenditures( DateTime from, DateTime to, List<Expenditure> expenditures) { expendituresAcrossTheYear.clear(); //... performs calculations and directly updates the expendituresAcrossTheYear map vaiable (not using setState();) } }
解决方案
1. 修复柱状图溢出问题
- 匹配数据范围与maxY/minY:如果实际数据值超过设置的
maxY:20或minY:-20,图表会溢出容器。根据expendituresAcrossTheYear中的最大/最小值,动态调整maxY和minY(比如设为数据最大值的1.1倍),确保有足够显示空间。 - 调整AspectRatio:当前
aspectRatio:0.8会让图表高度大于宽度,容易撑满窗口。改为示例中的1.7或根据布局需求调整为合适的值(比如1.5)。 - 正确处理数据更新:不要在
build方法中调用populateBarChartData,而是在initState和didUpdateWidget中调用,并且更新数据后必须调用setState()触发UI刷新:@override void initState() { super.initState(); populateBarChartData(); } @override void didUpdateWidget(covariant BarChartSample5 oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.expenditures != widget.expenditures || oldWidget.date != widget.date) { populateBarChartData(); } } void getTrueValueOfExpenditures(DateTime from, DateTime to, List<Expenditure> expenditures) { expendituresAcrossTheYear.clear(); // ... 你的计算逻辑 setState(() {}); // 更新数据后触发UI刷新 }
2. 修复轴标签不显示问题
- 修正文本颜色与背景的对比度:代码中所有轴标签的文本颜色设为
Colors.white,如果图表所在容器背景是浅色(比如默认白色),标签会完全看不见。将文本颜色改为深色(比如Colors.black87):const style = TextStyle(color: Colors.black87, fontSize: 10); - 匹配x轴分组的key值:当前
bottomTitles和topTitles的switch分支只处理0-6的x值,但代码是按年份生成月份数据(x值为1-12),导致匹配不到任何分支,显示空文本。修改为适配月份的逻辑:Widget bottomTitles(double value, TitleMeta meta) { const style = TextStyle(color: Colors.black87, fontSize: 10); final monthIndex = value.toInt(); final months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; String text = monthIndex >=1 && monthIndex <=12 ? months[monthIndex-1] : ''; return SideTitleWidget( axisSide: meta.axisSide, child: Text(text, style: style), ); } - 检查SideTitles配置:确保
reservedSize足够容纳标签文本,避免被截断。如果文本较长,适当增大reservedSize的值。
内容的提问来源于stack exchange,提问作者Tloz
相关产品推荐
相关产品推荐

