flutter获取listview可视区域的firstIndex和lastIndex,不破坏原有listview

Posted 庄童

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flutter获取listview可视区域的firstIndex和lastIndex,不破坏原有listview相关的知识,希望对你有一定的参考价值。

一种侵入性不强的方案,解决获取listview可视firstIndex和lastIndex问题。
无需修改已有项目代码 套上ScrollIndexWidget即可,代码如下

import 'dart:math';

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

typedef ViewPortCallback = void Function(int firstIndex, int lastIndex);

class ScrollIndexWidget extends StatelessWidget {
  final ScrollView child;
  final ViewPortCallback callback;

  const ScrollIndexWidget(
      {Key? key, required this.child, required this.callback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return NotificationListener(child: child, onNotification: _onNotification);
  }

  bool _onNotification(ScrollNotification notice) {
    final SliverMultiBoxAdaptorElement sliverMultiBoxAdaptorElement =
        findSliverMultiBoxAdaptorElement(notice.context! as Element)!;

    final viewportDimension = notice.metrics.viewportDimension;

    int firstIndex = 0;
    int lastIndex = 0;
    void onVisitChildren(Element element) {
      final SliverMultiBoxAdaptorParentData oldParentData =
          element.renderObject?.parentData as SliverMultiBoxAdaptorParentData;
      double layoutOffset = oldParentData.layoutOffset!;
      double pixels = notice.metrics.pixels;
      double all = pixels + viewportDimension;
      if (layoutOffset >= pixels) {
        ///first和last是不同item
        firstIndex = min(firstIndex, oldParentData.index! - 1);
        if (layoutOffset <= all) {
          lastIndex = max(lastIndex, oldParentData.index!);
        }
        firstIndex = max(firstIndex, 0);
      } else {
        ///first和last是同一个item
        lastIndex = firstIndex = oldParentData.index!;
      }
    }

    sliverMultiBoxAdaptorElement.visitChildren(onVisitChildren);

    callback(
      firstIndex,
      lastIndex,
    );

    return false;
  }

  SliverMultiBoxAdaptorElement? findSliverMultiBoxAdaptorElement(
      Element element) {
    if (element is SliverMultiBoxAdaptorElement) {
      return element;
    }
    SliverMultiBoxAdaptorElement? target;
    element.visitChildElements((child) {
      target = findSliverMultiBoxAdaptorElement(child);
    });
    return target;
  }
}

使用方式

import 'package:flitter_okgo/index_listen.dart';
import 'package:flutter/material.dart';

class IndexPage extends StatefulWidget {
  @override
  _IndexPageState createState() => _IndexPageState();
}

class _IndexPageState extends State<IndexPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('indexPage'),
      ),
      body: ScrollIndexWidget(
        child: ListView.builder(
          itemBuilder: (c, i) {
            return Container(
              alignment: Alignment.center,
              color: Colors.green.withBlue((i + 1) * 100),
              height: (i + 1) * 20,
              child: Text('index==$i'),
            );
          },
          scrollDirection: Axis.vertical,
          itemCount: 100,
        ),
        callback: (first, last) {
          print('当前第一个可见元素下标 $first 当前最后一个可见元素下标 $last');
        },
      ),
    );
  }
}

以上是关于flutter获取listview可视区域的firstIndex和lastIndex,不破坏原有listview的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Flutter 的 listview.builder 中获取长度

如何从 Listview.builder 中获取模型类 Flutter 的列表索引

Flutter:在动画的、定位的容器中获取 ListView,作为堆栈的一部分

在 Flutter 中将 Firebase 数据作为 Listview 构建器获取

Flutter ListView设置分割线及列表嵌套不同的Item

Flutter ListView.Builder() 在可滚动列中与其他小部件