Flutter ListView获取页面显示的第一个和最后一个item、点击item自动偏移居中ListView_flutter 判断listview当前显示的位置-程序员宅基地

技术标签: 首尾item  自动偏移居中  flutter  listview  Flutter  

本文章是两个flutter小组件listview的两个功能,这里做一个分享方便以后其他衍生特殊高级功能的开发,扩宽你的思路:

一、ListView获取页面显示的第一个和最后一个item

1.listview 中利用custom才能监听到第一个条目
new ListView.custom(
  controller: controller,
  cacheExtent: 1.0, // 只有设置了1.0 才能够准确的标记position 位置
  childrenDelegate:  MyChildrenDelegate(
        (BuildContext context, int index) {
//Dismissible这个可以用作列表的横向滑动删除功能效果
      return new Dismissible(
          key: new Key(list[index]),
          onDismissed: (direction) {
            //被移除回掉
            list.removeAt(index);
            var item = list[index];
            Scaffold.of(context).showSnackBar(
                new SnackBar(content: new Text("$item")));
          },
          child: new ListTile(
            title: new Text(list[index]),
          ));
    },
    childCount: list.length,
  ),
)

SliverChildBuilderDelegate 

class _SaltedValueKey extends ValueKey{
  const _SaltedValueKey(Key key): assert(key != null), super(key);
}
class MyChildrenDelegate extends SliverChildBuilderDelegate {
  MyChildrenDelegate(
      Widget Function(BuildContext, int) builder, {
        int childCount,
        bool addAutomaticKeepAlive = true,
        bool addRepaintBoundaries = true,
      }) : super(builder,
      childCount: childCount,
      addAutomaticKeepAlives: addAutomaticKeepAlive,
      addRepaintBoundaries: addRepaintBoundaries);
  // Return a Widget for the given Exception
  Widget _createErrorWidget(dynamic exception, StackTrace stackTrace) {
    final FlutterErrorDetails details = FlutterErrorDetails(
      exception: exception,
      stack: stackTrace,
      library: 'widgets library',
      context: ErrorDescription('building'),
    );
    FlutterError.reportError(details);
    return ErrorWidget.builder(details);
  }
  @override
  Widget build(BuildContext context, int index) {
    assert(builder != null);
    if (index < 0 || (childCount != null && index >= childCount))
      return null;
    Widget child;
    try {
      child = builder(context, index);
    } catch (exception, stackTrace) {
      child = _createErrorWidget(exception, stackTrace);
    }
    if (child == null)
      return null;
    final Key key = child.key != null ? _SaltedValueKey(child.key) : null;
    if (addRepaintBoundaries)
      child = RepaintBoundary(child: child);
    if (addSemanticIndexes) {
      final int semanticIndex = semanticIndexCallback(child, index);
      if (semanticIndex != null)
        child = IndexedSemantics(index: semanticIndex + semanticIndexOffset, child: child);
    }
    if (addAutomaticKeepAlives)
      child = AutomaticKeepAlive(child: child);
    return KeyedSubtree(child: child, key: key);
  }

  @override
  void didFinishLayout(int firstIndex, int lastIndex) {
    // TODO: implement didFinishLayout
    super.didFinishLayout(firstIndex, lastIndex);
  }
 ///监听 在可见的列表中 显示的第一个位置和最后一个位置
@override
  double estimateMaxScrollOffset(int firstIndex, int lastIndex, double leadingScrollOffset, double trailingScrollOffset) {
  print('firstIndex sss : $firstIndex, lastIndex ssss : $lastIndex, leadingScrollOffset ssss : $leadingScrollOffset,'
      'trailingScrollOffset ssss : $trailingScrollOffset  ' );
    return super.estimateMaxScrollOffset(firstIndex, lastIndex, leadingScrollOffset, trailingScrollOffset);
  }
}

 

二、listview点击item自动居中

