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

Flutter实现带图片AppBar、自定义容器及滚动吸附TabBar问题求助

解决方案:滚动吸附TabBar+自定义展开头部

问题背景

已通过appbar_animated: ^0.0.3实现滚动时顶部图片、餐厅信息容器消失,AppBar切换为普通样式的效果。现需添加TabBar置于这些元素下方,且滚动时TabBar能吸附到AppBar。尝试过NestedScrollView+SliverAppBar方案,但不知如何添加展开状态的背景图,以及放置自定义的restaurantInfoContainer。

实现思路

直接基于NestedScrollView+SliverAppBar完成所有需求:

  • 将背景图、餐厅信息容器放入SliverAppBar的flexibleSpace中,实现滚动时的展开/收起动画
  • TabBar绑定到SliverAppBar的bottom属性,滚动时自动吸附到顶部
  • 通过pinned: true保持AppBar和TabBar在滚动到顶部时固定显示

完整代码实现

// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables
import 'package:flutter/material.dart';

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

  @override
  State<TalabadScreenWithTab> createState() => _TalabadScreenWithTabState();
}

class _TalabadScreenWithTabState extends State<TalabadScreenWithTab> with SingleTickerProviderStateMixin {
  final _tabs = List.generate(10, (index) => 'Tab#${index + 1}');
  late final TabController _tabCont;

  @override
  void initState() {
    _tabCont = TabController(length: 10, vsync: this);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final screenHeight = MediaQuery.of(context).size.height;
    final expandedHeaderHeight = screenHeight * 0.35;

    return Scaffold(
      backgroundColor: Colors.grey[200],
      body: NestedScrollView(
        headerSliverBuilder: (context, innerBoxIsScrolled) {
          return [
            SliverAppBar(
              backgroundColor: innerBoxIsScrolled ? Colors.white : Colors.transparent,
              scrolledUnderElevation: innerBoxIsScrolled ? 4 : 0,
              elevation: 0,
              leading: CircleAvatar(
                backgroundColor: Colors.white,
                child: IconButton(
                  onPressed: () => Navigator.pop(context),
                  icon: Icon(Icons.arrow_back, color: Colors.black87, size: 30),
                ),
              ),
              actions: [
                CircleAvatar(
                  radius: 28,
                  backgroundColor: Colors.white,
                  child: IconButton(
                    padding: EdgeInsets.all(0),
                    icon: Icon(Icons.ios_share_rounded, color: Colors.black87, size: 30),
                    onPressed: () {},
                  ),
                ),
                SizedBox(width: 10),
                CircleAvatar(
                  radius: 28,
                  backgroundColor: Colors.white,
                  child: IconButton(
                    padding: EdgeInsets.all(0),
                    icon: Icon(Icons.search, color: Colors.black87, size: 30),
                    onPressed: () {},
                  ),
                ),
                SizedBox(width: 10),
              ],
              expandedHeight: expandedHeaderHeight,
              pinned: true,
              floating: false,
              centerTitle: true,
              // 自定义展开头部内容
              flexibleSpace: FlexibleSpaceBar(
                background: Stack(
                  fit: StackFit.expand,
                  children: [
                    // 顶部背景图
                    Image.asset(
                      "img/cover.jpg",
                      fit: BoxFit.cover,
                    ),
                    // 白色底部容器(覆盖背景图下半部分)
                    Positioned(
                      bottom: 0,
                      left: 0,
                      right: 0,
                      child: Container(
                        height: screenHeight * 0.1,
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
                        ),
                      ),
                    ),
                    // 餐厅信息容器(悬浮在白色容器上方)
                    Positioned(
                      bottom: screenHeight * 0.05,
                      left: 10,
                      right: 10,
                      child: restaurantInfoContainer(),
                    ),
                  ],
                ),
              ),
              // TabBar部分
              bottom: TabBar(
                controller: _tabCont,
                isScrollable: true,
                indicatorColor: Colors.red,
                labelColor: Colors.black87,
                unselectedLabelColor: Colors.grey,
                tabs: _tabs.map((label) => Tab(text: label)).toList(),
              ),
            ),
          ];
        },
        // TabBarView内容
        body: TabBarView(
          controller: _tabCont,
          children: _tabs.map((label) {
            return ListView.builder(
              padding: EdgeInsets.zero,
              itemCount: 50,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text('$label - 列表项 $index'),
                );
              },
            );
          }).toList(),
        ),
      ),
    );
  }

  // 自定义餐厅信息容器(替换成你自己的实现)
  Widget restaurantInfoContainer() {
    return Container(
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black12,
            blurRadius: 8,
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('餐厅名称', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
          SizedBox(height: 8),
          Row(
            children: [
              Icon(Icons.star, color: Colors.amber, size: 16),
              SizedBox(width: 4),
              Text('4.8 (1200+ 评价)'),
              SizedBox(width: 16),
              Text('·'),
              SizedBox(width: 16),
              Text('快餐'),
            ],
          ),
        ],
      ),
    );
  }
}

关键说明

  • FlexibleSpaceBar:承载背景图、白色容器和餐厅信息,实现滚动时的收起动画
  • pinned: true:确保SliverAppBar和TabBar在滚动到顶部时保持固定
  • innerBoxIsScrolled:判断滚动状态,动态切换AppBar的背景色和阴影
  • TabController联动:保证TabBar和TabBarView的选中状态同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 03:35:29