Flutter实现固定header底部滑动页效果示例
作者:Zuo 发布时间:2022-06-15 06:31:05
实现的效果是这样的:
刚开始的时候,是在dev上找了两个轮子,简单测了下,都不太满意,滑动事件处理的比较粗糙,总有bug。就在想着,要不要拿源码改一版的时候,让我无意间看到了这个帖子
里面的想法,大开眼界,是通过 DraggableScrollableSheet 和 IgnorePointer 来完美实现上面的效果。
实现
这是 DraggableScrollableSheet 的代码,
DraggableScrollableSheet(
maxChildSize: 0.8,
minChildSize: 0.25, // 注意都是占父组件的比例
initialChildSize: 0.25,
expand: true,
builder: (BuildContext context, ScrollController controller) {
return Stack(); // body列表和header栏都在stack内
},
)
这是 body 列表和 header,这里的 body 是个 list,
Stack(
children: [
Container(
color: Colors.blue,
child: Body( // ListView.separated
controller: controller,
paddingTop: headerHeight, // 防止压盖
),
),
const IgnorePointer( // 这里不接收事件,所以拖动 header 也能够滑动页面
child: Header( // Container[Center[Text]]
height: headerHeight,
),
),
],
)
但如果我们想在 header 内加点击事件呢?那在 Stack header 上层再加 widget 就好了。
代码就这点,我放在了 gitHub 上,感兴趣的可以看下。
2022.8.23 补充:
这是在上面功能基础上的一个小扩展,即当滑动距离超过一半则自动滚至顶部,反之回到底部,来看下效果:
思路也很简单,首先我要知道当前滚动的距离或其占比,DraggableScrollableController 提供了这个能力:
void _draggableScrollListener() {
// [_currStale] 记录下当前的占比
// [_controller.size] 即占比, 范围[minChildSize,maxChildSize]
// [_controller.pixels] 即距离
if (_currStale != _controller.size) {
_currStale = _controller.size;
}
debugPrint('[listener] size: ${_controller.size}'
', pixels : ${_controller.pixels}');
}
其次要知道用户何时停止了滚动,我们可以使用 NotificationListener 来监听 DraggableScrollableSheet 的滚动状态:
NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
...
return false;
},
child: DraggableScrollableSheet(...),
之后在用户停止滚动的时候,我们判断当前距离,并根据结果让 DraggableScrollableSheet 自动滚动到顶部或底部。
onNotification: (ScrollNotification notification) {
if (_animation) { // 动画中,不处理状态
return false;
}
if (notification is ScrollStartNotification) {
debugPrint('start scroll');
} else if (notification is ScrollEndNotification) {
debugPrint('stop scroll');
// 通过 [_controller.animateTo] 方法滚动
_scrollAnimation();
}
return false;
在 _scrollAnimation 内就是滚动的方法了,这里要注意的是,不能直接使用 await Feature,我测试在频繁不同方向滑动时,可能会导致方法被挂起。在这直接 dedelayed(duration: xx) 即可:
Future<void> _scrollAnimation() async {
if (_animation) {
return;
}
_animation = true;
//debugPrint('async start');
final int duration;
// `await`ing the returned Feature(of [animateTo]) may cause the method to hang
// So, Start a timer to set [_animation].
if (_currStale >= (_maxScale + _minScale) * 0.5) {
duration =
(_duration * ((_maxScale - _currStale) / (_maxScale - _minScale)))
.toInt();
if (duration == 0) {
_animation = false;
return;
} else {
// [duration] control speed, Avoid situations where it's equal to 0
_animationTo(_maxScale, duration);
}
} else {
duration =
(_duration * ((_currStale - _minScale) / (_maxScale - _minScale)))
.toInt();
if (duration == 0) {
_animation = false;
return;
} else {
_animationTo(_minScale, duration);
}
}
Future.delayed(
Duration(milliseconds: duration),
).then((value) => {
//debugPrint('async stop'),
_animation = false,
});
}
其中 _animationTo
是实际控制控件滚动的方法:
void _animationTo(double scale, int duration) {
_controller.animateTo(
scale,
duration: Duration(milliseconds: duration),
curve: Curves.ease,
);
}
2022.9.24 补充:
那如果再提供一种通过点击按钮来控制 DraggableScrollableSheet
收起和弹出的方法呢?
我们可以直接这样,是不是很简单:
Future<void> _scrollAnimation2() async {
if (_animation) {
return;
}
if (_currStale > (_maxScale + _minScale) * 0.5) {
_animationTo(_minScale, _duration);
} else {
_animationTo(_maxScale, _duration);
}
}
来源:https://juejin.cn/post/7127686678587637796
猜你喜欢
- Java操作redis设置第二天凌晨过期场景在做查询数据的时候,遇到了需要设置数据在redis中第二天过期的问题,但是redis又没有对应的
- 一、前言学习概述:学习四种不同类型的方法应用、方法被调用时的内存图、重载学习目标:熟练掌握方法的应用以及重载二、定义与调用1.概述定义:方法
- 一、前言通过前面我们也知道,通过getMapper方式来进行查询,最后会通过mapperMehod类,对接口中传来的参数也会在这个类里面进行
- 操作符就是为了解决对Observable对象的变换的问题,操作符用于在Observable和最终的Subscriber之间修改Observa
- 1、特效按钮的进展 之前的思路:css设置div的样式,在js中实现div对事件的响应,并改变div的样式,以实现动画效果。 1:以动画的形
- 一般情况下每个spring boot工程启动都有固定的端口,但是固定端口不利用服务的动态扩容,如果在一台服务器上需要对同一个服务进行多实例部
- Java-JDK * (AOP)使用及实现原理分析第一章:代理的介绍介绍:我们需要掌握的程度 * (理解) 基于反射机制掌握的程度:1.
- 在上一篇博文《C/C++ Qt TreeWidget 单层树形组件应用》中给大家演示了如何使用TreeWidget组件创建单层树形结构,并给
- 我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还
- 自定义封装 banner 组件,供大家参考,具体内容如下1. 效果图预览 2.基本功能一个简单方便的轮播图组件,基于viewpag
- 本文实例讲述了Android中View的炸裂特效实现方法。分享给大家供大家参考,具体如下:前几天微博上被一个很优秀的 Android 开源组
- 对于初学java的同学来说,第一件事不是写hello world,而是搭建好java开发环境,下载jdk,安装,配置环境变量。这些操作在xp
- 本文实例为大家分享了C#实现银行家算法的具体代码,供大家参考,具体内容如下1.死锁死锁,顾名思义,是一种锁住不可自行解开的死局。在操作系统中
- 1.简介if判断语句是很多编程语言的重要组成部分。但是,若我们最终编写了大量嵌套的if语句,这将使得我们的代码更加复杂和难以维护。让我们看看
- mybatis实体类字段大小写问题 字段获取不到值由于前期设计问题,项目中需要用到的一个字段 rootpath,所以我再实体层加了这么一个字
- 定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。类型:行为类
- 在最近的两个项目中,项目需求要求我们实现 /*登陆页面的内容能够随着键盘的弹出而被顶上去,避免键盘遮挡住登陆按钮*/ 这样的效果,宝宝心里苦
- C# DateTime与时间戳的相互转换,包括JavaScript时间戳和Unix的时间戳。1. 什么是时间戳首先要清楚JavaScript
- 1.概述其实最简单的办法就是使用原生sql,如 session.createSQLQuery("sql"),或者使用jd
- 有时候我们需要在一个ArrayList的for循环中动态删除元素的需求, 废话不多说看代码List<Integer> list