2

I have a container upon which I'm detecting gestures so that I can scroll through an image that I'm creating with custom painter, like so

class CustomScroller extends StatefulWidget {
  @override
  _CustomScrollerState createState() => _CustomScrollerState();
}

class _CustomScrollerState extends State<CustomScroller>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  double dx = 0;
  Offset velocity = Offset(0, 0);
  double currentLocation = 0;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    super.initState();
  }

  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    double width = MediaQuery.of(context).size.width;
    return GestureDetector(
      onHorizontalDragUpdate: (DragUpdateDetails dragUpdate) {
        dx = dragUpdate.delta.dx;
        currentLocation =
            currentLocation + (dx * 10000) / (width * _absoluteRatio);
        setState(() {});
      },
      onHorizontalDragEnd: (DragEndDetails dragUpdate) {
        velocity = dragUpdate.velocity.pixelsPerSecond;
        //_runAnimation(velocity, size);
      },
      child: Container(
        width: width,
        height: 50,
        child: Stack(
          children: <Widget>[
            CustomPaint(
                painter: NewScrollerPainter(1, true, currentLocation),
                size: Size.fromWidth(width)),

          ],
        ),
      ),
    );
  }
}

I've written some code so that on a drag the pointer's dx value is used to calculate currentLocation, which is used by the customPainter 'NewScrollerPaint' to determine what area it needs to paint. I essentially want a fling animation, whereby releasing from a drag with a velocity, the custom painter 'NewScrollerPainter' will continue to scroll until it is out of momentum. I believe this requires the value of currentLocation to be calculated from the velocity, and then returned for NewScrollerPainter to be updated, but I've spent a few hours looking through Flutter's animation docs and playing around with open source code but I'm still not sure how I would go about doing this. Could anyone shed some light onto this issue?

exitpotato
  • 21
  • 4

1 Answers1

1
class CustomScroller extends StatefulWidget {
  @override
  _CustomScrollerState createState() => _CustomScrollerState();
}

class _CustomScrollerState extends State<CustomScroller>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    super.initState();
  }

  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    return GestureDetector(
      onHorizontalDragUpdate: (DragUpdateDetails dragUpdate) {
        _controller.value += dragUpdate.primaryDelta / width;
      },
      onHorizontalDragEnd: (DragEndDetails dragEnd) {
        final double flingVelocity = dragEnd.primaryVelocity / width;
        const spring = SpringDescription(
          mass: 30,
          stiffness: 1,
          damping: 1,
        );
        final simulation = SpringSimulation(spring, _controller.value, 0, flingVelocity); //if you want to use a spring simulation returning to the origin
        final friction = FrictionSimulation(1.5, _controller.value, -(flingVelocity.abs())); //if you want to use a friction simulation returning to the origin
        //_controller.animateWith(friction);
        //_controller.animateWith(simulation);
        _controller.fling(velocity: -(flingVelocity.abs())); //a regular fling based on the velocity you were dragging your widget
      },
      child: Stack(
          children: [
            SlideTransition(
              position: Tween<Offset>(begin: Offset.zero, end: const Offset(1, 0))
          .animate(_controller),
              child: Align(
                alignment: Alignment.centerLeft,
                child: CustomPaint(
                  painter: NewScrollerPainter(),
                  size: Size.fromWidth(width),
                  child: SizedBox(width: width, height: 50)
                ),
              )
            )
          ]
        )
    );
  }
}

I see you are trying to move in the X axis only (you're usign onHorizontalDragUpdate and End) so you can use the values primaryDelta and primaryVelocity, that already gives you the calculations of the position and velocity in the primary axis (horizontal in this case).

In onHorizontalDragUpdate you mve the controller value adding itself the position of the dragging (positive you're moving away of the place you first tapped, negative you're moving back, 0 you haven't moved or dragged).

In onHorizontalDragEnd you calculate the velocity with primaryVelocity and the same logic (positive you're moving away, negative you're returning, 0 there is no velocity, bascially you stopped moving before your drag ended). You can use simulations or just the fling method and pass the velocity you have (positive you want to end the animation, negative you velocity you want to dismiss it and 0 you don't want to move).

At the end the only thing you need to do is wrap your widget you cant to move with the controller in a SlideTransition with a Tween animated by the controller beggining at zero and end where you want to end it (in my case I want to move it from left to right)

EdwynZN
  • 4,895
  • 2
  • 12
  • 15
  • This isn't quite the effect I'm looking for, but I've since figured it out. I've reformulated my question here: https://stackoverflow.com/questions/62713997/animating-custompainter-with-repaint-whilst-also-retrieving-controller-value-in and I noticed you answered a similar question before, so I was hoping you might be able to help me on this question as well. Thanks – exitpotato Jul 03 '20 at 10:59