Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save HansMuller/ce5c474a458f5f4bcc07b0d621843165 to your computer and use it in GitHub Desktop.
Save HansMuller/ce5c474a458f5f4bcc07b0d621843165 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
runApp(const ThumbDragApp());
}
class ThumbDragApp extends StatelessWidget {
const ThumbDragApp({ super.key });
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const ThumbDrag(),
);
}
}
class ThumbDrag extends StatefulWidget {
const ThumbDrag({ super.key });
@override
State<ThumbDrag> createState() => _ThumbDragState();
}
class _ThumbDragState extends State<ThumbDrag> {
static const int itemCount = 25;
static const double itemExtent = 100;
late final ScrollController scrollController;
late double lastScrollOffset;
@override
void initState() {
scrollController = ScrollController();
super.initState();
}
@override
void dispose() {
super.dispose();
scrollController.dispose();
}
bool handleScrollNotification(ScrollNotification notification) {
print(notification);
if (notification is ScrollStartNotification) {
lastScrollOffset = scrollController.position.pixels;
}
if (notification is ScrollEndNotification && scrollController.position.activity is! DrivenScrollActivity) {
final ScrollMetrics m = notification.metrics;
final int lastIndex = ((m.extentBefore + m.extentInside) ~/ itemExtent).clamp(0, itemCount - 1);
final double scrollOffset = itemExtent * (lastIndex + 1) - m.extentInside;
if ((scrollController.position.pixels - lastScrollOffset).abs() > itemExtent) {
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
scrollController.animateTo(
scrollOffset,
duration: const Duration(milliseconds: 400),
curve: Curves.fastOutSlowIn,
);
});
}
}
return true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Scrollbar(
controller: scrollController,
thumbVisibility: true,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: NotificationListener<ScrollNotification>(
onNotification: handleScrollNotification,
child: CustomScrollView(
controller: scrollController,
slivers: <Widget>[
SliverFixedExtentList(
itemExtent: itemExtent,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Item(
title: 'Item $index',
color: Color.lerp(Colors.red, Colors.blue, index / itemCount)!
);
},
childCount: itemCount,
),
),
],
),
),
),
),
),
);
}
}
class Item extends StatelessWidget {
const Item({ super.key, required this.title, required this.color });
final String title;
final Color color;
@override
Widget build(BuildContext context) {
return Card(
color: color,
child: ListTile(
textColor: Colors.white,
title: Text(title),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment