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

如何在Flutter中实现Spotify风格的AppBar切换按钮?

实现Spotify风格的AppBar切换按钮

你可以通过两种方式实现目标效果:自定义TabBar样式,或者完全手动实现切换组件,以下是具体方案:

方案一:自定义TabBar适配Spotify样式

原生TabBar可以实现该效果,只需要调整样式参数匹配Spotify的视觉风格:

class SpotifyTabDemo extends StatelessWidget {
  const SpotifyTabDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.black,
          title: const Text("Spotify Clone"),
          // 用PreferredSize控制TabBar高度,避免AppBar被过度拉伸
          bottom: PreferredSize(
            preferredSize: const Size.fromHeight(50),
            child: TabBar(
              // 自定义下划线样式:绿色、短宽、居中
              indicator: const UnderlineTabIndicator(
                borderSide: BorderSide(color: Colors.green, width: 2),
                insets: EdgeInsets.symmetric(horizontal: 20),
              ),
              // 选中/未选中文字颜色
              labelColor: Colors.white,
              unselectedLabelColor: Colors.grey,
              // 选中/未选中文字粗细
              labelStyle: const TextStyle(fontWeight: FontWeight.bold),
              unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.normal),
              tabs: const [
                Tab(text: "全部"),
                Tab(text: "音乐"),
                Tab(text: "播客"),
              ],
            ),
          ),
        ),
        body: const TabBarView(
          children: [
            Center(child: Text("全部内容页")),
            Center(child: Text("音乐内容页")),
            Center(child: Text("播客内容页")),
          ],
        ),
      ),
    );
  }
}

关键调整点:

  • 通过UnderlineTabIndicator自定义下划线的颜色、宽度和边距,还原Spotify的短下划线效果
  • 配置labelColor/unselectedLabelColor控制文字切换变色
  • 用PreferredSize固定TabBar高度,避免影响AppBar整体布局

方案二:手动实现完全自定义的切换组件

如果TabBar的样式限制满足不了需求,可以用Row+GestureDetector完全手动实现,灵活性更高:

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

  @override
  State<CustomSpotifyTabs> createState() => _CustomSpotifyTabsState();
}

class _CustomSpotifyTabsState extends State<CustomSpotifyTabs> {
  int _selectedTabIndex = 0;
  final List<String> _tabTitles = ["全部", "音乐", "播客"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: const Text("Spotify Clone"),
        actions: [
          Expanded(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(_tabTitles.length, (index) {
                return GestureDetector(
                  onTap: () {
                    setState(() => _selectedTabIndex = index);
                    // 配合PageView切换页面:_pageController.jumpToPage(index);
                  },
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text(
                          _tabTitles[index],
                          style: TextStyle(
                            color: _selectedTabIndex == index ? Colors.white : Colors.grey,
                            fontWeight: _selectedTabIndex == index ? FontWeight.bold : FontWeight.normal,
                          ),
                        ),
                        // 选中时显示下划线
                        if (_selectedTabIndex == index)
                          Container(
                            margin: const EdgeInsets.only(top: 6),
                            height: 2,
                            width: 30,
                            color: Colors.green,
                          ),
                      ],
                    ),
                  ),
                );
              }),
            ),
          ),
        ],
      ),
      body: PageView(
        onPageChanged: (index) => setState(() => _selectedTabIndex = index),
        children: const [
          Center(child: Text("全部内容页")),
          Center(child: Text("音乐内容页")),
          Center(child: Text("播客内容页")),
        ],
      ),
    );
  }
}

这种方式可以完全控制每个按钮的布局、间距、下划线样式,适合需要高度定制的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:50:54