DEV Community

Guilherme Bahia
Guilherme Bahia

Posted on

Creating a Flexible Custom Icon Button in Flutter

Hello Flutter Community! Today, I want to share with you a custom component I recently created: the CustomIconButton. This widget offers a more flexible and stylizable alternative to Flutter's traditional IconButton.

The Problem with IconButton:

While working on a Flutter project, I encountered a challenge with the default IconButton widget. Its padding or margin were not quite fitting into my layout the way I wanted. The default padding and the fixed size were creating a larger-than-desired tap target and taking up too much space.

I initially experimented with the ButtonStyle property of the standard IconButton. My goal was to reduce the tap target size using MaterialTapTargetSize.shrinkWrap and minimizing the padding as follows:

style: ButtonStyle(
        tapTargetSize: MaterialTapTargetSize.shrinkWrap,
        padding: MaterialStateProperty.all<EdgeInsets>(
          const EdgeInsets.symmetric(horizontal: 0),
        ),
      ),
Enter fullscreen mode Exit fullscreen mode

However, I soon realized that this approach compromised the user experience. The tap target size became too small, making it difficult for users to accurately press the button.

Introducing CustomIconButton:

To overcome this, I created the CustomIconButton, a customizable widget that allows for greater control over icon, background, border colors, and size. It's perfect for those situations where you need a button that perfectly fits your UI's aesthetic.

Key Features:

Customizable Icon and Colors: You can specify the icon, icon color, background color, and border color.
Flexible Sizing: The size of the button and the icon's size relative to the button can be adjusted.
Material Design Compliant: It uses Material, InkWell, and Ink widgets to ensure compliance with material design standards while allowing for more design flexibility.
Code Breakdown:

import 'package:flutter/material.dart';

class CustomIconButton extends StatelessWidget {
  final IconData icon;
  final Color? iconColor;
  final Color? backgroundColor;
  final Color? borderColor;
  final double? size;
  final double? sizeInRelation;
  final double? padding;
  final VoidCallback onPressed;

  const CustomIconButton({
    super.key,
    required this.icon,
    this.iconColor,
    this.backgroundColor,
    this.borderColor,
    this.size,
    this.sizeInRelation,
    this.padding,
    required this.onPressed,
  });

  Color _getIconColor(ColorScheme colorScheme) {
    if (iconColor == null) {
      return colorScheme.background;
    }
    return iconColor!;
  }

  Color _getBackgroundColor(ColorScheme colorScheme) {
    if (backgroundColor == null) {
      return colorScheme.primary;
    }
    return backgroundColor!;
  }

  Color _getBorderColor(ColorScheme colorScheme) {
    if (borderColor == null) {
      return colorScheme.secondary;
    }
    return borderColor!;
  }

  double _getSize() {
    if (size == null) {
      return 26;
    }
    return size!;
  }

  double getPadding() {
    if (padding == null) {
      return 2;
    }
    return padding!;
  }

  double getSizeInRelation() {
    if (sizeInRelation == null) {
      return 0.6;
    }
    return sizeInRelation!;
  }

  @override
  Widget build(BuildContext context) {
    var colorScheme = Theme.of(context).colorScheme;
    return Padding(
      padding: EdgeInsets.all(getPadding()),
      child: Material(
        borderRadius: BorderRadius.circular(_getSize() / 2),
        color: _getBackgroundColor(colorScheme),
        child: Ink(
          decoration: ShapeDecoration(
            color: _getBackgroundColor(colorScheme),
            shape: CircleBorder(
              side: BorderSide(
                color: _getBorderColor(colorScheme),
                width: AppSize.s1,
              ),
            ),
          ),
          child: InkWell(
            borderRadius: BorderRadius.circular(_getSize() / 2),
            onTap: onPressed,
            child: Container(
              width: _getSize(),
              height: _getSize(),
              alignment: Alignment.center,
              child: Icon(
                icon,
                color: _getIconColor(colorScheme),
                size: _getSize() *
                    getSizeInRelation(), // Icon size in relation to the container
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

In this snippet, the CustomIconButton takes in standard parameters like icon and onPressed, along with optional parameters for color and size customization. The use of ColorScheme from the current theme context ensures that it adapts well to different themes.

Here’s how you can use CustomIconButton in your Flutter app:

CustomIconButton(
  icon: Icons.favorite,
  iconColor: Colors.red,
  backgroundColor: Colors.white,
  borderColor: Colors.grey,
  size: 50.0,
  onPressed: () {
    // Your action here
  },
)
Enter fullscreen mode Exit fullscreen mode

Feel free to use this component in your projects and modify it as needed. Happy coding!

Top comments (0)