带有 SliverAppBar 的 ScrollablePositionedList 无法正常工作
ScrollablePositionedList with SliverAppBar not working properly
This is a repository 创建一个最小的可重现示例。
我想在滚动 ScrollablePositionedList.builder
时隐藏 SliverAppBar
。这是我在此处包含的相关代码。
NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
backgroundColor: Colors.blue,
expandedHeight: 112,
snap: true,
pinned: false,
floating: true,
forceElevated: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.event),
)
],
flexibleSpace: SafeArea(
child: Column(
children: <Widget>[
Container(
height: kToolbarHeight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Title',
style: Theme.of(context)
.textTheme
.title
.copyWith(
fontSize: 16, color: Colors.white),
),
SizedBox(
height: 2,
),
Text(
'Date',
style: Theme.of(context)
.textTheme
.caption
.copyWith(
fontSize: 10, color: Colors.white),
),
SizedBox(
height: 2,
),
Text(
'Another Text',
style: Theme.of(context)
.textTheme
.subtitle
.copyWith(
fontSize: 14, color: Colors.white),
),
],
),
),
Expanded(
child: Container(
height: kToolbarHeight,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
'Prev',
),
Text(
'Next',
)
],
),
),
)
],
),
),
)
],
body: ScrollablePositionedList.builder(
physics: ScrollPhysics(),
itemPositionsListener: itemPositionListener,
itemScrollController: _itemScrollController,
initialScrollIndex: 0,
itemCount: 500,
itemBuilder: (BuildContext ctxt, int index) {
return Container(
margin: EdgeInsets.all(16)
,
child: Text('$index'));
})),
到目前为止,我尝试了两种方法 none,其中的方法工作正常,
方法一
I added physics: ScrollPhysics(),
to ScrollablePositionedList.builder
输出:
方法 2
I added physics: NeverScrollableScrollPhysics(),
to ScrollablePositionedList.builder
SliverAppBar
这次隐藏了,但现在我无法滚动到 ScrollablePositionedList.builder
的末尾 我的列表中有 500 个项目,但它只能滚动到第 14 个项目,查看输出。此外,它在滚动时滞后太多
输出:
提前致谢。
自己回答问题
This problem has no solution for it. I have created an issue here
看起来 ScrollablePositionedList
和 SliverAppBar
无法工作,直到 Flutter Team 不添加 shrinkwrap
属性 到 ScrollablePositionedList
.
添加 shrinkwrap
的功能请求已创建 here
It works for me
//create list of global keys
List<GlobalKey> _formKeys = [];
//assign keys from your list
for(int i=0 ;i< syourlist.length;i++){
final key = GlobalKey();
_formKeys.add(key);
}
//in list view give key as below
key:_formKeys[index]
//on button click
Scrollable.ensureVisible(_formKeys[index].currentContext);
这是一个基本的解决方法:
- 使用 ItemsPositionsListener 侦听列表滚动到的当前项目。
- 然后创建布尔值来检查 scroll-direction 和金额。
- 这些条件控制一个控制自定义高度的 AnimatedContainer header。
- 它作为 child 放置在具有 header 的列中,因此可滚动列表在动画前后正确占据 space。
虽然这是非常基础的,没有使用 NestedScrollView,但它仍然使用 ScrollablePositionedList,并根据设置的滚动条件实现与滑入和滑出的 header 类似的效果。
以防万一帮助其他人,直到根本问题得到解决...:)
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
class ScrollAllWords extends StatefulWidget {
const ScrollAllWords({
Key? key,
required this.list,
}) : super(key: key);
final List<String> list;
@override
State<ScrollAllWords> createState() => _ScrollAllWordsState();
}
class _ScrollAllWordsState extends State<ScrollAllWords> {
/// use this listener to control the header position.
final _itemPositionsListener = ItemPositionsListener.create();
///Can also use the ItemScrollController to animate through the list (code omitted)
final _itemScrollController = ItemScrollController();
/// Gets the current index the list has scrolled to.
int _currentIndex = 0;
/// Compares against current index to determine the scroll direction.
int _shadowIndex = 0;
bool _reverseScrolling = false;
bool _showHeader = true;
@override
void initState() {
/// Set up the listener.
_itemPositionsListener.itemPositions.addListener(() {
checkScroll();
});
super.initState();
}
void checkScroll() {
/// Gets the current index of the scroll.
_currentIndex =
_itemPositionsListener.itemPositions.value
.elementAt(0)
.index;
/// Checks the scroll direction.
if (_currentIndex > _shadowIndex) {
_reverseScrolling = false;
_shadowIndex = _currentIndex;
}
if (_currentIndex < _shadowIndex) {
_reverseScrolling = true;
_shadowIndex = _currentIndex;
}
/// Checks whether to show or hide the scroller (e.g. show when scrolled passed 15 items and not reversing).
if (!_reverseScrolling && _currentIndex > 15) {
_showHeader = false;
} else {
_showHeader = true;
}
setState(() {});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 120),
height: _showHeader ? 200 : 0,
curve: Curves.easeOutCubic,
child: Container(
color: Colors.red,
height: size.height * 0.20,
),
),
Flexible(
child: ScrollablePositionedList.builder(
itemScrollController: _itemScrollController,
itemPositionsListener: _itemPositionsListener,
itemCount: widget.list.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.list[index]),
);
},
),
),
],
);
}
}
This is a repository 创建一个最小的可重现示例。
我想在滚动 ScrollablePositionedList.builder
时隐藏 SliverAppBar
。这是我在此处包含的相关代码。
NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
backgroundColor: Colors.blue,
expandedHeight: 112,
snap: true,
pinned: false,
floating: true,
forceElevated: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.event),
)
],
flexibleSpace: SafeArea(
child: Column(
children: <Widget>[
Container(
height: kToolbarHeight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Title',
style: Theme.of(context)
.textTheme
.title
.copyWith(
fontSize: 16, color: Colors.white),
),
SizedBox(
height: 2,
),
Text(
'Date',
style: Theme.of(context)
.textTheme
.caption
.copyWith(
fontSize: 10, color: Colors.white),
),
SizedBox(
height: 2,
),
Text(
'Another Text',
style: Theme.of(context)
.textTheme
.subtitle
.copyWith(
fontSize: 14, color: Colors.white),
),
],
),
),
Expanded(
child: Container(
height: kToolbarHeight,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
'Prev',
),
Text(
'Next',
)
],
),
),
)
],
),
),
)
],
body: ScrollablePositionedList.builder(
physics: ScrollPhysics(),
itemPositionsListener: itemPositionListener,
itemScrollController: _itemScrollController,
initialScrollIndex: 0,
itemCount: 500,
itemBuilder: (BuildContext ctxt, int index) {
return Container(
margin: EdgeInsets.all(16)
,
child: Text('$index'));
})),
到目前为止,我尝试了两种方法 none,其中的方法工作正常,
方法一
I added
physics: ScrollPhysics(),
toScrollablePositionedList.builder
输出:
方法 2
I added
physics: NeverScrollableScrollPhysics(),
toScrollablePositionedList.builder
SliverAppBar
这次隐藏了,但现在我无法滚动到 ScrollablePositionedList.builder
的末尾 我的列表中有 500 个项目,但它只能滚动到第 14 个项目,查看输出。此外,它在滚动时滞后太多
输出:
提前致谢。
自己回答问题
This problem has no solution for it. I have created an issue here
看起来 ScrollablePositionedList
和 SliverAppBar
无法工作,直到 Flutter Team 不添加 shrinkwrap
属性 到 ScrollablePositionedList
.
添加 shrinkwrap
的功能请求已创建 here
It works for me
//create list of global keys
List<GlobalKey> _formKeys = [];
//assign keys from your list
for(int i=0 ;i< syourlist.length;i++){
final key = GlobalKey();
_formKeys.add(key);
}
//in list view give key as below
key:_formKeys[index]
//on button click
Scrollable.ensureVisible(_formKeys[index].currentContext);
这是一个基本的解决方法:
- 使用 ItemsPositionsListener 侦听列表滚动到的当前项目。
- 然后创建布尔值来检查 scroll-direction 和金额。
- 这些条件控制一个控制自定义高度的 AnimatedContainer header。
- 它作为 child 放置在具有 header 的列中,因此可滚动列表在动画前后正确占据 space。
虽然这是非常基础的,没有使用 NestedScrollView,但它仍然使用 ScrollablePositionedList,并根据设置的滚动条件实现与滑入和滑出的 header 类似的效果。
以防万一帮助其他人,直到根本问题得到解决...:)
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
class ScrollAllWords extends StatefulWidget {
const ScrollAllWords({
Key? key,
required this.list,
}) : super(key: key);
final List<String> list;
@override
State<ScrollAllWords> createState() => _ScrollAllWordsState();
}
class _ScrollAllWordsState extends State<ScrollAllWords> {
/// use this listener to control the header position.
final _itemPositionsListener = ItemPositionsListener.create();
///Can also use the ItemScrollController to animate through the list (code omitted)
final _itemScrollController = ItemScrollController();
/// Gets the current index the list has scrolled to.
int _currentIndex = 0;
/// Compares against current index to determine the scroll direction.
int _shadowIndex = 0;
bool _reverseScrolling = false;
bool _showHeader = true;
@override
void initState() {
/// Set up the listener.
_itemPositionsListener.itemPositions.addListener(() {
checkScroll();
});
super.initState();
}
void checkScroll() {
/// Gets the current index of the scroll.
_currentIndex =
_itemPositionsListener.itemPositions.value
.elementAt(0)
.index;
/// Checks the scroll direction.
if (_currentIndex > _shadowIndex) {
_reverseScrolling = false;
_shadowIndex = _currentIndex;
}
if (_currentIndex < _shadowIndex) {
_reverseScrolling = true;
_shadowIndex = _currentIndex;
}
/// Checks whether to show or hide the scroller (e.g. show when scrolled passed 15 items and not reversing).
if (!_reverseScrolling && _currentIndex > 15) {
_showHeader = false;
} else {
_showHeader = true;
}
setState(() {});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 120),
height: _showHeader ? 200 : 0,
curve: Curves.easeOutCubic,
child: Container(
color: Colors.red,
height: size.height * 0.20,
),
),
Flexible(
child: ScrollablePositionedList.builder(
itemScrollController: _itemScrollController,
itemPositionsListener: _itemPositionsListener,
itemCount: widget.list.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.list[index]),
);
},
),
),
],
);
}
}