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

Flutter如何判断ListView.Builder的最后一项已显示在屏幕上

实现ListView.Builder滚动监听并在最后一项显示时隐藏FloatingActionButton

要实现这个需求,你可以通过ScrollController监听滚动事件,判断最后一项是否进入用户视口,进而控制FloatingActionButton的显示状态。以下是完整的实现步骤和代码:

步骤1:定义核心变量

在页面的State类中声明滚动控制器和控制FAB显示的布尔变量:

class _YourPageState extends State<YourPage> {
  final ScrollController _scrollController = ScrollController();
  bool _showFab = true;
  final GlobalKey _lastItemKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    // 添加滚动监听
    _scrollController.addListener(_onScroll);
  }

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

步骤2:实现滚动监听逻辑

在_onScroll方法中,通过GlobalKey获取最后一项的渲染位置,判断其是否完全显示在视口内:

void _onScroll() {
  // 最后一项未渲染时直接返回
  if (_lastItemKey.currentContext == null) return;
  
  final renderBox = _lastItemKey.currentContext!.findRenderObject() as RenderBox;
  // 计算最后一项的底部在屏幕中的绝对位置
  final offset = renderBox.localToGlobal(Offset.zero);
  final lastItemBottom = offset.dy + renderBox.size.height;
  
  // 计算视口底部的位置
  final viewportBottom = _scrollController.position.pixels + _scrollController.position.viewportDimension;

  // 最后一项完全进入视口时隐藏FAB,否则显示
  setState(() {
    _showFab = lastItemBottom > viewportBottom;
  });
}

步骤3:更新ListView和FAB代码

将滚动控制器绑定到ListView,给最后一项设置GlobalKey,并根据_showFab控制FAB的显示:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: ListView.builder(
      controller: _scrollController,
      itemCount: Data.channels.length,
      itemBuilder: (c, index) {
        Channel item = Data.channels[index];
        return Container(
          padding: const EdgeInsets.all(16),
          // 给最后一项绑定GlobalKey
          key: index == Data.channels.length - 1 ? _lastItemKey : null,
          child: Text(item.name),
        );
      },
    ),
    // 根据_showFab状态控制FAB显示/隐藏
    floatingActionButton: _showFab
        ? FloatingActionButton(
            onPressed: () {},
            child: const Icon(Icons.add),
          )
        : null,
  );
}

备选方案:使用NotificationListener

如果不想额外维护ScrollController,可以用NotificationListener包裹ListView,直接监听滚动通知:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: NotificationListener<ScrollUpdateNotification>(
      onNotification: (notification) {
        if (_lastItemKey.currentContext == null) return true;
        
        final renderBox = _lastItemKey.currentContext!.findRenderObject() as RenderBox;
        final offset = renderBox.localToGlobal(Offset.zero);
        final lastItemBottom = offset.dy + renderBox.size.height;
        final viewportBottom = notification.metrics.pixels + notification.metrics.viewportDimension;

        setState(() {
          _showFab = lastItemBottom > viewportBottom;
        });
        return true;
      },
      child: ListView.builder(
        itemCount: Data.channels.length,
        itemBuilder: (c, index) {
          Channel item = Data.channels[index];
          return Container(
            padding: const EdgeInsets.all(16),
            key: index == Data.channels.length - 1 ? _lastItemKey : null,
            child: Text(item.name),
          );
        },
      ),
    ),
    floatingActionButton: _showFab ? FloatingActionButton(onPressed: () {}) : null,
  );
}

关键说明

  • 用GlobalKey获取最后一项的渲染位置,适配item高度不固定的场景,判断逻辑更精准。
  • 若你的item高度固定,也可以直接通过滚动位置+item高度的公式计算,省去GlobalKey的使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:19:57