I'm trying to build a screen where two vertically stacked ListViews cause themselves to grow and shrink as a result of being scrolled. Here is an illustration:
The initial state is that both lists take up 50% of the top and bottom of the screen respectively. When the user starts dragging the top list downward (to scroll up) it will initially cause the list to expand to take up 75% of the screen before the normal scrolling behavior starts; when the user changes direction, dragging upwards (to scroll down), then as they get to the bottom of the list it will cause the list to shrink back up to only taking up 50% of the screen (the initial state).
The bottom list would work similarly, dragging up would cause the list to expand upwards to take up 75% of the screen before the normal scrolling behavior starts; when the user changes direction, dragging downwards (to scroll up), then as they get to the top of the list it will shrink back to 50% of the screen.
My question is, what is the best widget combination to implement this and how do I tie the scrolling events with resizing the ListViews?
So far, this is as far as I've gotten:
Column(
children: [
SizedBox(
height: availableHeight / 2,
child: ListView(...)
),
Expanded(child: ListView(...)),
],
),
In terms of similar behavior, it appears that the CustomScrollView and SliverAppBar have some of the elements in scrolling behaving I'm going after but it's not obvious to me how to convert that into the the two adjacent lists view I described above.
Any advice would be greatly appreciated, thank you!
CodePudding user response:
hi Check this,
Column(
children: [
Expanded (
flex:7,
child: Container(
child: ListView.builder(
itemCount:50,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const Icon(Icons.list),
trailing: const Text(
"GFG",
style: TextStyle(color: Colors.green, fontSize: 15),
),
title: Text("List item $index"));
}),
),
),
Expanded (
flex:3,
child: Container(
child: ListView.builder(
itemCount:50,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const Icon(Icons.list),
trailing: const Text(
"GFG",
style: TextStyle(color: Colors.green, fontSize: 15),
),
title: Text("aaaaaaaaa $index"));
}),
),
),
],
),
CodePudding user response:
First, initialise two scroll controllers for two of your listviews. Then register a post-frame callback by using WidgetsBinding.instance.addPostFrameCallback
to make sure that the scroll controller has been linked to a scroll view. Next, setup scroll listeners in that callback.
To listen to scrolling update you can use scrollController.addListener
. As we do not need to know if the user is scrolling now or not, I have commented out that portion but left it there for you, if you needed it.
To listen to start and stop scrolling you can use scrollController.position.isScrollingNotifier.addListener
. Later, we can use two booleans to change the height of the scrolled listview. You can check my code implementation below:
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final ScrollController _scrollCtrl1 = ScrollController();
final ScrollController _scrollCtrl2 = ScrollController();
bool hasScrollStarted1 = false;
bool hasScrollStarted2 = false;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// _scrollCtrl1.addListener(() {
// print('scrolling');
// });
_scrollCtrl1.position.isScrollingNotifier.addListener(() {
if(!_scrollCtrl1.position.isScrollingNotifier.value) {
setState(() {
hasScrollStarted1 = false;
});
} else {
print('scroll is started');
setState(() {
hasScrollStarted1 = true;
});
}
});
_scrollCtrl2.position.isScrollingNotifier.addListener(() {
if(!_scrollCtrl2.position.isScrollingNotifier.value) {
setState(() {
hasScrollStarted2 = false;
});
} else {
print('scroll is started');
setState(() {
hasScrollStarted2 = true;
});
}
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
Expanded (
flex: hasScrollStarted1 ? 7 : hasScrollStarted2 ? 3 : 1,
child: ListView.builder(
itemCount:50,
controller: _scrollCtrl1,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const Icon(Icons.list),
trailing: const Text(
"GFG",
style: TextStyle(color: Colors.green, fontSize: 15),
),
title: Text("List item $index"));
}),
),
const Divider(thickness: 5,),
Expanded (
flex: hasScrollStarted2 ? 7 : hasScrollStarted1 ? 3 : 1,
child: ListView.builder(
itemCount:50,
controller: _scrollCtrl2,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const Icon(Icons.list),
trailing: const Text(
"GFG",
style: TextStyle(color: Colors.green, fontSize: 15),
),
title: Text("aaaaaaaaa $index"));
}),
),
],
),
);
}
}
Happy Coding !!