mrcendre / motion

A Flutter plugin to apply a gyroscope-based motion effect to widgets.
GNU General Public License v3.0
39 stars 15 forks source link

Hovering over one widget in a Row does some things with the other as well #14

Closed markfili closed 1 year ago

markfili commented 1 year ago

Hi there!

The resulting behaviour of this plugin looks promising on the pub.dev page, but when I add it to two widgets in a Row, there's something funny happening: both widgets are triggered and move the same way even though only one is being hovered

https://user-images.githubusercontent.com/3738182/226437855-9b19f0ad-b2e8-4d35-ac01-aa7639495dbc.mov

This here is the setup to reproduce this behaviour (the Key property was added in an attempt to mitigate the problem):

// main widget's build method:
Row(
  children: [
    Spacer(),
    Expanded(
      child: Hovering(text: "xxxxxxxxx"),
    ),
    SizedBox(width: 16,),
    Expanded(
      child: Hovering(text: "yyyyyyyy"),
    ),
    Spacer(),
  ],
),
// hovering widget mentioned above
class Hovering extends StatelessWidget {
  final String text;

  const Hovering({Key? key, required this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Motion(key: Key(text),
      child: Text("TEST ${text.hashCode}"),
    );
  }
}

I was just exploring for "onHover" packages, came across yours and thought I'd report this, maybe you need to take a closer look at it.

Thank you, cheers!

mrcendre commented 1 year ago

Hello @markfili, thank you so much for your interest in Motion !

When omitting the MotionController in the Motion widget constructor, the shared MotionController.defaultController will be used. Therefore, all widgets will reflect inputs on any of them since they use the same controller.

This behavior is intentional — it allows to optimize the gyroscope subscriptions by using the same controller by default.

In your case, the solution is to customize it by making your Hovering widget a StatefulWidget, whose state manages its own MotionController, like so :

class Hovering extends StatelessWidget {
  final String text;

  const Hovering({Key? key, required this.text}) : super(key: key);

  @override
  State<Hovering> createState() => _HoveringState();
}

class _HoveringState extends State<Hovering> {
  late MotionController controller;

  @override
  void initState() {
    super.initState();
    controller = MotionController();
  }

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

  @override
  Widget build(BuildContext context) => Motion(key: Key(widget.text),
                                                                                  controller: controller,
                                                                                  child: Text("TEST ${widget.text.hashCode}"));
  }
}

Let me know if it solves your issue ! I hope you'll enjoy using Motion 👍

markfili commented 1 year ago

Hi, @mrcendre! Thank you for your explanation, it makes sense. I will use Motion in future apps wherever I can and report back if something else comes up. Closing this issue now, keep up the great work!