'How to animate images on mouse hover using Flutter for Web?

I am a JavaScript developer and I am new to Flutter. I just want to animate a set of images on mouse hover like this using Flutter for Web. It includes Scaling, Opacity and Grayscale transformations. How to accomplish this in Flutter? Thanks in advance.



Solution 1:[1]

Other than the animation part of your question. The onHover argument of the InkWell only works if you specify the onTap argument first.

InkWell(
  child: SomeWidget(),
  onTap: () {
    //You can leave it empty, like that.
  }
  onHover: (isHovering) {
    if (isHovering) {
      //The mouse is hovering.
    } else {
      //The mouse is no longer hovering.
    }
  }
)

From the documentation, here's the benefit of the boolean, which is passed to the onHover callback:

The value passed to the callback is true if a pointer has entered this part of the material and false if a pointer has exited this part of the material.

Solution 2:[2]

This is just a demo to show that you can use onHover of Inkwell widget to accomplish the task. You will have to come up with the logic to decide how much offset and scale should be used and how to position the widget. In my example I have used a grid view. You can perhaps use a stack to set the currently active widget based on the hover.

Here is the example with a grid view. The live version of this is available in this dartpad.

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {  
  @override
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 3,      
      children: <Widget>[ImageHover(),ImageHover(),ImageHover(),ImageHover(),ImageHover(),ImageHover(),ImageHover(),],
    );
  }
}

class ImageHover extends StatefulWidget {
  @override
  _ImageHoverState createState() => _ImageHoverState();
}

class _ImageHoverState extends State<ImageHover> {
  double elevation = 4.0;
  double scale = 1.0;
  Offset translate = Offset(0,0);
  @override
  Widget build(context) {
    return InkWell(      
      onTap: (){},
      onHover: (value){
        print(value);
        if(value){
          setState((){
            elevation = 20.0;     
            scale = 2.0;
            translate = Offset(20,20);
          });
        }else{
          setState((){
            elevation = 4.0; 
            scale = 1.0;
            translate = Offset(0,0);
          });
        }
      },
      child: Transform.translate(
        offset: translate ,        
        child: Transform.scale(
          scale: scale,
          child: Material(        
            elevation: elevation,        
            child: Image.network(           
                'https://i.ytimg.com/vi/acm9dCI5_dc/maxresdefault.jpg',              
            ),
          ),
        ),
      ),
    );
  }
}

Solution 3:[3]

Just create an extension

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

extension HoverExtension on Widget {
  Widget get translateOnHover {
    return kIsWeb ? TranslateOnHover(child: this) : ThisContainer(child: this);
  }
}

class ThisContainer extends StatelessWidget {
  ThisContainer({this.child});
  final child;
  @override
  Widget build(BuildContext context) {
    return Container(child: child);
  }
}

class TranslateOnHover extends StatefulWidget {
  final Widget child;
  TranslateOnHover({required this.child});

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

class _TranslateOnHoverState extends State<TranslateOnHover> {
  double scale = 1.0;
  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (e) => _mouseEnter(true),
      onExit: (e) => _mouseEnter(false),
      child: TweenAnimationBuilder(
        duration: const Duration(milliseconds: 200),
        tween: Tween<double>(begin: 1.0, end: scale),
        builder: (BuildContext context, double value, _) {
          return Transform.scale(scale: value, child: widget.child);
        },
      ),
    );
  }

  void _mouseEnter(bool hover) {
    setState(() {
      if (hover)
        scale = 1.03;
      else
        scale = 1.0;
    });
  }
}

And use it anywhere by calling

yourWidget.translateOnHover

Solution 4:[4]

It's sad that there's not a built-in feature already, given that Flutter extended to web also, but luckily there is a package for this: hovering 1.0.4

You just need to install the package running this command flutter pub add hovering or adding hovering: ^1.0.4 to your dependencies. Then you can use HoverWidget,HoverContainer, HoverAnimatedContainer, and some more. It's not perfect, but it's an easy way to do it, specially for not complicated animations.

You can check the official docs of the package for more info: https://pub.dev/packages/hovering

Solution 5:[5]

You could use InkWell passing an onHover method alongside Transform Widget inside of a StatefulWidget. Down below you could see some code of mine. This is a widget that hovers depending on a bool parameter canHover

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tayan
Solution 2 Abhilash Chandran
Solution 3 Madhav Tripathi
Solution 4 parkorian
Solution 5 Evandro Junior