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.
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.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.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.
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.
borderRadius.InkWell does not expose, such as dragging, panning, or scaling. Use GestureDetector instead.InkWell is a Material widget and is generally not the appropriate choice there.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.Material ancestor. If the surrounding widget tree doesn't already provide one, wrap it in Material(color: Colors.transparent).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.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.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.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.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.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.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.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.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.
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.

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.

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.