overscroll_feedback 0.0.1+1
overscroll_feedback: ^0.0.1+1 copied to clipboard
A Flutter ListView that reveals custom feedback when users pull beyond the scroll edge, with optional haptics and callbacks.
overscroll_feedback #
A Flutter widget that reveals custom feedback when users pull beyond the end of a list.
It is useful for chat screens, message lists, feed pages, horizontal carousels, and any scrollable UI where you want to show a small “you reached the end” indicator, trigger haptic feedback, or run a callback when the user keeps pulling past the scroll limit.
Demo #
[Overscroll Feedback demo]
Features #
- Reveal a custom widget when the user pulls beyond the end of a list.
- Supports vertical and horizontal
ListView. - Works with simple string lists or custom item types.
- Optional haptic feedback when the pull distance reaches a threshold.
- Custom haptic callback support.
- One-shot callback when the scroll limit is reached.
- Repeated hold callback while the user keeps pulling.
- Configurable reveal distance, haptic trigger distance, feedback size, and offset.
Installation #
Add the package to your pubspec.yaml:
dependencies:
overscroll_feedback: ^0.0.1
Then run:
flutter pub get
Import it:
import 'package:overscroll_feedback/overscroll_feedback.dart';
Quick start #
Use OverscrollFeedback.strings when you want a simple list of text items.
OverscrollFeedback.strings(
messages: const [
'Welcome aboard',
'Pull gently',
'Keep scrolling',
'Now pull past the end',
],
reachLimitWidget: DecoratedBox(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(999),
),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12),
child: Text(
'You reached the end',
style: TextStyle(color: Colors.white),
),
),
),
onReachLimit: () {
debugPrint('Reached the bottom');
},
)
Custom item type #
Use the generic constructor when your list contains custom model objects.
class Message {
const Message({
required this.text,
required this.sender,
});
final String text;
final String sender;
}
final messages = [
const Message(text: 'Hello', sender: 'Me'),
const Message(text: 'Hi there', sender: 'Friend'),
];
OverscrollFeedback<Message>(
items: messages,
itemBuilder: (context, message, index) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(16),
),
child: Text('${message.sender}: ${message.text}'),
);
},
reachLimitWidget: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(999),
),
child: const Text(
'No more messages',
style: TextStyle(color: Colors.white),
),
),
)
Horizontal list #
For horizontal lists, set isHorizontal: true.
A horizontal list needs a fixed height, so height is required when
isHorizontal is true.
OverscrollFeedback.strings(
isHorizontal: true,
height: 140,
messages: const [
'Card',
'Article',
'Story',
'Preview',
],
itemWidth: 180,
reachLimitWidget: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.deepPurple,
borderRadius: BorderRadius.circular(16),
),
child: const Text(
'End',
style: TextStyle(color: Colors.white),
),
),
)
Callbacks #
onReachLimit #
Called once when the user pulls far enough to reach the feedback trigger.
OverscrollFeedback.strings(
messages: const ['One', 'Two', 'Three'],
reachLimitWidget: const Text('End reached'),
onReachLimit: () {
debugPrint('User reached the scroll limit');
},
)
onReachLimitHold #
Called repeatedly while the user keeps holding/pulling beyond the limit.
OverscrollFeedback.strings(
messages: const ['One', 'Two', 'Three'],
reachLimitWidget: const Text('Keep holding...'),
holdTriggerDistance: 140,
holdInterval: const Duration(milliseconds: 500),
onReachLimitHold: (pullDistance) {
debugPrint('Still pulling: $pullDistance');
},
)
Custom haptic feedback #
By default, the widget uses Flutter haptic vibration when the haptic trigger distance is reached.
You can provide your own haptic feedback callback:
OverscrollFeedback.strings(
messages: const ['One', 'Two', 'Three'],
reachLimitWidget: const Text('Limit reached'),
hapticFeedback: (pullDistance) {
if (pullDistance > 180) {
return HapticFeedback.heavyImpact();
}
return HapticFeedback.selectionClick();
},
)
You can also disable haptics:
OverscrollFeedback.strings(
messages: const ['One', 'Two', 'Three'],
reachLimitWidget: const Text('Limit reached'),
hapticsEnabled: false,
)
Tuning the reveal behavior #
You can control when the feedback starts appearing, when it becomes fully visible, and when haptics/callbacks are triggered.
OverscrollFeedback.strings(
messages: const ['One', 'Two', 'Three'],
reachLimitWidget: const Text('You reached the end'),
revealStartDistance: 32,
revealEndDistance: 120,
hapticTriggerDistance: 140,
)
Important values #
| Property | Description |
|---|---|
revealStartDistance |
Pull distance where the feedback widget starts appearing. |
revealEndDistance |
Pull distance where the feedback widget becomes fully visible. |
hapticTriggerDistance |
Pull distance where haptic feedback and onReachLimit trigger. |
holdTriggerDistance |
Pull distance where the repeated hold callback starts. |
holdInterval |
How often onReachLimitHold is called while holding. |
reachLimitExtent |
Space reserved for the revealed feedback widget. |
offset |
Starting offset animation for the revealed widget. |
hapticsEnabled |
Enables or disables built-in haptic feedback. |
hapticFeedback |
Custom haptic callback. |
Full example #
import 'package:flutter/material.dart';
import 'package:overscroll_feedback/overscroll_feedback.dart';
class OverscrollDemoPage extends StatelessWidget {
const OverscrollDemoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: OverscrollFeedback.strings(
messages: List.generate(12, (index) => 'Message'),
revealStartDistance: 40,
revealEndDistance: 120,
hapticTriggerDistance: 135,
reachLimitWidget: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(999),
),
child: const Text(
'You reached the end',
style: TextStyle(color: Colors.white),
),
),
onReachLimit: () {
debugPrint('Reached the list end');
},
onReachLimitHold: (pullDistance) {
debugPrint('Holding at distance: $pullDistance');
},
),
),
);
}
}
Notes #
Haptics #
Haptic feedback depends on the device, platform, and system settings.
For best testing, use a real device instead of a simulator or emulator. Some devices may ignore repeated vibration requests or make them feel weaker than expected.
Android vibration permission #
If your custom haptic implementation uses a vibration plugin, you may need to add the Android vibration permission:
<uses-permission android:name="android.permission.VIBRATE"/>
Flutter's built-in HapticFeedback does not require extra Dart setup.
Horizontal lists #
When using isHorizontal: true, provide a fixed height.
OverscrollFeedback.strings(
isHorizontal: true,
height: 160,
messages: const ['One', 'Two', 'Three'],
reachLimitWidget: const Text('End'),
)
Example app #
A runnable example app is included in the example folder.
cd example
flutter run
API overview #
OverscrollFeedback<T> #
Generic widget for custom item types.
OverscrollFeedback<T>({
required List<T> items,
required ReachLimitItemBuilder<T> itemBuilder,
required Widget reachLimitWidget,
})
OverscrollFeedback.strings #
Convenience constructor for simple string lists.
OverscrollFeedback.strings({
required List<String> messages,
required Widget reachLimitWidget,
})
License #
MIT