Ripple effect not working with when wrapping a Card in InkWell

Viewed 1626

After doing a bit of research, I tried to wrap ListTiles with InkWell to get the onTap ripple effect, but it doesn't seem to work. This is what I have:

return AnimationLimiter(
      child: Scrollbar(
        child: ListView.builder(
          shrinkWrap: true,
          itemCount: widget.myItems.length,
          itemBuilder: (context, index) => AnimationConfiguration.staggeredList(
            position: index,
            duration: const Duration(milliseconds: 300),
            child: SlideAnimation(
              verticalOffset: 50.0,
              child: FadeInAnimation(
                child: Material(
                  /// child: Ink( also tried with Ink and no Ink and no Material just InkWell but no ripple
                  child: InkWell(
                    onTap: () => widget.nav(widget.myItems[index]),
                    splashColor: Colors.red,
                    child: Card(   
                      child: _itemTile(index),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );

The ListTile:

  return ListTile(
      dense: true,
      trailing: Icon(Icons.keyboard_arrow_right),
      leading: category,
      title: Text(
        item.title,
        style: TextStyle(
          color: Colors.black,
          fontWeight: FontWeight.bold,
        ),
      ),
    );

I managed to get the ripple effect working by doing onTap: () => {} to the InkWell. But after adding the GestureDetector, the Ripple is gone again.

             child: InkWell(
                borderRadius: BorderRadius.circular(8),
                onTap: () {},
                splashColor: Colors.red,
                child: GestureDetector(
                  onTap: () => widget.nav(widget.myItems[index]),
                  child: _itemTile(index),
                ),
              ),

I even tried this, ripple works, but it the widget.nav function doesn't get called:

Stack(
              children: [
                Card(
                  elevation: 1,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                  margin:
                      EdgeInsets.only(bottom: 5.0, left: 2.0, right: 2.0),
                  child: GestureDetector(
                    onTap: () {
                      widget.nav(widget.myItems[index]);
                    },
                    child: _itemTile(index),
                  ),
                ),
                Positioned.fill(
                  child: Material(
                    color: Colors.transparent,
                    child: InkWell(
                      onTap: () => {},
                    ),
                  ),
                )
              ],
            ),

Edit* Adding the nav function:

_navToItem(item) {
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => Item(
        items: _activityItems.length > 0 ? _activityItems : _items,
        showAd: false,
        user: userStream,
        item: item,
        favorites: _favorites,
      ),
    ),
  );
}
5 Answers

ListTile has a ripple effect by default, so you shouldn't need to add an inkwell. If there isn't a ripple, one of the children is probably causing some issues. Try removing some and see if that helps.

Example

You're correct in using the Stack.

It's necessary due to the way the widget tree is rendered from bottom up, layering each widget on top of each other (which blocks the visual effect of splash if there are children below it.)

The GestureDetector you have in your Stack example won't ever get the tap gesture, cause InkWell below it is taking the event.

Something like this work for you? (Move your widget.nav call to the InkWell onTap:.)

Stack(
          children: [
            Card(
              elevation: 1,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8),
              ),
              margin:
              EdgeInsets.only(bottom: 5.0, left: 2.0, right: 2.0),
                // GESTUREDETECTOR Removed ****************
                child: Text('itemTile goes here'),
              ),
            Positioned.fill(
              child: Material(
                color: Colors.transparent,
                child: InkWell(
                  onTap: () => print('InkWell tapped! Put widget.nav here!'),
                ),
              ),
            )
          ],
        )

Please try to understand the concept of it. You should provide some margin to the widget to show the effects. You didn't provide any margin or align so it has no space to show the effects.

Below exp. show the effects in all the page because center creates margin from all the side. The Centre aligns its child widget from the top, left, right and bottom.

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

In your case:

return AnimationLimiter(
      child: Scrollbar(
        child: ListView.builder(
          shrinkWrap: true,
          itemCount: widget.myItems.length,
          itemBuilder: (context, index) => AnimationConfiguration.staggeredList(
            position: index,
            duration: const Duration(milliseconds: 300),
            child: SlideAnimation(
              verticalOffset: 50.0,
              child: FadeInAnimation(
                child: Material(
                  /// child: Ink( also tried with Ink and no Ink and no Material just InkWell but no ripple
                  child: InkWell(
                    onTap: () => widget.nav(widget.myItems[index]),
                    splashColor: Colors.red,
                    child: Center(
                             child: Card(   
                                   child: _itemTile(index),
                        ),
                     ),
                ),
                ),
              ),
            ),
          ),
        ),
      ),
    );

For ListTile case try passing custom ripple color. May be your background and your ripple effect color is same.

There is no need to wrap ListTile with InkWell because it already does it under the hood. We could do this with a Material widget to preserve the ripple effect and to act like a Card widget:

Material(
  color: Colors.white,
  borderRadius: BorderRadius.circular(4),
  elevation: 1,
  child: ListTile(
    shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(4)),
    leading: Text('Hello World'),
    onTap: () => print('Pressed ListTile'),
  ),
),

Output:

Output

Please change your code

Card(child: InkWell(child: ListTile(), onTab: () {},),),

First of all Card widget, its child Inkwell and then ListTile.

Related