【Flutter】可滚动组件之滚动控制和监听
前言
可以用ScrollController来控制可滚动组件的滚动位置。
接口描述
ScrollController({
// 初始滚动位置
double initialScrollOffset = 0.0,
// 是否保持滚动位置
this.keepScrollOffset = true,
this.debugLabel,
})
代码示例
// ScrollController
// 可以用ScrollController来控制可滚动组件的滚动位置。
import 'package:flutter/material.dart';
class ScrollControllerTest extends StatefulWidget{
@override
ScrollControllerTestState createState() => ScrollControllerTestState();
}
class ScrollControllerTestState extends State<ScrollControllerTest>{
ScrollController _controller = ScrollController();
// 是否显示“返回到顶部”按钮
bool showToTopBtn = false;
@override
void initState(){
super.initState();
// 监听滚动事件,打印滚动位置
_controller.addListener((){
// 打印滚动位置
print(_controller.offset);
if(_controller.offset < 1000 && showToTopBtn){
setState(() {
showToTopBtn = false;
});
} else if(_controller.offset >= 1000 && showToTopBtn == false){
setState(() {
showToTopBtn = true;
});
}
});
}
@override
void dispose(){
// 为了避免内存泄漏,需要调用_controller.dispose
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text('滚动控制1'),
),
body: Scrollbar(
child: ListView.builder(
itemCount: 100,
itemExtent: 50.0,
controller: _controller,
itemBuilder: (context, index){
return ListTile(title: Text("$index"),);
},
),
),
floatingActionButton: !showToTopBtn ? null : FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: (){
// 返回到顶部时执行动画
_controller.animateTo(
.0,
// 返回顶部的过程中执行一个滚动动画,动画时间是200毫秒,动画曲线是Curves.ease
duration: Duration(milliseconds: 200),
curve: Curves.ease,
);
},
),
);
}
}
// 滚动监听
class ScrollNotificationTest extends StatefulWidget{
@override
_ScrollNotificationTestState createState() => _ScrollNotificationTestState();
}
class _ScrollNotificationTestState extends State<ScrollNotificationTest>{
//保持进度百分比
String _progress = '0%';
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text('滚动控制2'),
),
// 进度条
body: Scrollbar(
// 监听滚动通知
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification){
// pixels:当前滚动位置;maxScrollExtent:最大可滚动长度
double progress = notification.metrics.pixels /
notification.metrics.maxScrollExtent;
// 重新构建
setState(() {
_progress = '${(progress * 100).toInt()}%';
});
// extentBefore:滑出ViewPort顶部的长度;此示例中相当于顶部滑出屏幕上方的列表长度。
// extentInside:ViewPort内部长度;此示例中屏幕显示的列表部分的长度。
// extentAfter:列表中未滑入ViewPort部分的长度;此示例中列表底部未显示到屏幕范围部分的长度。
// atEdge:是否滑到了可滚动组件的边界(此示例中相当于列表顶或底部)。
print('BottomEdge: ${notification.metrics.extentAfter == 0}');
//
return true;
},
child: Stack(
alignment: Alignment.center,
children: <Widget>[
ListView.builder(
itemCount: 100,
itemExtent: 50.0,
itemBuilder: (context, index){
return ListTile(title: Text('$index'),);
},
),
// 显示百分比
CircleAvatar(
radius: 30.0,
child: Text(_progress),
backgroundColor: Colors.black54,
)
],
),
),
),
);
}
}
总结
滚动位置恢复
PageStorage是一个用于保存页面(路由)相关数据的组件,它并不会影响子树的UI外观,其实,PageStorage是一个功能型组件,它拥有一个存储桶(bucket),子树中的Widget可以通过指定不同的PageStorageKey来存储各自的数据或状态。
每次滚动结束,可滚动组件都会将滚动位置offset存储到PageStorage中,当可滚动组件重新创建时再恢复。如果ScrollController.keepScrollOffset为false,则滚动位置将不会被存储,可滚动组件重新创建时会使用ScrollController.initialScrollOffset;ScrollController.keepScrollOffset为true时,可滚动组件在第一次创建时,会滚动到initialScrollOffset处,因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置,而initialScrollOffset会被忽略。
当一个路由中包含多个可滚动组件时,如果你发现在进行一些跳转或切换操作后,滚动位置不能正确恢复,这时你可以通过显式指定PageStorageKey来分别跟踪不同的可滚动组件的位置。
ScrollPosition
ScrollPosition是用来保存可滚动组件的滚动位置的。一个ScrollController对象可以同时被多个可滚动组件使用,ScrollController会为每一个可滚动组件创建一个ScrollPosition对象,这些ScrollPosition保存在ScrollController的positions属性中(List)。ScrollPosition是真正保存滑动位置信息的对象,offset只是一个便捷属性。
一个ScrollController虽然可以对应多个可滚动组件,但是有一些操作,如读取滚动位置offset,则需要一对一!但是我们仍然可以在一对多的情况下,通过其它方法读取滚动位置。ScrollPosition有两个常用方法:animateTo() 和 jumpTo(),它们是真正来控制跳转滚动位置的方法,ScrollController的这两个同名方法,内部最终都会调用ScrollPosition的。
ScrollController控制原理
ScrollController的另外三个方法:
ScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition);
void attach(ScrollPosition position) ;
void detach(ScrollPosition position) ;
当ScrollController和可滚动组件关联时,可滚动组件首先会调用ScrollController的createScrollPosition()方法来创建一个ScrollPosition来存储滚动位置信息,接着,可滚动组件会调用attach()方法,将创建的ScrollPosition添加到ScrollController的positions属性中,这一步称为“注册位置”,只有注册后animateTo() 和 jumpTo()才可以被调用。
当可滚动组件销毁时,会调用ScrollController的detach()方法,将其ScrollPosition对象从ScrollController的positions属性中移除,这一步称为“注销位置”,注销后animateTo() 和 jumpTo() 将不能再被调用。
需要注意的是,ScrollController的animateTo() 和 jumpTo()内部会调用所有ScrollPosition的animateTo() 和 jumpTo(),以实现所有和该ScrollController关联的可滚动组件都滚动到指定的位置。
滚动监听
Flutter Widget树中子Widget可以通过发送通知(Notification)与父(包括祖先)Widget通信。父级组件可以通过NotificationListener组件来监听自己关注的通知,这种通信方式类似于Web开发中浏览器的事件冒泡。
可滚动组件在滚动时会发送ScrollNotification类型的通知,ScrollBar正是通过监听滚动通知来实现的。通过NotificationListener监听滚动事件和通过ScrollController有两个主要的不同:
- 通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而ScrollController只能和具体的可滚动组件关联后才可以。
- 收到滚动事件后获得的信息不同;NotificationListener在收到滚动事件时,通知中会携带当前滚动位置和ViewPort的一些信息,而ScrollController只能获取当前滚动位置。
【Flutter】可滚动组件之滚动控制和监听的更多相关文章
- highcharts图表组件入门教程:如何监听柱状图柱子点击事件动态更新当前数据点数值和所对应X轴刻度
highcharts图表组件入门教程:如何监听柱状图柱子点击事件动态更新当前数据点数值和所对应X轴刻度 作者:highcharts | 时间:2014-6-11 14:07:05 | [小 大] | ...
- 【Flutter学习】可滚动组件之滚动监听及控制
一,概述 ScrollController可以用来控制可滚动widget的滚动位置 二,ScrollController 构造函数 ScrollController({ double initialS ...
- 对ListView滚动状态的监听
有的时候,我们需要对ListView滚动做一个相应的监听事件,例如:要实现如下图通讯录的功能: 思路为:首先呢,中间那个"路"字为一个TextView,它与ListView采用相对 ...
- 20、解决Vue使用bus兄弟组件间传值,第一次监听不到数据
1.新建bus.js文件: import Vue from 'vue' export default new Vue; 2.在需要通信组件A,B中引入bus: A组件: import Bus from ...
- 【Flutter学习】可滚动组件之ScrollView
一,概述 ScrollView 是一个带有滚动的视图组件. 二,组成部分 ScrollView 由三部分组成: Scrollable - 它监听各种用户手势并实现滚动的交互设计.可滚动Widget都直 ...
- 【Flutter】可滚动组件简介
前言 当组件内容超过当前显示视口(ViewPort)时,如果没有特殊处理,Flutter则会提示Overflow错误.为此,Flutter提供了多种可滚动组件(Scrollable Widget)用于 ...
- 【Flutter学习】可滚动组件之SingleChildScrollView
一,概述 SingleChildScrollView类似于Android中的ScrollView,它只能接收一个子Widget.定义如下: 二,构造函数 const SingleChildScroll ...
- vue.js 组件监听
一.在通过点击事件触发的子组件中: addCart(event) { if (!event._constructed) { return; } if (!this.food.count) { Vue. ...
- 父组件向子组件传值时,值已经传过来却没有触发子组件的watch监听,解决~
需求: 父组件像封装的子组件传值 (父组件属性传值,子组件props接受) 子组件接受后经过处理回显页面; 预想:子组件接受值 , 经过watch监听,在监听中处理数据,回显数据; 问题:子组件 ...
随机推荐
- day106:MoFang:BUG:获取数据验证token是否过期&相册/相机取消头像无法显示&MongoDB
目录 BUG1:前端在获取数据时,要检验token是否过期 BUG2:相册/相机取消后设置页面头像无法显示 MongoDB 1.MongoDB基本介绍 2.MongoDB安装 3.MongoDB:通用 ...
- python学习之路 初识xml
import requests from xml.etree import ElementTree as ET r = requests.get('http://www.webxml.com.cn// ...
- (数据科学学习手札100)搞定matplotlib中的字体设置
本文示例文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 matplotlib作为数据可视化的利器,被广泛 ...
- do while 后面要加分号,你大爷的
do { //do something } while (0) TSfree(url); 这个TSFree 正好是个宏,然后编译就提示错误: error: expected ';' before '_ ...
- [日常摸鱼]UVA393 The Doors 简单计算几何+最短路
The Boy Next Doors 题意:给定一个固定大小的房间($x,y$的范围都是$[0,10]$),有$n$个墙壁作为障碍(都与横坐标轴垂直),每个墙壁都有两扇门分别用四个点来描述,起点 ...
- Eureka系列(一)Eureka功能介绍
Eureka核心功能点 服务注册(register): Client会发送一次Rest请求给Server端来实现注册,Server接受到请求会将服务信息存储起来,并将注册信息给同集群其他Serve ...
- 嵌入式开发笔记——调试组件SEGGER_RTT
一.前言 在嵌入式开发过程中,经常会通过打印输出一些调试信息来调试参数.查找问题等,通常我的做法都是使用芯片的串口硬件设备配合串口助手软件来进行调试.但是这次项目的PCB硬件设计并未预留串口调试接口, ...
- Python 炫技操作:安装包的八种方法,你知道吗?
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 1. 使用 easy_install easy_install 这应该是最古老的包安装方式了,目前基 ...
- python的常用内建模块与常用第三方模块
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 一.常用内置模块1.datetimePython 提供了一个 time 和 calendar 模块可 ...
- python初学者-从键盘获取信息
name = input(">>> 姓名:") QQ = input(">>>QQ: ") phone_num = inpu ...