flutter_magnetic_scroll 0.1.0
flutter_magnetic_scroll: ^0.1.0 copied to clipboard
A Flutter custom ScrollPhysics package for TikTok/Reels style feeds and pickers. Features magnetic snapping to fixed or variable item boundaries with physics-based smoothness.
flutter_magnetic_scroll #
A custom ScrollPhysics package designed for "TikTok/Reels" style feeds or custom pickers. It allows developers to define "magnetic snap points" based on item size. The user can freely scroll, but when they let go, the UI aggressively and smoothly snaps to the nearest logical item boundary.
Features #
- TikTok/Reels Style Scrolling: Easily snap to full screen or fixed size items.
- Variable Item Sizes: Supports lists with dynamically sized items.
- Strict Single Item Snapping: Enforce a "one item per swipe" rule so users can't accidentally skip past content when scrolling fast.
- MagneticScrollController: A specialized controller to programmatically jump or animate to specific items, and effortlessly track the currently focused item.
- Early Event Triggers: A built-in
onItemFocusedcallback that fires exactly when the physics engine predicts its target, allowing you to prep/play videos or animations early!
Usage #
1. Fixed Size Items (TikTok/Reels clone) #
Provide the MagneticScrollPhysics to any Scrollable widget (like ListView).
You just need to pass the itemSize, which represents the height (for vertical) or width (for horizontal) of each item.
import 'package:flutter/material.dart';
import 'package:flutter_magnetic_scroll/flutter_magnetic_scroll.dart';
class MyTikTokFeed extends StatelessWidget {
@override
Widget build(BuildContext context) {
// For a full-screen feed, the item size is typically the screen height
final itemHeight = MediaQuery.of(context).size.height;
return ListView.builder(
physics: MagneticScrollPhysics(
itemSize: itemHeight,
strictSingleItemSnapping: true, // Only advances 1 item per swipe
onItemFocused: (index) {
debugPrint('Starting playback for video \$index');
},
),
itemCount: 10,
itemBuilder: (context, index) {
return Container(
height: itemHeight,
child: Center(child: Text('Video \$index')),
);
},
);
}
}
2. Variable Size Items & Programmatic Navigation #
If your items are all different sizes, provide a List<double> to the itemSizes property. You can also use a MagneticScrollController to animate to specific items or track state easily.
class VariableSizeFeed extends StatefulWidget {
@override
_VariableSizeFeedState createState() => _VariableSizeFeedState();
}
class _VariableSizeFeedState extends State<VariableSizeFeed> {
late MagneticScrollController _controller;
final List<double> _sizes = [200.0, 300.0, 400.0, 200.0];
@override
void initState() {
super.initState();
_controller = MagneticScrollController(itemSizes: _sizes);
// Listen to changes in the currently focused item
_controller.currentIndex.addListener(() {
debugPrint('Snapped to item: \${_controller.currentIndex.value}');
});
}
void _nextItem() {
_controller.animateToItem(
index: _controller.currentIndex.value + 1,
duration: const Duration(milliseconds: 300)
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _controller,
physics: MagneticScrollPhysics(itemSizes: _sizes),
itemCount: _sizes.length,
itemBuilder: (context, index) {
return Container(
height: _sizes[index],
child: Center(child: Text('Item \$index')),
);
},
);
}
}
Check the example/ folder for a more detailed implementation!