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 {
  @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成长,flutter,listview,可见区域)