snap_scroll_physics icon indicating copy to clipboard operation
snap_scroll_physics copied to clipboard

CustomScrollBehavior can break snap behaviour

Open JesperBllnbm opened this issue 2 years ago • 0 comments

Hi there,

Thanks a lot for this cool package, really useful!

I found a really weird edge case where the ScrollPhysics was not behaving as expected. When I wrap a scrollable with CustomScrollBehaviour, the snapping freezes when I reach the end of the scrollable. This may only happen on web builds, I haven't tested that. I think this little video explains it much better than I could with words:

https://user-images.githubusercontent.com/31738080/217820777-7044418d-686d-4792-a9af-8307db3cbbfb.mov

If you want to test it yourself, here is the code:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:snap_scroll_physics/snap_scroll_physics.dart';

void main() {
  runApp(MyApp());
}

class CustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final items = List.generate(20, (index) => index);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: ScrollConfiguration(
        behavior: CustomScrollBehavior(),
        child: Material(
          child: ListView.builder(
            physics: SnapScrollPhysics(
              snaps: List.generate(
                items.length,
                (index) => Snap(index * 120, distance: 60),
              ),
            ),
            itemCount: items.length,
            itemBuilder: (context, index) {
              return SizedBox(
                height: 120,
                width: double.infinity,
                child: ElevatedButton(
                  onPressed: () => print('pressed'),
                  child: Text(items[index].toString()),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

I could also "fix" it by adding the following changes:

...

  @override
  Simulation? createBallisticSimulation(ScrollMetrics position,
      double velocity) {
    final simulation = super.createBallisticSimulation(position, velocity);
    final proposedPixels = simulation?.x(double.infinity) ?? position.pixels;
    final double targetUnbound =
    _getTargetPixels(position, proposedPixels, this.tolerance, velocity);
    final double target = targetUnbound.clamp(
        position.minScrollExtent, position.maxScrollExtent);
    if ((target - proposedPixels).abs() > precisionErrorTolerance) {
      // if (simulation is BouncingScrollSimulation) {
      //   return BouncingScrollSimulation(
      //     leadingExtent: math.min(target, position.pixels),
      //     trailingExtent: math.max(target, position.pixels),
      //     velocity: velocity,
      //     position: position.pixels,
      //     spring: spring,
      //     tolerance: tolerance,
      //   );
      // }
return ScrollSpringSimulation(
        spring,
        position.pixels,
        target,
        velocity,
        tolerance: tolerance,
      );

...

Cheers!

JesperBllnbm avatar Feb 09 '23 13:02 JesperBllnbm