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

Flutter实现滚动时TabBar固定在顶部的求助

Flutter实现滚动时TabBar固定在顶部的求助

嘿,我刚好做过类似的需求,这在Flutter里用NestedScrollView配合SliverAppBar就能轻松搞定,给你分享下具体的实现思路和代码示例:

核心逻辑是借助NestedScrollView的滚动联动特性,让顶部的SliverAppBar在滚动到一定位置时固定,而TabBar作为SliverAppBar的底部部分,自然就会跟着固定在屏幕顶部了。

下面是完整的可运行代码示例:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TabBar Sticky Top Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const StickyTabBarPage(),
    );
  }
}

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

  @override
  State<StickyTabBarPage> createState() => _StickyTabBarPageState();
}

class _StickyTabBarPageState extends State<StickyTabBarPage> with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    // 这里的length对应Tab的数量,按需调整
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    // 记得销毁控制器,避免内存泄漏
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        // 构建顶部的可滚动头部
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              title: const Text('我的页面'),
              pinned: true, // 关键!设置为true,AppBar会固定在顶部
              expandedHeight: 200.0, // 头部展开时的高度,可自定义
              // 这里可以放顶部的横幅图片,不需要的话可以删掉这个属性
              flexibleSpace: FlexibleSpaceBar(
                background: Image.network(
                  'https://picsum.photos/800/400',
                  fit: BoxFit.cover,
                ),
              ),
              // 把TabBar放在SliverAppBar的bottom里
              bottom: TabBar(
                controller: _tabController,
                tabs: const <Tab>[
                  Tab(text: '首页'),
                  Tab(text: '分类'),
                  Tab(text: '我的'),
                ],
              ),
            ),
          ];
        },
        // 构建Tab对应的内容区域
        body: TabBarView(
          controller: _tabController,
          children: const <Widget>[
            // 用ListView模拟长列表,方便测试滚动效果
            ListView.builder(
              itemCount: 50,
              itemBuilder: (context, index) => ListTile(title: Text('首页内容 $index')),
            ),
            ListView.builder(
              itemCount: 50,
              itemBuilder: (context, index) => ListTile(title: Text('分类内容 $index')),
            ),
            ListView.builder(
              itemCount: 50,
              itemBuilder: (context, index) => ListTile(title: Text('我的内容 $index')),
            ),
          ],
        ),
      ),
    );
  }
}

几个需要注意的关键点:

  • SliverAppBar的pinned: true是实现固定的核心,没有这个属性的话,TabBar会跟着头部一起滑出屏幕。
  • 如果你不需要顶部的横幅图片和大标题,也可以简化SliverAppBar,只保留bottom的TabBar,同样能实现固定效果:
SliverAppBar(
  pinned: true,
  bottom: TabBar(
    controller: _tabController,
    tabs: const <Tab>[
      Tab(text: '首页'),
      Tab(text: '分类'),
      Tab(text: '我的'),
    ],
  ),
)
  • 记得给TabController设置正确的length,要和Tab的数量一致,并且在页面销毁时调用dispose()释放资源。

你可以直接把这段代码复制到项目里测试,调整成符合你UI需求的样式就好啦!

备注:内容来源于stack exchange,提问作者Happy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 07:44:59