Skip to content

Instantly share code, notes, and snippets.

@TalalMash
Created March 7, 2024 04:14
Show Gist options
  • Save TalalMash/69a2e6fa9d2fd4af57be2620a5c93515 to your computer and use it in GitHub Desktop.
Save TalalMash/69a2e6fa9d2fd4af57be2620a5c93515 to your computer and use it in GitHub Desktop.
Flutter PageView: Proper scrolling with Scrollwheel, Touchpad and Touchscreen
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:universal_html/html.dart' as html;
bool isDesktop() {
final userAgent = html.window.navigator.userAgent.toString().toLowerCase();
if (kIsWeb) {
return !(userAgent.contains("iphone") ||
userAgent.contains("android") ||
userAgent.contains("ipad"));
} else {
return !(Platform.isAndroid || Platform.isIOS);
}
}
class Homepage extends StatefulWidget {
@override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
final PageController pageController = PageController();
bool pageIsScrolling = false;
// Debounce duration in milliseconds
final int debounceDuration = 20;
Timer? _debounce;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: isDesktop() ? _buildGestureDetector() : _buildPageView(),
),
);
}
Widget _buildGestureDetector() {
return GestureDetector(
onPanUpdate: (details) {
_onScroll(details.delta.dy * -1);
},
child: Listener(
onPointerSignal: (pointerSignal) {
if (pointerSignal is PointerScrollEvent) {
_onScroll(pointerSignal.scrollDelta.dy);
}
},
child: _buildPageView(),
),
);
}
Widget _buildPageView() {
return PageView(
scrollDirection: Axis.vertical,
controller: pageController,
physics: isDesktop()
? const NeverScrollableScrollPhysics()
: const AlwaysScrollableScrollPhysics(),
pageSnapping: true,
children: [
Container(color: Colors.red),
Container(color: Colors.blue),
Container(color: Colors.orange),
],
);
}
void _onScroll(double offset) {
if (!pageIsScrolling) {
pageIsScrolling = true;
_debounce = Timer(Duration(milliseconds: debounceDuration), () {
if (offset > 0) {
pageController
.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
)
.then((value) => pageIsScrolling = false);
} else {
pageController
.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
)
.then((value) => pageIsScrolling = false);
}
});
}
}
@override
void dispose() {
_debounce?.cancel();
super.dispose();
}
}
void main() {
runApp(MaterialApp(
home: Homepage(),
));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment