J. Sarkar
J. Sarkar

Reputation: 2643

InkWell not showing ripple effect

Tapping the container triggers the onTap() handler but does not show any ink splash effect.

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new InkWell(
          onTap: (){print("tapped");},
          child: new Container(
            width: 100.0,
            height: 100.0,
            color: Colors.orange,
          ),
        ),
      ),
    );
  }
}

I tried putting the InkWell inside the Container as well but in vain.

Upvotes: 254

Views: 234913

Answers (30)

Binh Tran
Binh Tran

Reputation: 11

I managed to do it by using Stack to overlap the Material widget containing InkWell and the primary Container together. I made it into a widget for ease of use:

import 'package:flutter/material.dart';

class InkWellContainer extends StatelessWidget {
  final Widget? child;
  final double? width;
  final double? height;
  final BoxDecoration? decorator;
  final VoidCallback? onTap;
  final VoidCallback? onLongPress;
  final VoidCallback? onDoubleTap;
  final Alignment? alignment;
  final EdgeInsets? padding;
  final Decoration? foregroundDecoration;
  final EdgeInsets? margin;
  final Matrix4? transform;
  final Alignment? transformAlignment;
  final Clip clipBehavior;
  final BoxConstraints? constraints;

  const InkWellContainer({
    super.key,
    this.child,
    this.width,
    this.height,
    this.decorator,
    this.onTap,
    this.onLongPress,
    this.onDoubleTap,
    this.alignment,
    this.padding,
    this.foregroundDecoration,
    this.margin,
    this.transform,
    this.transformAlignment,
    this.clipBehavior = Clip.none,
    this.constraints,
  });

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          width: width,
          height: height,
          padding: padding,
          alignment: alignment,
          decoration: decorator,
          foregroundDecoration: foregroundDecoration,
          margin: margin,
          transform: transform,
          transformAlignment: transformAlignment,
          clipBehavior: clipBehavior,
          constraints: constraints,
          child: child,
        ),
        Material(
          color: Colors.transparent,
          child: InkWell(
            onTap: onTap,
            onLongPress: onLongPress,
            onDoubleTap: onDoubleTap,
            child: SizedBox(
              width: width,
              height: height,
            ),
          ),
        ),
      ],
    );
  }
}

Upvotes: 1

Jagie
Jagie

Reputation: 2220

 Positioned(
        bottom: 4.r,
        right: 8.r,
        child: Material(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20.r),
          ),
          child: InkWell(
            borderRadius: BorderRadius.circular(20.r),
            onTap: () {},
            child: Ink(
                width: 40.r,
                height: 40.r,
                padding: EdgeInsets.zero,
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(20.r),
                ),
                child: Center(
                  child: Image.asset(
                    Assets.images.eventGrow.path,
                    width: 20.r,
                    height: 20.r,
                    color: AppColors.light().mainColor,
                  ),
                )),
          ),
        ),
      ),

Upvotes: 0

Muzammil
Muzammil

Reputation: 18

You can solve this problem by just using Card Widget on the top of InkWell Widget like this:

Card(               elevation: 0,
                    color: Colors.yellow,
                    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
                    child: InkWell(
                      splashColor: Colors.white54,
                      borderRadius: BorderRadius.circular(18),
                      onTap:(){},
                      child: Container(
                        width: size.width * 0.5,
                        height: 230,
                        color: Colors.transparent,

Upvotes: -1

Javad Vatan
Javad Vatan

Reputation: 83

I'm facing an issue where the ripple animation is not visible when using InkWell within a BottomSheet in my Flutter app. I've tried various solutions, but the ink effects seem to be going behind the card.

Here's a simplified version of the code:

    void showDialog(BuildContext context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return Container(
        height: 200.0,
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20.0),
            topRight: Radius.circular(20.0),
          ),
        ),
        child: SelectBox(),
      );
    },
  );
}

class SelectBox extends StatelessWidget {
  const SelectBox({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      splashColor: Colors.blue,
      onTap: () {
        print("Tapped");
      },
      child: Container(
        color: Colors.transparent,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            const Text('Label 1'),
            const Text('Label 2'),
          ],
        ),
      ),
    );
  }
}