当然这个功能用上面的说的功能也可以实现,但是有便捷的代码为什么不用呢,哈哈。下面就贴上我的代码(代码量非常简洁,这里就不放git库了,开源给大家看了,直接拷贝进项目就可以使用了)。

/*
 * @Author: Cao Shixin
 * @Date: 2021-02-22 14:47:01
 * @LastEditors: Cao Shixin
 * @LastEditTime: 2021-02-23 10:56:57
 * @Description: 可自带偏移滚动列表
 * @Email: [email protected]
 * @Company: BrainCo
 */

import 'dart:math';

import 'package:flutter/material.dart';

// ignore: must_be_immutable
class AutoOffsetListView extends StatefulWidget {
  //滚动方位
  Axis scrollDirection;
  //内部显示的item数组
  List<AutoOffsetListItem> items;

  AutoOffsetListView(
      {this.items, this.scrollDirection = Axis.vertical, Key key})
      : super(key: key) {
    items ??= <AutoOffsetListItem>[];
    scrollDirection ??= Axis.vertical;
  }

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

class _AutoOffsetListViewState extends State<AutoOffsetListView>
    with WidgetsBindingObserver {
  ScrollController _controller;
  Point _contentCenter;

  @override
  void initState() {
    super.initState();
    _controller = ScrollController();
  }

  @override
  Widget build(BuildContext context) {
    var contentSize = MediaQuery.of(context).size;
    _contentCenter = Point(contentSize.width / 2, contentSize.height / 2);
    return ListView.builder(
      scrollDirection: widget.scrollDirection,
      controller: _controller,
      cacheExtent: 1.0, // 只有设置了1.0 才能够准确的标记position 位置
      itemBuilder: (BuildContext context, int index) {
        return AutoOffsetListItemView(
            item: widget.items[index],
            centerBack: (center) {
              var _offset = _controller.offset;
              if (widget.scrollDirection == Axis.horizontal) {
                _offset += (center.x - _contentCenter.x);
              } else {
                _offset += (center.y - _contentCenter.y);
              }
              if (_offset < 0) {
                _offset = 0;
              } else if (_offset > _controller.position.maxScrollExtent) {
                _offset = _controller.position.maxScrollExtent;
              }
              _controller.animateTo(_offset,
                  duration: Duration(
                    milliseconds: 200,
                  ),
                  curve: Curves.ease);
            });
      },
      itemCount: widget.items?.length ?? 0,
    );
  }
}

typedef WidgetCenterBack = Function(Point center);

// ignore: must_be_immutable
class AutoOffsetListItemView extends StatefulWidget {
  WidgetCenterBack centerBack;
  AutoOffsetListItem item;
  AutoOffsetListItemView({this.item, this.centerBack});

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

class _AutoOffsetListItemViewState extends State<AutoOffsetListItemView> {
  @override
  Widget build(BuildContext context) {
    return InkWell(
      child: AbsorbPointer(
        child: widget.item.child,
      ),
      onTap: _scrollExcursion,
    );
  }

