Skip to content

Instantly share code, notes, and snippets.

@callmephil
Created July 15, 2024 22:04
Show Gist options
  • Save callmephil/1a8333636dc59ef88882dc51f6858bb8 to your computer and use it in GitHub Desktop.
Save callmephil/1a8333636dc59ef88882dc51f6858bb8 to your computer and use it in GitHub Desktop.
smooth page view
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SmoothPageView extends StatefulWidget {
const SmoothPageView({
super.key,
required this.onPageChanged,
required this.pages,
required this.pageIndex,
});
final ValueNotifier<int> pageIndex;
final void Function(int index) onPageChanged;
final List<Widget> pages;
@override
State<SmoothPageView> createState() => _SmoothPageViewState();
}
class _SmoothPageViewState extends State<SmoothPageView> {
late final PageController _pageController;
final FocusNode _focusNode = FocusNode();
bool _canPhysicScroll = true;
@override
void initState() {
super.initState();
_pageController = PageController(initialPage: widget.pageIndex.value)
..addListener(_onPageChanged);
widget.pageIndex.addListener(_onPageIndexChanged);
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
}
@override
void dispose() {
_focusNode.dispose();
_pageController.removeListener(_onPageChanged);
_pageController.dispose();
widget.pageIndex.removeListener(_onPageIndexChanged);
super.dispose();
}
void setSafeState(void Function() fn) {
if (!mounted) return;
setState(fn);
}
void _onPageIndexChanged() {
if (_pageController.page == widget.pageIndex.value) return;
if (_pageController.position.isScrollingNotifier.value) return;
_pageController.animateToPage(
widget.pageIndex.value,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
void _onPageChanged() {
// if (isInit) {
// isInit = false;
// }
// setSafeState(() {});
widget.onPageChanged(_pageController.page?.floor().abs() ?? 0);
}
int get _pageCount => widget.pages.length;
bool get _isScrolling => _pageController.position.isScrollingNotifier.value;
void _onPreviousPage() {
if (_isScrolling) return;
_pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
void _onNextPage() {
if (_isScrolling) return;
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
void _onPointerDown(PointerDownEvent event) {
if (event.position.dx > 10) return;
if (event.kind != PointerDeviceKind.mouse) return;
_setPhysicalScroll(true);
}
void _setPhysicalScroll(bool value) {
if (_canPhysicScroll == value) return;
setSafeState(() {
_canPhysicScroll = value;
});
// TODO: add a debounce timer to reset the canPhysicCall
}
void _onPointerSignal(PointerSignalEvent pointerSignal) {
if (pointerSignal is! PointerScrollEvent) return;
if (pointerSignal.kind == PointerDeviceKind.mouse) {
_setPhysicalScroll(false);
} else {
_setPhysicalScroll(true);
return;
}
if (_isScrolling) return;
// Check if the scroll is downward
if (pointerSignal.scrollDelta.dy > 0) {
// Scroll is going down, navigate to the next page
// Assuming itemCount is 3
if (_pageController.page == _pageCount) return;
_onNextPage();
} else {
if (_pageController.page == 0) return;
_onPreviousPage();
}
}
@override
Widget build(BuildContext context) {
return KeyboardListener(
focusNode: _focusNode,
onKeyEvent: (event) {
if (event is! KeyDownEvent) return;
switch (event.logicalKey) {
case LogicalKeyboardKey.arrowUp:
_onPreviousPage();
break;
case LogicalKeyboardKey.arrowDown:
_onNextPage();
break;
}
},
child: Stack(
fit: StackFit.expand,
children: [
Listener(
onPointerDown: _onPointerDown,
onPointerSignal: _onPointerSignal,
child: PageView.builder(
itemCount: _pageCount,
scrollDirection: Axis.vertical,
controller: _pageController,
scrollBehavior: const MaterialScrollBehavior(),
physics: _canPhysicScroll
? const AlwaysScrollableScrollPhysics()
: const NeverScrollableScrollPhysics(),
itemBuilder: (_, int index) {
return widget.pages[index];
},
),
),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment