This is a repository to create a minimal reproducible example.
I want SliverAppBar
hidden when ScrollablePositionedList.builder
is Scrolled. This is the relevant piece of code I am including here.
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'));
})),
I tried two approaches so far none of them working properly,
Approach 1
I added
physics: ScrollPhysics(),
toScrollablePositionedList.builder
Output:
Appraoch 2
I added
physics: NeverScrollableScrollPhysics(),
toScrollablePositionedList.builder
SliverAppBar
hides this time but now I can not scroll to the very end of ScrollablePositionedList.builder
I have 500 items on my list but it scrolls up to only 14th item, see the output. Also, it lags too much on scroll
Output:
Thanks in advance.
Solution 1: Ravinder Kumar
Answering question myself
This problem has no solution for it. I have created an issue here
It looks like ScrollablePositionedList
with SliverAppBar
cannot work until Flutter Team does not add shrinkwrap
property to ScrollablePositionedList
.
Feature request to add shrinkwrap
is created here
Solution 2: vinod kumar
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);
Solution 3: Kdon
Here is a basic workaround:
- Use the ItemsPositionsListener to listen for the current item the list has scrolled to.
- Then create boolean values to check the scroll-direction and amount.
- These conditions control an AnimatedContainer controlling the height of a custom header.
- This is placed as a child in a Column with the header in a Flexible widget so the scrollablelist correctly takes up the space before and after animation.
Although this is very basic and does not use the NestedScrollView, it keeps use of the ScrollablePositionedList, and achieves a similar effect with a header that slides in and out, based on the set scroll conditions.
Providing in case helps anyone else, until the underlying issue is fixed...:)
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]),
);
},
),
),
],
);
}
}