  void _scrollExcursion() {
    RenderObject renderObject = context.findRenderObject();
    //获取元素尺寸
    Rect rect = renderObject.paintBounds;
    //获取元素位置
    var vector3 = renderObject.getTransformTo(null)?.getTranslation();
    if (widget.centerBack != null) {
      widget.centerBack(Point(
          vector3.x + rect.size.width / 2, vector3.y + rect.size.height / 2));
    }
    if (widget.item?.onTap != null) {
      widget.item.onTap();
    }
  }
}

class AutoOffsetListItem {
  //这里widget设置的点击事件无效,如果想使用点击效果,实现下面的onTap即可
  Widget child;
  //点击区域的回调,不使用可以不用实现
  VoidCallback onTap;
  AutoOffsetListItem(this.child, {this.onTap});
}

class _SaltedValueKey extends ValueKey<Key> {
  const _SaltedValueKey(Key key)
      : assert(key != null),
        super(key);
}

使用示例:

AutoOffsetListView(
        items: _list,
        scrollDirection: Axis.horizontal,
      ),

注意,这个里面是一个listview,不要说你外面不加上一个宽或者高来限制(横向展示列表你就加一个container高限制,纵向展示你就加一个contain宽限制,或者宽都加也适合),用我的组件AutoOffsetListView报错,你不限制直接用listview也是会错的!(不是我的代码错误,哈哈)。

最后祝你使用愉快,财源广进,健康平安。

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/BUG_delete/article/details/113977438

智能推荐

LTE物理层概述及基本概念讲解_lte linear-程序员宅基地

文章浏览阅读2.1w次,点赞9次,收藏70次。There are two types of frame structure in the LTE standard, Type 1 and Type 2. Type 1 uses Frequency Division Duplexing (uplink and downlink separated by frequency), and TDD uses Time Division Duple_lte linear

Spring @Autowired注入为 null 的原因总结_spring autowired null-程序员宅基地

文章浏览阅读3.3w次,点赞13次,收藏37次。原因配置缺失,比如为开启注解扫描驱动、注入组件为注册;使用 new 关键字创建的对象不受spring容器管理,无法注入;注入静态变量, 静态变量/类变量不是对象的属性,而是一个类的属性,spring则是基于对象层面上的依赖注入。静态注入的三种方式在一些工具类中可能会依赖 service 层的对象,一般情况下工具类都是使用静态方法,对应的成员变量也需要声明为静态的,此时如果直接使用 @..._spring autowired null

红队眼中的防守弱点_红队 漏洞总是修复不好-程序员宅基地

文章浏览阅读207次。通过对政府、央企、银行、证券、民生、运营商、互联网等行业的红队实战工作,发现各行业安全防护具备如下特点。一、 资产混乱、隔离策略不严格除了大型银行之外,很多行业对自身资产情况比较混乱,没有严格的访问控制(ACL)策略,且办公网和互联网之间大部分相通,可以直接使远程控制程序上线。除了大型银行与互联网行业外,其他很多行业在DMZ区和办公网之间不做或很少做隔离,网络区域划分也不严格,给了红队很多可乘之机。此外,几乎所有行业的下级单位和上级单位的业务网都可以互通。而除了大型银行之外,其他很多行业的办公网也大_红队 漏洞总是修复不好

matlab编程控制舵机,船舶航向控制器设计(MATLAB,附仿真程序)-程序员宅基地

文章浏览阅读1.1k次。船舶航向控制器设计(MATLAB,附仿真程序)(课题申报表,任务书,开题报告,中期检查表,外文翻译,论文15400字,仿真程序,答辩PPT)摘 要本文对反向递推(Backstepping)设计方法在非线性船舶航向控制器中的应用进行了研究。Backstepping设计方法是近几年兴起的一种基于Lyapunov稳定性理论的比较先进的非线性控制方法。因此,本文首先对目前常见的非线性控制方法进行了概述,简..._舵机模型matlab程序

营销系统优惠券模板设计_优惠劵表结构设置-程序员宅基地

文章浏览阅读819次。目录券模板基础信息定义优惠券类型优惠券门槛优惠券面值/折扣率,折扣金额上限有效期自定义使用限定使用时间限定可用商品限定可用门店限定券模板设计类似于商品SKU,定义了一个券模板后,发券操作可以基于同一个券模板实现发放同一种优惠券给多个不同的用户。券模板基础信息定义一张优惠券的基础信息,例如常规的名称,类型,面值等等。下面罗列一些有逻辑含义的字段。优惠券类型立减券: 无门槛或有门槛的满减券,达到使用门槛后可以立减指定金额。 折扣券:按商品价格减免指_优惠劵表结构设置

(深度学习快速入门)人工智能、机器学习和深度学习总体概述_人工智能与深度学习 简明教程-程序员宅基地

文章浏览阅读3k次,点赞15次,收藏67次。线性回归:线性回归假设输出变量是若干输入变量的线性组合,并根据这一关系求解线性组合中的最优系数。具体来说,线性回归的作用是求得一组参数wi,i=0.1.,,,.nwi​,i=0.1.,,,.n,使预测输出可以表示为以这组参数为权重的实例属性的线性组合,引入常量x0=1x_{0}=1x0​=1,线性回归试图学习的模型就是当实例只有一个属性时,输入和输出之间的关系就是二维平面上的一条直线;_人工智能与深度学习 简明教程

随便推点

Shiro与Spring结合时报Configuration error: No realms have been configured! One or more realms must be……_configuration error: no realms have been configure-程序员宅基地

文章浏览阅读2.4k次,点赞2次,收藏7次。最近在跟着网上的教学视频学习Shiro安全框架,使用Shiro可以方便的做验证、授权等,其中在谈到使用多realm做验证,授权时,报了个缺少realm配置的错误。Configuration error: No realms have been configured! One or more realms must be present to execute an authorization o..._configuration error: no realms have been configured! one or more realms must

疟原虫感染治疗癌症,灵感竟然来源于地图-程序员宅基地

文章浏览阅读909次。2月9日,央视一套《新闻30分》节目向全球宣布了陈小平科学研究团队的重大发明《疟原虫感染免疫疗法治疗晚期癌症》。“疟原虫可成为抗癌生力军”迅速占据新闻热搜榜,引发民众热议。为何引发疟疾带来痛苦的疟原虫可以抗癌?这背后有什么样的故事?1985年,在陈小平教授还在中山医科大学读研究生的时候,有一天老师讲疟疾的流行病学时,挂出一张地图,与下图很像——疟疾主要流行在非洲、赤道附近,可以理解为:蚊子多的...

PAT乙级C语言1005 继续(3n+1)思想_pat1005继续 c语言-程序员宅基地

文章浏览阅读95次。#include<stdio.h>void da(int*b,int r);int main(){ //输入数据 int n; scanf("%d",&n); int a[999]; for(int i=0;i<n;i++){ scanf("%d",&a[i]); } //记录重复的数的下标 int t=0; int b[999]; int p; for(int i=0;i<n;i++){ p=a[i]; for(int._pat1005继续 c语言

【啃书】《智能优化算法及其MATLAB实例》例5.1蚁群算法求解TSP问题_蚁群算法 约束优化 matlab-程序员宅基地

文章浏览阅读905次,点赞3次,收藏13次。文章目录问题描述仿真过程matlab源码问题描述仿真过程matlab源码%20201012lu注:该matlab代码成功在matlabR2019a运行%%%%%%%%%%%%%%%%%%%%蚁群算法解决TSP问题%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%初始化%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%clear all; %清除所有变量close all; _蚁群算法 约束优化 matlab

WPF页面切换之Page与Window_wpf window page-程序员宅基地

文章浏览阅读2.3k次。先从基础背景知识说起:一、Page与Window介绍1.应用程序GUI的所有内容都是包含在Window中的,想要显示东西?必须先创建一个Window或者一个Window的子类;2.Window下面可以直接写内容,写布局,但是这些内容和布局写完后只能是固定的、不可变的,这里是不可变是指Window的内容不会发生变化了,因为元素和布局是写死的了;3.在Window下面插入Frame作为容器,再用该Frame包含你的某个Page(***注意:所有的Page都需要容器包含,否则无法进行页面之间的跳转和导航!_wpf window page

C++抽象数据类型(ADT)表示和实现--链队列(Queue)_c++数据结构adt怎么写-程序员宅基地

文章浏览阅读1.5k次。先放上ADT的解释和预定义常量。抽象数据类型(ADT)是指一个数学模型以及定义在该模型上的一组操作。抽象数据类型的定义仅取决于它的一组逻辑特性,而与其在计算机内部如何表示和实现无关,即不论其内部结构如何变化,只要它的数学特性不变,都不影响其外部的使用。以下是队列的抽象数据类型定义。_c++数据结构adt怎么写