Flutter Custom Radio Button with Custom Shapes

Flutter development is becoming popular and more popular every day, due to its wide range of customization, custom widgets, and very easy-to-implement approach. Today we will learn how we can make flutter custom radio button with custom shapes like square containers, circle containers, or icons. I have tried to demonstrate the different methods so that they can be used as per need and the developer who will learn from this tutorial can also get the idea to make his own flutter custom radio button, with his required shape or widget.

This Flutter Custom Radio Button example will increase your development speed and save your time by calling it directly as a child of any widget or by making static methods where it is needed. Because no app can be done without implementation of any custom widegt, all ready made widgets does not help all the time. So learning to make custom widget is the most essenstial part of flutter developement.

What we will get in output?

Required Dependencies

No thirdparty dependencies needed.

Let’s start step by step implementation so that we can understand what is need to be done to acheive the desired output. First we will make our home widget where we going to show the custom radio buttons.

It is a stafull widget which is capable to handle all the functionality of radio button, like showing custom design, tap event and selection etc.

home_screen.dart

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<RadioButtonModel> radioOptions = [];

  @override
  void initState() {
    super.initState();
    radioOptions.add(RadioButtonModel(false, 'A', 'Option A'));
    radioOptions.add(RadioButtonModel(false, 'B', 'Option B'));
    radioOptions.add(RadioButtonModel(false, 'C', 'Option C'));
    radioOptions.add(RadioButtonModel(false, 'D', 'Option D'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Custom Check Box Example'),
      ),
      body: MyRadioButtonWidget(
        options: radioOptions,
        onItemSelected: (index) {
          print('I am index: $index');
        },
      ),
    );
  }
}

Let’s make custom radio button now

This class is reponsible to handle all custom radio button operations, like design, events and callback. If you will notice i have added a callback function which is returning current tapped item index. In case you want to make your custom radio button class independent and generic you can make for events as per your requirement.

 final Function(int)? onItemSelected;

my_radio_button_widget.dart

class MyRadioButtonWidget extends StatefulWidget {
  final List<RadioButtonModel>? options;
  final Function(int)? onItemSelected;

  const MyRadioButtonWidget({Key? key, this.options, this.onItemSelected})
      : super(key: key);

  @override
  createState() {
    return MyRadioButtonWidgetState();
  }
}

class MyRadioButtonWidgetState extends State<MyRadioButtonWidget> {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: widget.options!.length,
      itemBuilder: (BuildContext context, int index) {
        return InkWell(
          onTap: () {
            setState(() {
              for (var element in widget.options!) {
                element.isSelected = false;
              }
              widget.options![index].isSelected = true;

              widget.onItemSelected!(index);
            });
          },
          child: CircleRadioButtonItem(widget.options![index]),
        );
      },
    );
  }
}

Now we are going to make custom shapes for our cutom radio button, we have multiple option you can find three different classes to demonstrate different style. by using this idea you can make your own and modify the given examples as you want.

Square Radio Button Item

Here we are making square shape container with text inside it.

class SquareRadioButtonItem extends StatelessWidget {
  final RadioButtonModel _item;

  const SquareRadioButtonItem(this._item, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Container(
            height: 50.0,
            width: 50.0,
            child: Center(
              child: Text(_item.buttonText,
                  style: TextStyle(
                      color: _item.isSelected ? Colors.white : Colors.black,
                      //fontWeight: FontWeight.bold,
                      fontSize: 18.0)),
            ),
            decoration: BoxDecoration(
              color: _item.isSelected ? Colors.blueAccent : Colors.transparent,
              border: Border.all(
                  width: 1.0,
                  color: _item.isSelected ? Colors.blueAccent : Colors.grey),
              borderRadius: const BorderRadius.all(Radius.circular(2.0)),
            ),
          ),
          Container(
            margin: const EdgeInsets.only(left: 10.0),
            child: Text(_item.text),
          )
        ],
      ),
    );
  }
}

Circle Radio Button Item

class CircleRadioButtonItem extends StatelessWidget {
  final RadioButtonModel _item;

  const CircleRadioButtonItem(this._item, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Container(
            height: 50.0,
            width: 50.0,
            child: Center(
              child: Text(_item.buttonText,
                  style: TextStyle(
                      color: _item.isSelected ? Colors.white : Colors.black,
                      //fontWeight: FontWeight.bold,
                      fontSize: 18.0)),
            ),
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: _item.isSelected ? Colors.blueAccent : Colors.transparent,
              border: Border.all(
                  width: 1.0,
                  color: _item.isSelected ? Colors.blueAccent : Colors.grey),
            ),
          ),
          Container(
            margin: const EdgeInsets.only(left: 10.0),
            child: Text(_item.text),
          )
        ],
      ),
    );
  }
}

