2

I need to animate a Draggable widget while it is in motion. The animation depends on the global position coordinates of the widget being dragged. I've used a Listener to get the finger tap position but the problem is that the feedback widget of the Draggable widget is not updating with setState. I found this, this, this and this link but none of them helped. Here's the code to demonstrate my problem.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double x = 0, y = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Listener(
        onPointerMove: (opm) {
          setState(() {
            x = opm.position.dx;
            y = opm.position.dy;
          });
          print('(${x.floor()}, ${y.floor()})');
        },
        child: Center(
          child: Draggable(
            childWhenDragging: Container(),
            child: Container(
              height: 150,
              width: 150,
              color: Colors.red,
            ),
            feedback: Material(
              child: Container(
                height: 150,
                width: 150,
                color: Colors.blue,
                child: Center(child: Text('(${x.floor()}, ${y.floor()})')),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

In this case, the coordinates displayed inside the blue screen should animate but even after doing a setState they don't update. I tried using AnimatedBuilder but there was no difference. It would be really helpful if someone could provide me with some help or an alternative method for achieving this with the Draggable widget.

Ashutosh Singh
  • 1,107
  • 17
  • 28
  • 1
    i tried `AnimatedBuilder` and it works just fine – pskink Sep 30 '20 at 06:41
  • Then maybe I made some mistakes trying to use it. Could you please share your version of the code? – Ashutosh Singh Sep 30 '20 at 06:43
  • 1
    `feedback: AnimatedBuilder(...)` - btw `ValueListenableBuilder` should work as well but i did not tried that – pskink Sep 30 '20 at 06:44
  • If your solution is working then could you please post it as an answer so that I can verify it and mark as the right solution? – Ashutosh Singh Sep 30 '20 at 06:47
  • 1
    it was a quick fix and i already deleted the code - simply make `feedback` as `AnimatedBuilder` that builds `Material > Container > Center > Text` – pskink Sep 30 '20 at 06:49

1 Answers1

-1

I don't understand this part fully but with pskink's help and with some documentations I was able to achieve this animation using the following code.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  double x = 0, y = 0;

  AnimationController _controller;

  Animation animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
    animation = Tween(begin: 0, end: 100).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Listener(
        onPointerMove: (opm) {
          setState(() {
            x = opm.position.dx;
            y = opm.position.dy;
            _controller.value = 0;
          });
          print('(${x.floor()}, ${y.floor()})');
        },
        child: Center(
          child: Draggable(
            childWhenDragging: Container(),
            child: Container(
              height: 150,
              width: 150,
              color: Colors.red,
            ),
            feedback: AnimatedBuilder(
              animation: animation,
              builder: (_, __) {
                return Material(
                  child: Container(
                    height: 150,
                    width: 150,
                    color: Colors.blue,
                    child: Center(child: Text('(${x.floor()}, ${y.floor()})')),
                  ),
                );
              },
            ),
          ),
        ),
      ),
    );
  }
}

Please comment if you have any better way of doing this or if you'd like to add some extra information or explanations.

Ashutosh Singh
  • 1,107
  • 17
  • 28
  • instead of `AnimationController` use `ValueNotifier` and then remove `setState` and simply use `notifier.value = opm.position` - also `ValueListenableBuilder` is preferable in that case – pskink Sep 30 '20 at 08:14
  • I couldn't try that part. Will try to give you an answer soon. – Ashutosh Singh Oct 08 '20 at 07:37