Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Positioned Widget Animation with Flutter

I have the following code in place to animate a Positioned widgets, position, when a pan ends. However, it doesn't animate.

Initialising AnimationController and variables:

  AnimationController nodesListAnimationController;
  Animation<double> nodesListAnimation;

  double nodesListOffsetY = 0.0; //widget default offset (will be large, but acts like 0.0)
  double nodesListDragOffsetY = 0.0; //how far we have moved the widget from default offset (nodesListOffsetY)
  double nodesListTouchOffset = 0.0; //the initial touch when drag begins
  final double nodesListHeight = 350.0; //widget height

@override
  void initState() {
    super.initState();

    nodesListAnimationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 350),
    )
    ..addListener((){
      setState(() {

      });
    });

  }

Build function; See the Positioned widget. I can drag this up and down within the bound I have created using the panning callbacks on GestureDetector.

When the pan ends, I want to animate the Positioned widget back to a given position. Currently, no animation occurs.

@override
  Widget build(BuildContext context) {
    nodesListOffsetY = MediaQuery.of(context).size.width + 110.0;

    AppBar appBar = AppBar(
      ...
    );

    return Scaffold(
        appBar: appBar,
        backgroundColor: Colors.black,
        body: Stack(
          children: <Widget>[
            Column(
              children: <Widget>[
                cameraWidget(),
                cameraControls(),
              ],
            ),
            Positioned(
              left: 0.0,
              top: nodesListAnimationController.isAnimating ? nodesListAnimation.value : nodesListOffsetY + nodesListDragOffsetY,
              width: MediaQuery.of(context).size.width,
              height: nodesListHeight,
              child: GestureDetector(
                onPanStart: (dragDetails) {
                  nodesListTouchOffset = (dragDetails.globalPosition.dy - appBar.preferredSize.height) - nodesListOffsetY - nodesListDragOffsetY;
                },
                onPanUpdate: (dragDetails) {
                  setState(() {
                    double newDragOffset = (dragDetails.globalPosition.dy - nodesListOffsetY) - appBar.preferredSize.height - nodesListTouchOffset;

                    //allow it only to move up if is clips off screen, otherwise upper limit not needed
                    double nodesListVisible = ((MediaQuery.of(context).size.height - appBar.preferredSize.height) - nodesListOffsetY);
                    bool isClipping = nodesListVisible < nodesListHeight ? true : false;
                    double upperLimit = 0.0;
                    if (isClipping) upperLimit = -(nodesListHeight - nodesListVisible);

                    //limit drag bounds. don't drag too high or low.
                    if (newDragOffset < upperLimit) newDragOffset = upperLimit;
                    else if (newDragOffset > 0) newDragOffset = 0.0;
                    else nodesListDragOffsetY = newDragOffset;
                  });
                },
                onPanEnd: (dragDetails) {
                  double currentPos = (nodesListOffsetY + nodesListDragOffsetY);
                  nodesListAnimation = Tween(begin: currentPos, end: nodesListOffsetY).animate(nodesListAnimationController);
                  nodesListAnimationController.forward(from: currentPos);
                  nodesListAnimation.addStatusListener((state) {
                    print(nodesListAnimation.value);
                  });
                },
                child: nodesList(),
              ),
            ),
          ],
        ));
  }
like image 334
Josh Kahane Avatar asked Nov 30 '25 19:11

Josh Kahane


1 Answers

Late to the party, but your issue is because of:

nodesListAnimationController.forward(from: currentPos)

Whose from is incorrectly accepting a value outside of 0.0-1.0. So you should probably change it to just controller.forward()

This isn’t well documented, but forward’s from is meant to accept values from 0.0-1.0, or more specifically, if set, then the range provided by lowerBound and upperBound (with few exceptions). (you can also think of it as 0-100%). It is not meant to accept the value range from the Tween’s begin and end, nor is it aware of that range.

https://api.flutter.dev/flutter/animation/AnimationController-class.html

By default, an AnimationController linearly produces values that range from 0.0 to 1.0

https://flutter.dev/docs/development/ui/animations/overview#addstatuslistener

An animation might then run forward (e.g., from 0.0 to 1.0)

(And I can verify that after I changed your code to use just .forward(), I was able to see it animate)

like image 132
TWL Avatar answered Dec 02 '25 12:12

TWL



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!