Icon Radio Button Item

class IconRadioButtonItem extends StatelessWidget {
  final RadioButtonModel _item;

  const IconRadioButtonItem(this._item, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          _item.isSelected
              ? const Icon(Icons.circle)
              : const Icon(Icons.circle_outlined),
          Container(
            margin: const EdgeInsets.only(left: 10.0),
            child: Text(_item.text),
          )
        ],
      ),
    );
  }
}

Radio Button Model


class RadioButtonModel {
  bool isSelected;
  final String buttonText;
  final String text;

  RadioButtonModel(this.isSelected, this.buttonText, this.text);
}

Here is the full code in one shot

import 'package:flutter/material.dart';

class MyRadioButtonWidget extends StatefulWidget {
  final List<RadioButtonModel>? options;
  final Function(int)? onItemSelected;

  const MyRadioButtonWidget({Key? key, this.options, this.onItemSelected})
      : super(key: key);

  @override
  createState() {
    return MyRadioButtonWidgetState();
  }
}

class MyRadioButtonWidgetState extends State<MyRadioButtonWidget> {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: widget.options!.length,
      itemBuilder: (BuildContext context, int index) {
        return InkWell(
          onTap: () {
            setState(() {
              for (var element in widget.options!) {
                element.isSelected = false;
              }
              widget.options![index].isSelected = true;

              widget.onItemSelected!(index);
            });
          },
          child: CircleRadioButtonItem(widget.options![index]),
        );
      },
    );
  }
}

class SquareRadioButtonItem extends StatelessWidget {
  final RadioButtonModel _item;

  const SquareRadioButtonItem(this._item, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Container(
            height: 50.0,
            width: 50.0,
            child: Center(
              child: Text(_item.buttonText,
                  style: TextStyle(
                      color: _item.isSelected ? Colors.white : Colors.black,
                      //fontWeight: FontWeight.bold,
                      fontSize: 18.0)),
            ),
            decoration: BoxDecoration(
              color: _item.isSelected ? Colors.blueAccent : Colors.transparent,
              border: Border.all(
                  width: 1.0,
                  color: _item.isSelected ? Colors.blueAccent : Colors.grey),
              borderRadius: const BorderRadius.all(Radius.circular(2.0)),
            ),
          ),
          Container(
            margin: const EdgeInsets.only(left: 10.0),
            child: Text(_item.text),
          )
        ],
      ),
    );
  }
}

class CircleRadioButtonItem extends StatelessWidget {
  final RadioButtonModel _item;

  const CircleRadioButtonItem(this._item, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Container(
            height: 50.0,
            width: 50.0,
            child: Center(
              child: Text(_item.buttonText,
                  style: TextStyle(
                      color: _item.isSelected ? Colors.white : Colors.black,
                      //fontWeight: FontWeight.bold,
                      fontSize: 18.0)),
            ),
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: _item.isSelected ? Colors.blueAccent : Colors.transparent,
              border: Border.all(
                  width: 1.0,
                  color: _item.isSelected ? Colors.blueAccent : Colors.grey),
            ),
          ),
          Container(
            margin: const EdgeInsets.only(left: 10.0),
            child: Text(_item.text),
          )
        ],
      ),
    );
  }
}

class IconRadioButtonItem extends StatelessWidget {
  final RadioButtonModel _item;

  const IconRadioButtonItem(this._item, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          _item.isSelected
              ? const Icon(Icons.circle)
              : const Icon(Icons.circle_outlined),
          Container(
            margin: const EdgeInsets.only(left: 10.0),
            child: Text(_item.text),
          )
        ],
      ),
    );
  }
}

class RadioButtonModel {
  bool isSelected;
  final String buttonText;
  final String text;

  RadioButtonModel(this.isSelected, this.buttonText, this.text);
}

If you have any query feel free to ask. Also, check out the bottom navigation seriescustom textfield, custom social login buttons and many others.

Enter Details to Download Code
Drop your Terms & Conditions here.
I agree with the Terms & Conditions
Get Download Link

Er. Sandeep Singh Sidhu

I am the founder of CallStem Software LLP and Stem Technocrats IT Services, Blood Wallet, Apni Boli, and of course flutterfumes.com.
I am very passionate about technology and learning new things. I believe sharing knowledge is the most powerful way to learn. One who is an active learner is always an active contributor too because whatever you learn, you should implement.

I also love to write poetry, and talking to myself is my favorite all-time activity, no matter it is a program logic, emotional poetry, or a social issue.

Previous Post
Next Post

Leave a Reply

Your email address will not be published.

Hey, wait! If You don't want to miss Flutter Updates!

Please join our Newsletter List and enjoy with daily Flutter Updates