如何在Flutter中实现如图所示的自定义Tab组件?
Flutter 实现自定义Tab样式
根据你提供的样式需求,这里给出一种直接的实现方案,通过自定义Tab组件结合状态控制来还原效果:
核心实现思路
- 自定义Tab子组件,根据选中状态切换背景、文字样式及装饰
- 借助
DefaultTabController管理选中索引,同步所有Tab的状态 - 隐藏系统默认的Tab指示器,完全用自定义样式替代
完整代码示例
import 'package:flutter/material.dart'; void main() => runApp(const CustomTabDemo()); class CustomTabDemo extends StatelessWidget { const CustomTabDemo({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('自定义Tab示例')), body: const DefaultTabController( length: 3, child: Column( children: [ CustomTabBar(), Expanded(child: TabBarView( children: [ Center(child: Text('首页内容')), Center(child: Text('分类内容')), Center(child: Text('我的内容')), ], )), ], ), ), ), ); } } class CustomTabBar extends StatelessWidget { const CustomTabBar({super.key}); @override Widget build(BuildContext context) { final tabController = DefaultTabController.of(context)!; return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: const BoxDecoration( color: Color(0xFFF5F5F5), borderRadius: BorderRadius.vertical(bottom: Radius.circular(16)), ), child: TabBar( controller: tabController, indicator: const BoxDecoration(), // 隐藏默认指示器 labelPadding: const EdgeInsets.symmetric(horizontal: 8), tabs: [ _CustomTabItem(title: '首页', isSelected: tabController.index == 0), _CustomTabItem(title: '分类', isSelected: tabController.index == 1), _CustomTabItem(title: '我的', isSelected: tabController.index == 2), ], onTap: (index) => tabController.animateTo(index), ), ); } } class _CustomTabItem extends StatelessWidget { final String title; final bool isSelected; const _CustomTabItem({required this.title, required this.isSelected}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: isSelected ? Colors.white : Colors.transparent, borderRadius: const BorderRadius.all(Radius.circular(20)), boxShadow: isSelected ? [ BoxShadow( color: Colors.grey.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ) ] : null, ), child: Text( title, style: TextStyle( color: isSelected ? Colors.blue : Colors.grey, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ); } }
样式调整说明
如果需要匹配图中更细节的样式(比如底部弧形选中条),可以修改_CustomTabItem为以下实现:
class _CustomTabItem extends StatelessWidget { final String title; final bool isSelected; const _CustomTabItem({required this.title, required this.isSelected}); @override Widget build(BuildContext context) { return Stack( alignment: Alignment.bottomCenter, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Text( title, style: TextStyle( color: isSelected ? Colors.blue : Colors.grey, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ), // 底部弧形选中标识 if (isSelected) Container( margin: const EdgeInsets.only(bottom: -8), width: 24, height: 12, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(12)), ), ), ], ); } }
你可以根据实际需求调整颜色、圆角大小、阴影参数等,完全自定义出和图中一致的样式。
内容的提问来源于stack exchange,提问作者MMSH
相关产品推荐
相关产品推荐

