Flutter中如何制作带间距的按钮式Tab Bars?匹配图示样式
Flutter实现两类自定义Tab Bar效果
一、带间距的按钮式Tab Bar
这种Tab Bar将每个选项做成独立按钮样式,选项间保留明显间距,适合突出单个Tab按钮的场景。
实现代码
import 'package:flutter/material.dart'; class SpacedButtonTabBar extends StatefulWidget { const SpacedButtonTabBar({super.key}); @override State<SpacedButtonTabBar> createState() => _SpacedButtonTabBarState(); } class _SpacedButtonTabBarState extends State<SpacedButtonTabBar> with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("带间距的按钮Tab Bar"), bottom: TabBar( controller: _tabController, // 控制Tab之间的横向间距 labelPadding: const EdgeInsets.symmetric(horizontal: 16), // 自定义选中状态的按钮背景 indicator: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), ), labelColor: Colors.blue, unselectedLabelColor: Colors.grey, // 自定义Tab内边距,形成按钮形态 tabs: const [ Tab( child: Padding( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 20), child: Text("首页"), ), ), Tab( child: Padding( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 20), child: Text("分类"), ), ), Tab( child: Padding( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 20), child: Text("我的"), ), ), ], ), ), body: TabBarView( controller: _tabController, children: const [ Center(child: Text("首页内容区域")), Center(child: Text("分类内容区域")), Center(child: Text("我的内容区域")), ], ), ); } }
关键实现点
- 通过
labelPadding设置Tab之间的横向间距,避免选项拥挤 - 给
Tab内部添加Padding,让每个Tab呈现按钮尺寸 - 用
BoxDecoration作为indicator,实现选中时的按钮背景高亮效果
二、匹配示例样式的Tab Bar
示例样式核心特征:底部带自适应宽度的下划线指示器,选中时下划线加粗变色,Tab文字同步切换粗细与颜色。
实现代码
import 'package:flutter/material.dart'; class SampleStyledTabBar extends StatefulWidget { const SampleStyledTabBar({super.key}); @override State<SampleStyledTabBar> createState() => _SampleStyledTabBarState(); } class _SampleStyledTabBarState extends State<SampleStyledTabBar> with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 4, vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("示例样式Tab Bar"), bottom: TabBar( controller: _tabController, labelColor: Colors.blue, unselectedLabelColor: Colors.grey.shade600, // 自定义下划线指示器,控制宽度与高度 indicator: const UnderlineTabIndicator( borderSide: BorderSide(width: 3, color: Colors.blue), // 通过insets控制下划线左右缩进,适配文字宽度 insets: EdgeInsets.symmetric(horizontal: 18), ), labelStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), unselectedLabelStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.normal, ), tabs: const [ Tab(text: "推荐"), Tab(text: "热门"), Tab(text: "关注"), Tab(text: "最新"), ], ), ), body: TabBarView( controller: _tabController, children: const [ Center(child: Text("推荐内容")), Center(child: Text("热门内容")), Center(child: Text("关注内容")), Center(child: Text("最新内容")), ], ), ); } }
进阶自定义(精准匹配文字宽度)
如果需要下划线宽度完全贴合文字,可以自定义指示器:
class CustomTextUnderlineIndicator extends Decoration { final double height; final Color color; const CustomTextUnderlineIndicator({ required this.height, required this.color, }); @override BoxPainter createBoxPainter([VoidCallback? onChanged]) { return _CustomIndicatorPainter(this, onChanged); } } class _CustomIndicatorPainter extends BoxPainter { final CustomTextUnderlineIndicator decoration; _CustomIndicatorPainter(this.decoration, VoidCallback? onChanged) : super(onChanged); @override void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { final textPainter = TextPainter( text: TextSpan( text: configuration.text ?? "", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), textDirection: TextDirection.ltr, )..layout(); final textWidth = textPainter.width; final centerX = offset.dx + configuration.size!.width / 2; final left = centerX - textWidth / 2; final right = centerX + textWidth / 2; final paint = Paint() ..color = decoration.color ..style = PaintingStyle.fill; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(left, configuration.size!.height - decoration.height, right - left, decoration.height), Radius.circular(decoration.height / 2), ), paint, ); } }
使用时替换TabBar的indicator:
indicator: const CustomTextUnderlineIndicator( height: 3, color: Colors.blue, ),
内容的提问来源于stack exchange,提问作者Mr.Robot
相关产品推荐
相关产品推荐

