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

如何在PageView.builder中实现AppBar滚动隐藏与显示?

问题描述

尝试在PageView.builder滚动时隐藏AppBar,但滚动时AppBar无响应。相同的SliverAppBar逻辑在ListView.builder中可正常工作,需求为向下滚动隐藏AppBar、向上滚动重新显示,询问能否在PageView.builder中实现该功能。

原代码:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import '../../Controller/NewsController.dart';
import 'Export.dart';

class ShowNewsPage extends StatefulWidget {
  const ShowNewsPage({Key? key}) : super(key: key);

  @override
  State<ShowNewsPage> createState() => _ShowNewsPageState();
}

class _ShowNewsPageState extends State<ShowNewsPage> {
  bool _init = true;
  bool _isLoadingNews = false;
  @override
  void didChangeDependencies() async {
    if (_init) {
      _isLoadingNews =
          await Provider.of<NewsController>(context, listen: false).getNews();
    }
    _init = false;
    setState(() {});
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    final news = Provider.of<NewsController>(context).allNews;
    if (!_isLoadingNews) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    } else {
      return Scaffold(
        extendBodyBehindAppBar: true,
        body: NestedScrollView(
          floatHeaderSlivers: true,
          headerSliverBuilder: (context, innerBoxIsScrolled) => [
            SliverAppBar(
              floating: true,
              title: Text('Next'),
              centerTitle: true,
              toolbarHeight: 40,
            ),
          ],
          body: PageView.builder(
            physics: const ScrollPhysics(),
            itemCount: news.length,
            scrollDirection: Axis.vertical,
            // shrinkWrap: true,
            itemBuilder: (context, index) {
              return NewNewscardWidget(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
                title: news[index].title!.rendered!,
                content: news[index].content!.rendered!,
                image: news[index].ogImage ?? [],
                id: news[index].id!,
                // authorName: news[index].author!,
                date: 'November 5, 2022',
              );
            },
          ),
        ),
      );
    }
  }
}
解决方案

NestedScrollView的联动逻辑仅支持内部是ListView、GridView这类实现了嵌套滚动协议的组件,PageView不在其支持范围内,因此直接嵌套无法触发SliverAppBar的显示/隐藏。可以通过以下方法实现需求:

方法:用NotificationListener监听滚动,手动控制AppBar状态

通过监听ScrollUpdateNotification判断滚动方向,手动切换AppBar的显示状态,同时可保留动画过渡效果:

class _ShowNewsPageState extends State<ShowNewsPage> {
  bool _init = true;
  bool _isLoadingNews = false;
  bool _showAppBar = true;
  // 可选:添加滚动阈值,避免微小滚动触发状态切换
  final double _scrollThreshold = 20;
  double _lastScrollPosition = 0;

  @override
  void didChangeDependencies() async {
    if (_init) {
      _isLoadingNews =
          await Provider.of<NewsController>(context, listen: false).getNews();
    }
    _init = false;
    setState(() {});
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    final news = Provider.of<NewsController>(context).allNews;
    if (!_isLoadingNews) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    } else {
      return Scaffold(
        extendBodyBehindAppBar: true,
        appBar: AnimatedSwitcher(
          duration: const Duration(milliseconds: 300),
          child: _showAppBar
              ? AppBar(
                  key: const ValueKey('appbar'),
                  title: const Text('Next'),
                  centerTitle: true,
                  toolbarHeight: 40,
                )
              : null,
        ),
        body: NotificationListener<ScrollUpdateNotification>(
          onNotification: (notification) {
            final currentPosition = notification.metrics.pixels;
            final scrollDelta = currentPosition - _lastScrollPosition;

            // 向下滚动超过阈值,隐藏AppBar
            if (scrollDelta > _scrollThreshold && _showAppBar) {
              setState(() => _showAppBar = false);
            }
            // 向上滚动超过阈值,显示AppBar
            else if (scrollDelta < -_scrollThreshold && !_showAppBar) {
              setState(() => _showAppBar = true);
            }

            _lastScrollPosition = currentPosition;
            return true;
          },
          child: PageView.builder(
            physics: const ScrollPhysics(),
            itemCount: news.length,
            scrollDirection: Axis.vertical,
            itemBuilder: (context, index) {
              return NewNewscardWidget(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
                title: news[index].title!.rendered!,
                content: news[index].content!.rendered!,
                image: news[index].ogImage ?? [],
                id: news[index].id!,
                date: 'November 5, 2022',
              );
            },
          ),
        ),
      );
    }
  }
}

关键说明

  • 使用AnimatedSwitcher给AppBar添加显示/隐藏的过渡动画,提升体验
  • 增加_scrollThreshold阈值,避免微小滚动导致AppBar频繁切换状态
  • 通过ScrollUpdateNotification监听PageView的滚动偏移变化,判断滚动方向

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 07:41:35