В предыдущей статье об элементах управления прокруткой мы упоминалиcontroller
атрибут, он получаетScrollController
объект.ScrollController
Основная роль заключается в управлении положением прокрутки и прослушивании событий прокрутки.
Эта глава начинается сListView
Например, показатьScrollController
конкретное использование
ScrollController
ScrollController
Управляйте положением прокрутки и слушайте события прокрутки.
Пример исходного кода
Конструктор выглядит следующим образом:
ScrollController({
double initialScrollOffset = 0.0, //初始滚动位置
this.keepScrollOffset = true,//是否保存滚动位置
...
})
объяснение атрибута
Значение конкретных атрибутов было указано выше, вот введениеScrollController
Часто используемые свойства и методы
offset
Это свойство представляет текущую позицию прокрутки прокручиваемого компонента.
прыгать, анимировать
jumpTo(double offset)
,animateTo(double offset,...)
: Оба эти метода используются для прыжка в указанную позицию, разница в том, что последний выполняет анимацию при прыжке, а первый нет.
монитор прокрутки
ScrollController
Косвенно унаследовано отListenable
, мы можем согласноScrollController
Чтобы прослушать события прокрутки,
Например:
controller.addListener(()=>print(controller.offset))
Этот код печатает текущую позицию прокрутки
Пример кода:
мы создаемListView
, при изменении позиции прокрутки мы сначала распечатываем текущую позицию прокрутки, а затем оцениваем, превышает ли текущая позиция 1000 пикселей.Если она превышает, в правом нижнем углу экрана будет отображаться кнопка «Вернуться к началу».ListView
Вернитесь в исходное положение, если не более 1000 пикселей, скройте кнопку «наверх».
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
ScrollController _controller = new ScrollController();
bool showToTopBtn = false; //是否显示“返回到顶部”按钮
@override
void 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();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("滚动控制")),
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,
duration: Duration(milliseconds: 200), curve: Curves.ease);
}),
);
}
}
Так как высота элемента списка составляет 50 пикселей, при пролистывании 20-го элемента списка в правом нижнем углу будет отображаться кнопка «Вернуться к началу».Нажав эту кнопку, ListView будет выполнять анимацию прокрутки в процессе возвращается наверх, а время анимации составляет 200 миллисекунд. , кривая анимацииCurves.ease
текущий результат:
восстановление положения прокрутки
PageStorage
Является ли компонент, используемый для сохранения данных, связанных со страницей (маршрутизацией), он не влияет на внешний вид пользовательского интерфейса поддерева.
Каждый раз, когда прокрутка заканчивается, прокручиваемый компонент меняет положение прокрутки.offset
хранить вPageStorage
, восстановить при воссоздании прокручиваемого компонента.
еслиScrollController.keepScrollOffset
дляfalse
, позиция прокрутки не будет сохранена и будет использоваться при воссоздании прокручиваемого компонентаScrollController.initialScrollOffset
;
когдаScrollController.keepScrollOffset
дляtrue
, прокручиваемый компонент находится впервый разПосле создания прокрутите доinitialScrollOffset
, так как позиция прокрутки еще не сохранена. При следующем прокрутке позиция прокрутки сохраняется и восстанавливается, иinitialScrollOffset
будет игнорироваться.
Когда маршрут содержит несколько прокручиваемых компонентов, если вы обнаружите, что положение прокрутки не может быть правильно восстановлено после выполнения некоторых переходов или операций переключения, вы можете явно указатьPageStorageKey
отслеживать положение различных прокручиваемых компонентов по отдельности,
Такие как:
ListView(key: PageStorageKey(1), ... );
...
ListView(key: PageStorageKey(2), ... );
разныеPageStorageKey
, требует другого значения, чтобы положение прокрутки можно было сохранить для различных прокручиваемых компонентов.
Примечание. Когда маршрут содержит несколько прокручиваемых компонентов, если вы хотите отслеживать их положения прокрутки отдельно, вам не нужно указывать их отдельно.
PageStorageKey
. Это связано с тем, что Scrollable сам по себе является StatefulWidget, и его состояние также сохраняет текущую позицию прокрутки, пока сам прокручиваемый компонент не удаляется из дерева.detach
Если его отбросить, то его состояние не будет уничтожено (удалено), и позиция прокрутки не будет потеряна. Состояние теряется только при изменении структуры виджета, что приводит к разрушению или перестроению состояния прокручиваемого компонента, в этом случае его нужно указать явно.PageStorageKey
,пройти черезPageStorage
для хранения положения прокрутки типичным сценарием является использованиеTabBarView
При переключении вкладки состояние прокручиваемого компонента на странице вкладки будет уничтожено.В это время, если вы хотите восстановить положение прокрутки, вам нужно указатьPageStorageKey
.
ScrollPosition
ScrollPosition
используется для сохранения положения прокрутки прокручиваемого компонента.
ОдинScrollController
Объекты могут использоваться несколькими прокручиваемыми компонентами одновременно,ScrollController
создаст по одному для каждого прокручиваемого компонентаScrollPosition
объекты, этиScrollPosition
Сохранить какScrollController
изpositions
в свойствах.
ScrollPosition
это объект, который фактически сохраняет информацию о позиции скольжения,offset
Просто свойство удобства:
double get offset => position.pixels;
Как видно из вышеизложенногоoffect
изposition
из.
ОдинScrollController
Хотя это может соответствовать нескольким прокручиваемым компонентам, существуют некоторые операции, такие как чтение положения прокрутки.offset
, вам нужно один к одному!
Но мы все еще можем прочитать положение прокрутки с помощью других методов в случае «один ко многим»,
Например, предположим,ScrollController
Используется двумя прокручиваемыми компонентами одновременно, тогда мы можем читать их позиции прокрутки отдельно следующими способами:
...
controller.positions.elementAt(0).pixels
controller.positions.elementAt(1).pixels
...
мы можем пройтиcontroller.positions.length
Чтобы убедитьсяcontroller
Используется несколькими прокручиваемыми компонентами.
ScrollPosition
Есть два распространенных метода:animateTo()
а такжеjumpTo()
, они являются реальным способом управления положением прокрутки прыжка,ScrollController
Эти два метода с одинаковыми именами в конечном итоге будут вызываться внутриScrollPosition
из.
должны знать о том,
ScrollController
изanimateTo()
а такжеjumpTo()
Внутренние вызовы всехScrollPosition
изanimateTo()
а такжеjumpTo()
, добиться всего иScrollController
Связанные прокручиваемые компоненты прокручиваются до указанной позиции.
Пример слушателя прокрутки
Далее мониторимListView
Затем в уведомлении о прокрутке отображается текущий процент выполнения прокрутки:
import 'package:flutter/material.dart';
class ScrollNotificationTestRoute extends StatefulWidget {
@override
_ScrollNotificationTestRouteState createState() =>
new _ScrollNotificationTestRouteState();
}
class _ScrollNotificationTestRouteState
extends State<ScrollNotificationTestRoute> {
String _progress = "0%"; //保存进度百分比
@override
Widget build(BuildContext context) {
return Scrollbar( //进度条
// 监听滚动通知
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
double progress = notification.metrics.pixels /
notification.metrics.maxScrollExtent;
//重新构建
setState(() {
_progress = "${(progress * 100).toInt()}%";
});
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,
)
],
),
),
);
}
}
текущий результат:
Дочерние виджеты в дереве виджетов Flutter могут взаимодействовать с родительскими (включая предков) виджетами, отправляя уведомления. Родительский компонент может пройтиNotificationListener
Компоненты для прослушивания уведомлений об их собственном внимании, этот метод связи похож на всплывающую подсказку событий браузера в веб-разработке.
Прокручиваемые компоненты отправляются при прокруткеScrollNotification
тип уведомления,ScrollBar
Он делает это, прослушивая уведомления о прокрутке. пройти черезNotificationListener
Слушайте события прокрутки и пройтиScrollController
Есть два основных отличия:
-
пройти через
NotificationListener
Вы можете слушать где угодно, от прокручиваемого компонента до корня дерева виджетов. а такжеScrollController
Он может быть связан только с определенным прокручиваемым компонентом. -
Информация, полученная после получения события прокрутки, отличается;
NotificationListener
При получении события прокрутки уведомление будет содержать текущую позицию прокрутки иViewPort
некоторую информацию, покаScrollController
Можно получить только текущую позицию прокрутки.
Когда получено событие прокрутки, тип параметраScrollNotification
, который включает в себяmetrics
свойство, его типScrollMetrics
, который содержит текущийViewPort
и положение прокрутки и другую информацию:
-
pixels
: Текущая позиция прокрутки. -
maxScrollExtent
: Максимальная прокручиваемая длина. -
extentBefore
: длина слайда из верхней части окна просмотра; в этом примере она эквивалентна длине списка над верхней частью слайда из экрана. -
extentInside
: внутренняя длина ViewPort; в данном примере это длина области списка на экране. -
extentAfter
: Длина части списка, которая не перемещается в ViewPort; длина части экрана, которая не отображается в нижней части списка в этом примере. -
atEdge
: следует ли скользить к границам прокручиваемого компонента (эквивалентно верхней или нижней части списка в этом примере).