After trying different approaches, the solution that worked was to place the Ink widget as the root in the BottomSheet. Here's the updated code:

    void showDialog(BuildContext context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return Ink(
        height: 200.0,
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20.0),
            topRight: Radius.circular(20.0),
          ),
        ),
        child: Column(
          children: [
            // Other widgets can be added here
            SelectBox(),
            // Additional widgets
          ],
        ),
      );
    },
  );
}

class SelectBox extends StatelessWidget {
  const SelectBox({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      splashColor: Colors.blue,
      onTap: () {
        print("Tapped");
      },
      child: Container(
        color: Colors.transparent,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            const Text('Label 1'),
            const Text('Label 2'),
          ],
        ),
      ),
    );
  }
}

This adjustment ensures that the ink effects are rendered correctly on top of other content within the BottomSheet. Please let me know if you have any insights or suggestions on how to improve this solution.

Upvotes: 1

AmirahmadAdibi
AmirahmadAdibi

Reputation: 586

2023 update : very simple you have to have

Container -> Material -> InkWell in this order.

              Container(
                    width: 114,
                    height: 64,
                    decoration: const BoxDecoration(
                      color: AppColorsLight.surfacePrimary,
                      borderRadius: BorderRadius.all(Radius.circular(8)),
                    ),
                    child: Material(
                      color: Colors.transparent,
                      borderRadius: BorderRadius.all(Radius.circular(8)),
                      child: InkWell(
                        splashColor: Colors.white,
                        borderRadius: 
                        BorderRadius.all(Radius.circular(8)),

Upvotes: 25

Nomi
Nomi

Reputation: 760

Here is a custom widget if any of the previous solutions are not working for you

import 'package:flutter/material.dart';

class BaseInkWell extends StatelessWidget {
  final Widget child;
  final Function()? onTap;
  const BaseInkWell({Key? key, required this.child, this.onTap})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        child,
        Positioned(
          top: 0,
          left: 0,
          child: Material(
            color: Colors.transparent,
            child: InkWell(
              onTap: onTap,
              child: SizedBox(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

to use it

BaseInkWell(
 child: Container(),
 onTap:()=>print('works')
); 

Upvotes: 1

Manoj Reddy
Manoj Reddy

Reputation: 127

Instead of providing the color for the Container widget, provide color for the Material widget.

Material(
      color: Colors.orange,
      child: InkWell(
        onTap: () {
          print("tapped");
        },
        child:  Container(
          width: 100.0,
          height: 100.0,
        ),
      ),
    )

Upvotes: 2

JAg&#252;ero
JAg&#252;ero

Reputation: 677

Wrap the InkWell with Material and use there the color that you need:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: Material(
          color: Colors.orange,
          child: new InkWell(
            onTap: (){ print("tapped"); },
            child: new Container(
              width: 100.0,
              height: 100.0,
            ),
          ),
        ),
      ),
    );
  }
}

Upvotes: 6

Devendra
Devendra

Reputation: 3444

Make sure you have not added the below properties in the material theme:

(In my case this was the issue)

splashColor: Colors.transparent
splashFactory: NoSplash.splashFactory

eg:-

MaterialApp(
  home:......,
  theme: ThemeData(
    splashFactory: NoSplash.splashFactory, // Remove this
    splashColor: AppColors.transparent, // Remove this OR Change the color
    ......         
  ),
)

Upvotes: 2

sourav pandit
sourav pandit

Reputation: 9115

You might have set the properties in theme or parent of the material

ThemeData(
 ...
 splashFactory: NoSplash.splashFactory,
 ...
)

if you don't want splash effect for specific use it explicitly

 InkWell(
          splashFactory: NoSplash.splashFactory,
          ...
          child: Row(
          ...
          )
        );

Upvotes: 2

milan pithadia
milan pithadia

Reputation: 870

Single Line:

Use splashColor widget in an InkWell.

InkWell(
  onTap: () {}, // Handle your onTap 
  splashColor: Colors.grey,
  child: Container(
    width: 200,
    height: 200,
  ),
)

Upvotes: 2

Konstantin Kozirev
Konstantin Kozirev

Reputation: 1262

Quick solution in easy words:

This solution works in any place of the widget tree. Just add additional Material before InkWell, like so:

return Container(
  .......code.......
  ),
  child: Material(
    type: MaterialType.transparency,
    child: InkWell(
      onTap: () {},
      child: Container(
        .......code.......
      ),
    ),
  ),
);

Reference: https://api.flutter.dev/flutter/material/InkWell-class.html Section called "The ink splashes aren't visible!"

Upvotes: 7

user1462442
user1462442

Reputation: 8202

I think adding color to the container is covering over the ink effect

https://api.flutter.dev/flutter/material/InkWell/InkWell.html

This code seems to work

  body: new Center(
    child: new Container(
      child: new Material(
        child: new InkWell(
          onTap: (){print("tapped");},
          child: new Container(
            width: 100.0,
            height: 100.0,
          ),
        ),
        color: Colors.transparent,
      ),
      color: Colors.orange,
    ),
  ),

just click the middle square.

Edit: I found the bug report. https://github.com/flutter/flutter/issues/3782

This is actually as expected, though we should update the docs to make it clearer.

What's going on is that the Material spec says that the splashes are actually ink on the Material. So when we splash, what we do is we literally have the Material widget do the splash. If you have something on top of the Material, we splash under it, and you can't see it.

I have wanted to add a "MaterialImage" widget that conceptually prints its image into the Material as well so that then the splashes would be over the image. We could have a MaterialDecoration which does something similar for a Decoration. Or we could have Material itself take a decoration. Right now it takes a color, but we could extend that to taking a whole decoration. It's not clear whether it's really material-spec-compatible to have a material with a gradient, though, so I'm not sure whether we should do that.

In the short run, if you just need a workaround, you can put a Material on top of the container, with the material set to use the "transparency" type, and then put the ink well inside that.

--hixie

Update: Hixie merged a new Ink solution last year. The Ink provides a convenient way to splash over images.

  testWidgets('Does the Ink widget render anything', (WidgetTester tester) async {
    await tester.pumpWidget(
      new Material(
        child: new Center(
          child: new Ink(
            color: Colors.blue,
            width: 200.0,
            height: 200.0,
            child: new InkWell(
              splashColor: Colors.green,
              onTap: () { },
            ),
          ),
        ),
      ),
    );


Material(
  color: Colors.grey[800],
  child: Center(
    child: Ink.image(
      image: AssetImage('cat.jpeg'),
      fit: BoxFit.cover,
      width: 300.0,
      height: 200.0,
      child: InkWell(
        onTap: () { /* ... */ },
        child: Align(
          alignment: Alignment.topLeft,
          child: Padding(
            padding: const EdgeInsets.all(10.0),
            child: Text('KITTEN', style: TextStyle(fontWeight: FontWeight.w900, color: Colors.white)),
          ),
        )
      ),
    ),
  ),
)

Please Note: I did not test the new Ink Widget. I coped the code from ink_paint_test.dart and the Ink class docs

https://github.com/Hixie/flutter/blob/1f6531984984f52328e66c0cd500a8d517964564/packages/flutter/test/material/ink_paint_test.dart

https://github.com/flutter/flutter/pull/13900

https://api.flutter.dev/flutter/material/Ink-class.html

Upvotes: 390

CopsOnRoad
CopsOnRoad

Reputation: 267604

Screenshot:

enter image description here


Use Ink widget wrapped in an InkWell.

InkWell(
  onTap: () {}, // Handle your onTap 
  child: Ink(
    width: 200,
    height: 200,
    color: Colors.blue,
  ),
)

Upvotes: 241

adel parsa
adel parsa

Reputation: 446

For handling ripple effect you should use the below rules:

  1. onTap shouldn't be null (or empty).
  2. InkWell must be inside any of the Material Widgets

Upvotes: 1

Lutfi Fahmani
Lutfi Fahmani

Reputation: 35

child: Material(
color: Colors.transparent
child: InkWell(
  onTap: (){ print("this Ripple!";},
  splashColor: Colors.greenAccent,
  child: Container(
    height:100.0,
    color: Colors.white,
  ),
),
),

Add Material before InkWell and set color to Colors.transparent.

Upvotes: 2

Suragch
Suragch

Reputation: 511846

I was able to make my situation work by using a Stack.

Stack(
  children: [
    MyCustomWidget(), //              <--- Put this on bottom
    Material(
      color: Colors.transparent,
      child: InkWell(
        onTap: () {},
        child: Ink(
          width: 100,
          height: 100,
        ),
      ),
    ),
  ],
),

The reason that the other answers on this page weren't working for me is that my custom widget hid the ink effect and I didn't have a plain image (so I couldn't use Ink.image).

Edit:

You still may be able to use Ink.image if you convert the image to the right format.

Upvotes: 2

Renan Zumas
Renan Zumas

Reputation: 11

now using MaterialButton, in newer version flutter


Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16.0),
      child: MaterialButton(
        child: Text(
          labelText,
          style: TextStyle(fontSize: 22),
        ),
        onPressed: () {},
        color: backgroundColor,
        height: 45,
        minWidth: double.infinity,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(
            Radius.circular(16),
          ),
        ),
      ),
    );
  }

Upvotes: 1

Mohammed_7aafar
Mohammed_7aafar

Reputation: 475

If you want a Ripple Effect on any widget just:

1- Wrap widget with Material and Inkwell.

2- set color to widget from Material.

3- Never set color to the widget you want it to ripple.

   Material (
   color: const Color(0xcffFF8906),
   child: InkWell(
   ontap:() { },
   child: Container(
   color: Colors.transparent,
   height: 80,
   width: 80,
 )

Upvotes: 12

goofy
goofy

Reputation: 445

For me it was another problem. InkWell was not showing ripple effect when i had onTap function as parameter of my widget defined as below.

Function(Result) onTapItem;
...
onTap: onTapItem(result),

I don't know what's the difference, but next code is working well.

onTap: (){ onTapItem(result); },

Upvotes: 0

Jaswant Singh
Jaswant Singh

Reputation: 10749

InkWell() will never show the ripple effect until you add the

onTap : () {} 

or any of the callbacks like onDoubleTap, onLongPress etc.

parameter inside the InkWell as it starts listening to your taps only when you specify this parameter.

Upvotes: 41

Gene Bo
Gene Bo

Reputation: 12073

This is working for me:

Material(
    color: Colors.white.withOpacity(0.0),
    child: InkWell(
      splashColor: Colors.orange,
      child: Text('Hello'), // actually here it's a Container wrapping an image
      onTap: () {
        print('Click');
      },
    ));

After trying many answers here, it was a combination of:

  1. Setting splashColor
  2. Wrapping InkWell in Material(color: Colors.white.withOpacity(0.0), ..)

Thanks to the answers here that make those 2 points

Upvotes: 3

Aruna Warnasooriya
Aruna Warnasooriya

Reputation: 339

After you added onTap:(){} listener, ripple effect should work fine. It doesn't work if you use BoxShadow() with in the InkWell() widget.

Upvotes: 1

thisisyusub
thisisyusub

Reputation: 707

I have found this solution for me. I think it can help you:

Material(
      color: Theme.of(context).primaryColor,
      child: InkWell(
        splashColor: Theme.of(context).primaryColorLight,
        child: Container(
          height: 100,
        ),
        onTap: () {},
      ),
    )

Color is given to Material widget. It is the default color of your widget. You can adjust color of ripple effect using splashColor property of Inkwell.

Upvotes: 23

Edeson Bizerril
Edeson Bizerril

Reputation: 1715

The solution to circular widgets, do like below:

Material(
    color: Colors.transparent,
    child: Container(
      alignment: Alignment.center,
      height: 100,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          IconButton(
              enableFeedback: true,
              iconSize: 40,
              icon: Icon(
                Icons.skip_previous,
                color: Colors.white,
                size: 40,
              ),
              onPressed: () {}),
          IconButton(
            iconSize: 100,
            enableFeedback: true,
            splashColor: Colors.grey,
            icon: Icon(Icons.play_circle_filled, color: Colors.white, size: 100),
            padding: EdgeInsets.all(0),
            onPressed: () {},
          ),
          IconButton(
              enableFeedback: true,
              iconSize: 40,
              icon: Icon(
                Icons.skip_next,
                color: Colors.white,
                size: 40,
              ),
              onPressed: () {}),
        ],
      ),
    ),
  )

Upvotes: 0

Lee McLoughlin
Lee McLoughlin

Reputation: 9

I was hit by a similar problem adding an Inkwell to an existing complex Widget with a Container wrapping a BoxDecoration with a color. By adding the Material and Inkwell in the way suggested the Inkwell was still obscured by the BoxDecoration so I just made the BoxDecoration's color slightly opaque which allowed the Inkwell to be seen

Upvotes: 0

ibhavikmakwana
ibhavikmakwana

Reputation: 10101

A better way is to use the Ink widget instead of any other widget.

Instead of defining color inside container you can define it in Ink widget itself.

Below code will work.

Ink(
  color: Colors.orange,
  child: InkWell(
    child: Container(
      width: 100,
      height: 100,
    ),
    onTap: () {},
  ),
)

Do not forget to add a onTap: () {} in the InkWell else it will not show the ripple effect too.

Upvotes: 15

Sonu Saini
Sonu Saini

Reputation: 2054

  1. The InkWell widget must have a Material widget as an ancestor otherwise it can't show effects. E.g.:

    Material(
      child : InkWell(
        child : .....
    
  2. you have to add onTap method to see the actual effects as like

     Buttons {RaisedButton,FlatButton etc}.
     e.g -> Material(
                 child : InkWell(
                         onTap : (){}
                         child : .....
    

Let's come to the main points see some examples below and try to understand the actual concepts of InkWell.

  • In Below example Material is parent of InkWell with onTap but it still not working. Please try to understand the concept of it. You should provide some margin to container or other widget to show the effects. Actually below code working fine but we can't see because we did not provide any margin or align so it has no space to show the effects.

    Widget build(BuildContext context) {
      return Center(
        child: Material(
          child: new InkWell(
            onTap: () {
              print("tapped");
            },
            child: new Container(
              width: 100.0,
              height: 100.0,
              color: Colors.orange,
            ),  
          ),
        ),
      );
    }
    
  • Below example show InkWell effects only to upwards because we provide space {margin}.

    Widget build(BuildContext context) {
      return Center(
        child: Material(
          child: new InkWell(
            onTap: () {
              print("tapped");
            },
            child: new Container(
              margin: EdgeInsets.only(top: 100.0),
              width: 100.0,
              height: 100.0,
              color: Colors.orange,
            ),
          ),
        ),
      );
    }
    
  • Below exp. show the effects in all the page because center create margin from all the side. Centre align it's child widget from top, left, right and bottom.

    Widget build(BuildContext context) {
      return Center(
        child: Material(
          child: new InkWell(
            onTap: () {
              print("tapped");
            },
            child: Center(
              child: new Container(
                width: 100.0,
                height: 100.0,
                color: Colors.orange,
              ),
            ),
          ),
        ),
      );
     }
    

Upvotes: 81

S.a. Sadik
S.a. Sadik

Reputation: 64

This is the best way I found and use it always. You can try it.

  • Wrap your Widget with InkWell
  • Wrap InkWell with Material
  • Set the opacity 0% anyhow. e.g.: color: Colors.white.withOpacity(0.0),

    Material(
      color: Colors.white.withOpacity(0.0),
      child: InkWell(
        child: Container(width: 100, height: 100),
        onTap: (){print("Wow! Ripple");},
      ),
    )
    

Upvotes: 3

user3483238
user3483238

Reputation: 21

I ran into this same problem trying to create an alternating color of InkWell's in a ListView. In that particular case, there's a simple solution: wrap the alternates in a Container that uses a mostly transparent tint/brightness change -- the InkWell touch animation will still be visible beneath it. No Material needed. Note there are other issues when trying to work around this with a Materal -- e.g., it will override a DefaultTextStyle you're using with the default (it installs an AnimatedDefaultTextStyle) which is a huge pain.

Upvotes: 2

Related Questions