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

Flutter中如何滚动AppBar实现自动缩放并唤出抽屉菜单Drawer

问题根因
  • 你的SliverAppBar被包裹在Scaffold.appBar属性下的CustomScrollView中,该CustomScrollView和页面主滚动逻辑完全独立,页面滚动事件无法传递到SliverAppBar,因此onStretchTrigger不会触发,尺寸也无法联动滚动动态更新
  • Sliver系列组件的正确用法是作为CustomScrollView的子元素放在Scaffold.body中,嵌套在appBar属性中属于错误用法,自然无法实现滚动伸缩效果
修复方案

你需要使用NestedScrollView组件来联动SliverAppBar和TabBarView的滚动逻辑,这是Flutter中实现带TabBar的伸缩AppBar的标准方案,核心修改如下:

  1. 移除原来Scaffold.appBar属性下的PreferredSize、CustomScrollView嵌套逻辑
  2. 将Scaffold.body替换为NestedScrollView,把SliverAppBar和TabBar放在headerSliverBuilder中,原有TabBarView作为NestedScrollView的body
  3. 给SliverAppBar配置pinned、expandedHeight、stretchTriggerOffset等必要参数,保证拉伸触发逻辑正常运行
  4. 用SliverPersistentHeader包裹TabBar,保证TabBar在滚动时可以固定在顶部

修正后核心代码

import '../widgets/drawer_app.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import '../dummy_items.dart';
import './item_screen.dart';

class HomeScreen extends StatefulWidget {
  static const routeName = '/home_screen';
  int _selectedTab = 2;

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      initialIndex: 2,
      length: 5,
      child: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                Theme.of(context).primaryColor,
                const Color.fromRGBO(0, 56, 100, 1.0)
              ]),
        ),
        child: Scaffold(
          backgroundColor: Colors.transparent,
          drawer: DrawerApp(),
          body: NestedScrollView(
            headerSliverBuilder: (context, innerBoxIsScrolled) {
              return [
                SliverAppBar(
                  backgroundColor: Colors.transparent,
                  shadowColor: Colors.transparent,
                  pinned: true,
                  stretch: true,
                  expandedHeight: 180, // 按需调整展开最大高度
                  stretchTriggerOffset: 100, // 按需调整拉伸触发阈值
                  onStretchTrigger: () {
                    print("拉伸触发成功");
                    // 如需触发打开Drawer,直接调用下面的代码即可
                    // Scaffold.of(context).openDrawer();
                    return Future<void>.value();
                  },
                  flexibleSpace: const FlexibleSpaceBar(
                    // 可自定义伸缩区域的内容,比如背景、标题等
                    background: SizedBox.expand(),
                  ),
                ),
                SliverPersistentHeader(
                  pinned: true, // 设为false则TabBar会跟着滚动收起
                  delegate: _TabBarDelegate(
                    TabBar(
                      onTap: (index) {
                        setState(() {
                          widget._selectedTab = index;
                        });
                      },
                      tabs: [
                        Tab(
                          icon: Icon(
                            CupertinoIcons.add,
                            size: widget._selectedTab == 0 ? 24 : 20,
                          ),
                        ),
                        Tab(
                            icon: Icon(Icons.ac_unit,
                                size: widget._selectedTab == 1 ? 24 : 20)),
                        Tab(
                          icon: Icon(CupertinoIcons.alarm,
                              size: widget._selectedTab == 2 ? 24 : 20),
                        ),
                        Tab(
                          icon: Icon(CupertinoIcons.music_note,
                              size: widget._selectedTab == 3 ? 24 : 20),
                        ),
                        Tab(
                          icon: Icon(CupertinoIcons.tickets,
                              size: widget._selectedTab == 4 ? 24 : 20),
                        )
                      ],
                      unselectedLabelColor:
                      const Color.fromRGBO(142, 142, 147, 1),
                      labelColor: const Color.fromRGBO(0, 122, 255, 1),
                      overlayColor:
                      MaterialStateProperty.all(Colors.transparent),
                    ),
                  ),
                )
              ];
            },
            body: const TabBarView(
              children: [
                ItemScreen(Icon(Icons.ac_unit), Icon(Icons.ac_unit), dummyFirst),
                ItemScreen(Icon(CupertinoIcons.eye_fill), Icon(CupertinoIcons.eye_slash_fill), dummySecond),
                ItemScreen(Icon(CupertinoIcons.eye_fill), Icon(CupertinoIcons.eye_slash_fill), dummyThird),
                ItemScreen(Icon(CupertinoIcons.tags_solid), Icon(CupertinoIcons.tags), dummyFourth),
                ItemScreen(Icon(CupertinoIcons.tags_solid), Icon(CupertinoIcons.tags), dummyFith),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _TabBarDelegate extends SliverPersistentHeaderDelegate {
  final TabBar tabBar;

  _TabBarDelegate(this.tabBar);

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(child: tabBar);
  }

  @override
  double get maxExtent => tabBar.preferredSize.height;

  @override
  double get minExtent => tabBar.preferredSize.height;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return false;
  }
}
注意事项
  • 不需要再手动维护additionalSize变量修改AppBar高度,SliverAppBar会根据expandedHeight和滚动偏移自动计算尺寸
  • 如果不需要TabBar固定在顶部,只需将SliverPersistentHeader的pinned属性改为false即可
  • 若要实现拉伸触发打开Drawer,直接在onStretchTrigger回调中调用Scaffold.of(context).openDrawer()即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 05:39:02