How To Make Custom Radio Buttons With Cool Effects in Flutter

Taiwo FarinuTaiwo Farinu
6 min read

This tutorial will help you to understand how to create a group of custom radio buttons. Radio buttons are buttons that can be used to choose from different options.

So, we are creating this screen below:

Prerequisites

  • Basic knowledge of Dart programming language.

  • Familiarity with Flutter framework and its widgets.

  • A working installation of Flutter SDK and Android Studio or VS Code

  • Understanding of Stateful widgets and their role in managing state in a Flutter app.

  • Basic knowledge of layout and positioning in Flutter

  • Familiarity with using image assets in Flutter.

Tutorial

Open any of the IDE. Create a flutter project (Application) and name the project any name of your choice.

After the project is created, run the application on your device or emulator.

Download the three payment platforms' logos and the tick circle icon. I downloaded them from the images on google.

Add the four images to the assets folder or you may need to create the folder at the root of the project folder.

assets/images/

Add the path of the images to pubspec.yaml

 # To add assets to your application, add an assets section, like this:
assets:
    - assets/images/visa.png
    - assets/images/mastercard.png
    - assets/images/paypal.png
    - assets/images/tick-circle.png

In main.dart file, replace your code with the code below. The code sets up a basic Flutter app with an app bar and an empty container on the home page.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: const Color(0xFF5C6ED1),
      ),
      home: const HomePage(),
    );
  }
}
class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
    @override
    Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text("Custom Radio Button Demo"),
          ),
          body: Container();
     }
}

The HomePage class is a stateful widget that has a state object of type _HomePageState. The build() method of this class returns a Scaffold widget, which is a basic visual structure for the app. The Scaffold widget has an AppBar and a body. The AppBar has a title with the text "Custom Radio Button Demo", and the body is an empty Container.

...
class _HomePageState extends State<HomePage> {
  int selectedPayment = 0;

  Widget CustomPaymentCardButton(String assetName, int index) {
    return OutlinedButton(
      onPressed: () {
        setState(() {
          selectedPayment = index;
        });
      },
      style: OutlinedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
        side: BorderSide(
            width: (selectedPayment == index) ? 2.0 : 0.5,
            color: (selectedPayment == index)
                ? Colors.green
                : Colors.blue.shade600),
      ),
      child: Stack(
        children: [
          Center(
            child: Image.asset(
              assetName,
              fit: BoxFit.contain,
              width: 120,
              height: 120,
            ),
          ),
          if (selectedPayment == index)
            Positioned(
              top: 5,
              right: 5,
              child: Image.asset(
                "assets/images/tick-circle.png",
                width: 25,
                fit: BoxFit.cover,
              ),
            ),
        ],
      ),
    );
  }
...

Inside the _HomePageState which extends State class. There is a variable named selectedPayment that is initially set to 0. It is used to keep track of the selected payment option. There is a method CustomPaymentCardButton takes two parameters, assetName and index. assetName is a string that represents the path to an image asset, and index is an integer that represents the position of the button in a list of payment options.

The method returns an OutlinedButton widget, which is a Material Design styled button with an outlined border. The onPressed callback of the button sets the value of selectedPayment to the index of the button that is pressed.

The child property of the button is a Stack widget that contains an Image.asset widget with the image specified by the assetName parameter. The Stack also contains a Positioned widget that shows a checkmark icon if the button is selected.

The style property of the button is used to set its padding, border shape, and color based on whether it is selected or not. If the button is selected, its border width is set to 2.0, and its color is set to green. Otherwise, its border width is set to 0.5, and its color is set to blue.

...
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Custom Radio Button Demo"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        child: Column(
          children: [
            const SizedBox(
              height: 20,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  child: CustomPaymentCardButton("assets/images/visa.png", 0),
                ),
                const SizedBox(
                  width: 20,
                ),
                Expanded(
                  child: CustomPaymentCardButton(
                      "assets/images/mastercard.png", 1),
                ),
              ],
            ),
            const SizedBox(
              height: 16,
            ),
            CustomPaymentCardButton("assets/images/paypal.png", 2),
          ],
        ),
      ),
    );
  }
...

The body is defined using a Padding widget with a horizontal padding of 20. The padding contains a Column widget that contains a list of payment options.

The Row widget contains two Expanded widgets that each contain a custom payment card button defined using the CustomPaymentCardButton method. The first button shows an image of a Visa card and has an index of 0. The second button shows an image of a Mastercard and has an index of 1. The Row widget is used to display the two buttons side-by-side with an equal width.

The CustomPaymentCardButton method is called again with an image of a PayPal logo and an index of 2 to display a third button below the Row.

The overall code is the code below:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: const Color(0xFF5C6ED1),
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int selectedPayment = 0;

  Widget CustomPaymentCardButton(String assetName, int index) {
    return OutlinedButton(
      onPressed: () {
        setState(() {
          selectedPayment = index;
        });
      },
      style: OutlinedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
        side: BorderSide(
            width: (selectedPayment == index) ? 2.0 : 0.5,
            color: (selectedPayment == index)
                ? Colors.green
                : Colors.blue.shade600),
      ),
      child: Stack(
        children: [
          Center(
            child: Image.asset(
              assetName,
              fit: BoxFit.contain,
              width: 120,
              height: 120,
            ),
          ),
          if (selectedPayment == index)
            Positioned(
              top: 5,
              right: 5,
              child: Image.asset(
                "assets/images/tick-circle.png",
                width: 25,
                fit: BoxFit.cover,
              ),
            ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Custom Radio Button Demo"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        child: Column(
          children: [
            const SizedBox(
              height: 20,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  child: CustomPaymentCardButton("assets/images/visa.png", 0),
                ),
                const SizedBox(
                  width: 20,
                ),
                Expanded(
                  child: CustomPaymentCardButton(
                      "assets/images/mastercard.png", 1),
                ),
              ],
            ),
            const SizedBox(
              height: 16,
            ),
            CustomPaymentCardButton("assets/images/paypal.png", 2),
          ],
        ),
      ),
    );
  }
}

Run the app.

The overall code creates an app that displays custom payment card buttons that can be selected and displays a tick mark for the selected payment option.

Conclusion

In conclusion, custom radio buttons can provide a great way to enhance the user experience of your Flutter app. By using custom widgets and carefully selecting visual cues, you can create radio buttons that stand out and communicate their purpose to your users. With the example code we've covered here, you can get started on creating your custom radio buttons in your Flutter projects. With some practice and attention to detail, you can create radio buttons that are both functional and visually appealing for your users.

Thanks for reading.

0
Subscribe to my newsletter

Read articles from Taiwo Farinu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Taiwo Farinu
Taiwo Farinu

I am a software engineer who build mobile apps. I also write articles on android, flutter, DSAs