Migration to Flutter Guide
Discover our battle-tested 21-step framework for a smooth and successful migration to Flutter!
Home
Glossary

InkWell in Flutter

What is InkWell in Flutter?

InkWell is a Flutter Material widget that makes a rectangular area of its child respond to touch with an ink ripple and highlight, giving Material Design's signature tap feedback.

To render these ink effects, InkWell needs a Material ancestor. In a typical Flutter app, you usually don't need to add one manually: Scaffold, Card, and other Material widgets provide the required Material surface. A MaterialApp also sets up the Material app structure out of the box, so InkWell can commonly be used without explicitly adding a Material widget.

Key facts at a glance

  • InkWell in Flutter is part of the core Material library and requires no additional package.
  • It requires a Material ancestor – without one, Flutter triggers a debugCheckHasMaterial assertion in debug mode.
  • InkWell is built on top of InkResponse and configures it for a rectangular, contained ink area.
  • splashColor, highlightColor, hoverColor, and focusColor customize different parts of the interaction feedback.
  • overlayColor (WidgetStateProperty<Color?>) is the state-based alternative to these individual color properties. When it provides a value for a given state, that value takes precedence over the corresponding legacy color property, so combining both approaches can make the older properties appear not to work.
  • hoverColor only takes effect when the widget receives a hover interaction, such as from a mouse or other pointing device. It does not appear on a touchscreen that does not generate hover events. focusColor, on the other hand, applies when the InkWell has a keyboard or other focus.
  • An InkWell is enabled when at least one of its gesture callbacks is non-null. If all relevant gesture callbacks are null, the widget is disabled and does not respond to gestures or show their associated ink feedback.

How do I add an InkWell in Flutter?

Wrap any widget in InkWell and provide at least one gesture callback, such as onTap.

import 'package:flutter/material.dart';

class SettingsRow extends StatelessWidget {
  const SettingsRow({super.key, required this.onTap});

  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      child: InkWell(
        onTap: onTap,
        child: const Padding(
          padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
          child: Text('Notifications'),
        ),
      ),
    );
  }
}

Wrapping InkWell in Material(color: Colors.transparent) explicitly provides a Material ancestor for the ink effect, even when the surrounding widget tree does not already contain one.

In a typical Material app, you may not need to add Material explicitly if an appropriate Material ancestor already exists.

How do I change the ripple color of an InkWell in Flutter?

splashColor controls the expanding ripple; highlightColor controls the static overlay that appears while the finger is held down.

InkWell(
  onTap: () {},
  splashColor: Colors.amber.withValues(alpha: 0.3),
  highlightColor: Colors.amber.withValues(alpha: 0.1),
  borderRadius: BorderRadius.circular(8),
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('Tap me'),
  ),
),

borderRadius clips the ink effect to a rounded rectangle. Set it to match the shape of the surface containing the InkWell; otherwise, the ink effect may extend into corners that are visually rounded.

For state-based control, you can use overlayColor instead. It lets you specify colors for different interaction states, such as pressed, hovered, or focused:

InkWell(
  onTap: () {},
  overlayColor: WidgetStateProperty.resolveWith<Color?>(
    (states) {
      if (states.contains(WidgetState.pressed)) {
        return Colors.amber.withValues(alpha: 0.2);
      } if (states.contains(WidgetState.hovered)) {
        return Colors.amber.withValues(alpha: 0.1);
      }

      return null;
    },
  ),
  borderRadius: BorderRadius.circular(8),
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('Tap me'),
  ),
),

overlayColor is useful when you want to control interaction feedback based on widget state. When it provides a color for a given state, that value takes precedence over the corresponding individual color property, such as splashColor, highlightColor, hoverColor, or focusColor.

When should I use InkWell – and when not?

Use it when

  • You have a custom tappable area – such as a card, image, or custom list item – in a Material app and want the standard Material ink feedback.
  • You want Material tap feedback without wrapping plain content in a higher-level button widget.
  • You need a rectangular interaction area, optionally with a rounded shape controlled by borderRadius.
  • You want ink feedback alongside interactions such as tap, double tap, long press, hover, or focus.

Avoid it when

  • You need gestures that InkWell does not expose, such as dragging, panning, or scaling. Use GestureDetector instead.
  • You are building a Cupertino-style interface and want its native interaction patterns and visual feedback. InkWell is a Material widget and is generally not the appropriate choice there.
  • The interaction represents a standard button action. Prefer a semantic button widget such as TextButton, ElevatedButton, or OutlinedButton when appropriate, because these widgets provide built-in behavior for states such as disabled, focused, and hovered, as well as appropriate button semantics.

Best practices

  1. Always give InkWell a Material ancestor. If the surrounding widget tree doesn't already provide one, wrap it in Material(color: Colors.transparent).
  2. Match borderRadius to the shape of the interactive surface. An ink effect that extends into the corners of a rounded surface can look incorrect. For a non-rectangular shape, use customBorder instead of borderRadius.
  3. Use overlayColor when you need state-based control over interaction feedback. For simpler cases, splashColor, highlightColor, hoverColor, and focusColor can be used individually. The surface's actual background color belongs to the Material, Ink, Container, or other widget that paints it.
  4. Reach for InkResponse when you need more control over the ink interaction shape or behavior than InkWell provides, such as a non-rectangular or circular interaction area around an icon.
  5. To disable an InkWell, leave all of its gesture callbacks null. Use AbsorbPointer when you need to block pointer interaction for an entire subtree rather than simply disabling the InkWell.

Common mistakes

  • Forgetting the Material ancestor. In debug mode, this triggers a debugCheckHasMaterial assertion with a 'No Material widget found' message. This commonly happens when an InkWell is placed outside a widget tree that already provides a Material ancestor, such as one containing a Scaffold or Card.
  • Placing an opaque widget between Material and InkWell. Ink effects are painted on the Material surface, so an opaque Container, DecoratedBox, or Image between the Material and InkWell can hide the splash. When you need to paint a background underneath the ink effect, consider using an Ink widget instead of a plain Container.
  • Skipping borderRadius on a rounded interactive surface. InkWell uses a rectangular ink area by default, so an effect that isn't clipped to the surface's rounded shape can extend into its corners.
  • Mixing overlayColor with the individual color properties without understanding their precedence. overlayColor is state-based and takes precedence for states where it provides a value. As a result, setting splashColor or highlightColor may appear to have no effect when overlayColor is also configured.
  • Assuming InkWell controls the surface itself. InkWell provides interaction feedback; it doesn't paint the background of its child. Use Material, Ink, DecoratedBox, or another appropriate widget to control the visual surface.

Learn more

Design system Flutter

Building a Design System in a Large Flutter App

Flutter is known for its seamless UI/UX features and robust design elements. It allowed us to deliver a unique customer experience in the “CA24 Mobile” banking app. This article shares some of the learnings and pain points behind implementing it in this large project.

Advantages of design system

Benefits of a Design System: Why Your Digital Product Needs One

Building a new digital product, such as a web or mobile application, is complex. You need to map user stories - create a simple description of features - then apply them to the developers' scope of work and the app’s design. Discover how a design system can unleash efficiency, consistency, and scalability in your digital product.

Is Flutter good for app development?

Flutter Pros and Cons: Why Choose Flutter in 2025?

Flutter is loved by many for its simple and fast development of high-quality cross-platform apps. Let’s take a closer look if Flutter is a suitable solution in every case, i.e., when developing mobile, web, and desktop applications.