flutter
/

Flutter Radio Button – Complete Guide with RadioListTile

Last Sync: Today

On this page

9
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter Radio Button – Complete Guide with RadioListTile

What is a Radio Button?

A radio button is a UI element that allows the user to select exactly one option from a set. In Flutter, the Radio widget provides a simple circular button, while RadioListTile combines a radio button with a label, making it easier to use in forms and lists. Radio buttons are commonly used for gender selection, payment methods, or any exclusive choice.

Basic Radio Widget

The Radio widget is the simplest form. It requires three parameters: value, groupValue, and onChanged. The value is the unique identifier for this radio button. The groupValue is the currently selected value. When the user taps the radio, onChanged is called with the radio's value.

DARTRead-only
1
int? _selectedValue = 1;

Row(
  children: [
    Radio<int>(
      value: 1,
      groupValue: _selectedValue,
      onChanged: (value) => setState(() => _selectedValue = value),
    ),
    Text('Option 1'),
    Radio<int>(
      value: 2,
      groupValue: _selectedValue,
      onChanged: (value) => setState(() => _selectedValue = value),
    ),
    Text('Option 2'),
  ],
)

Using RadioListTile for Better UI

RadioListTile wraps a radio button and a label (title, subtitle, etc.) in a single widget. It handles the layout and provides a larger tap target. It also integrates well with ListTile and forms.

DARTRead-only
1
RadioListTile<int>(
  value: 1,
  groupValue: _selectedValue,
  title: Text('Option 1'),
  subtitle: Text('Description for option 1'),
  onChanged: (value) => setState(() => _selectedValue = value),
)

Creating a Radio Group

A radio group is simply a set of Radio or RadioListTile widgets that share the same groupValue. The type parameter (e.g., <int>, <String>) ensures type safety. You can use any type as the group value.

DARTRead-only
1
enum Gender { male, female, other }

Gender? _selectedGender;

Column(
  children: Gender.values.map((gender) {
    return RadioListTile<Gender>(
      value: gender,
      groupValue: _selectedGender,
      title: Text(gender.name.toUpperCase()),
      onChanged: (value) => setState(() => _selectedGender = value),
    );
  }).toList(),
)

Customizing Radio Buttons

You can customize the radio button's color, size, and other properties:

    • activeColor: Color when selected.
    • fillColor: Overrides the fill color (MaterialStateProperty).
    • visualDensity: Adjusts the compactness.
    • materialTapTargetSize: Controls the tap target size.
DARTRead-only
1
Radio<int>(
  value: 1,
  groupValue: _selectedValue,
  activeColor: Colors.green,
  visualDensity: VisualDensity.compact,
  onChanged: (value) => setState(() => _selectedValue = value),
)

Integrating with Forms

To use radio buttons in a Form, you can wrap them in a FormField or use RadioListTile inside a Form with a GlobalKey. For validation, you can create a custom form field that manages the radio group.

DARTRead-only
1
class RadioFormField extends FormField<int> {
  RadioFormField({
    required int? initialValue,
    required FormFieldSetter<int> onSaved,
    required FormFieldValidator<int> validator,
  }) : super(
          initialValue: initialValue,
          onSaved: onSaved,
          validator: validator,
          builder: (FormFieldState<int> state) {
            return Column(
              children: [
                RadioListTile<int>(
                  value: 1,
                  groupValue: state.value,
                  title: Text('Option 1'),
                  onChanged: (value) => state.didChange(value),
                ),
                RadioListTile<int>(
                  value: 2,
                  groupValue: state.value,
                  title: Text('Option 2'),
                  onChanged: (value) => state.didChange(value),
                ),
                if (state.hasError)
                  Text(
                    state.errorText!,
                    style: TextStyle(color: Colors.red),
                  ),
              ],
            );
          },
        );
}

Common Mistakes

    • Not updating groupValue in onChanged: The radio won't visually update.
    • Using different value types in the same group: Mixing int and String will break the group.
    • Forgetting to call setState (if using StatefulWidget) to rebuild when selection changes.
    • Not providing a groupValue: The radio will be indeterminate (no selection).
    • Using RadioListTile without a title: The tile may be empty; always provide at least a title.

Best Practices

    • Use RadioListTile for a better user experience – larger tap area, integrated label.
    • Use enum for group values to avoid magic numbers/strings.
    • For forms, either use a custom FormField or manage the selection separately and validate in the form submission.
    • Keep the number of radio options reasonable; for many options, consider a dropdown.

Key Takeaways

    • Radio provides a simple circular button; RadioListTile adds a label and larger tap area.
    • All radio buttons in a group share the same groupValue and onChanged updates it.
    • Use enum for type‑safe group values.
    • Customize colors and density with properties like activeColor.
    • For forms, create a custom FormField or use RadioListTile inside a Form with manual validation.

Try it yourself

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Radio Demo',
      home: Scaffold(
        appBar: AppBar(title: Text('Radio Button Example')),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Center(child: RadioDemo()),
        ),
      ),
    );
  }
}

class RadioDemo extends StatefulWidget {
  @override
  _RadioDemoState createState() => _RadioDemoState();
}

class _RadioDemoState extends State<RadioDemo> {
  String? _selectedOption = 'Option 1';

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        RadioListTile<String>(
          value: 'Option 1',
          groupValue: _selectedOption,
          title: Text('Option 1'),
          onChanged: (value) => setState(() => _selectedOption = value),
        ),
        RadioListTile<String>(
          value: 'Option 2',
          groupValue: _selectedOption,
          title: Text('Option 2'),
          onChanged: (value) => setState(() => _selectedOption = value),
        ),
        RadioListTile<String>(
          value: 'Option 3',
          groupValue: _selectedOption,
          title: Text('Option 3'),
          onChanged: (value) => setState(() => _selectedOption = value),
        ),
        SizedBox(height: 20),
        Text('Selected: ${_selectedOption ?? 'none'}'),
      ],
    );
  }
}

Test Your Knowledge

Q1
of 4

Which property determines which radio is selected?

A
value
B
groupValue
C
selected
D
checked
Q2
of 4

What is the purpose of `RadioListTile`?

A
To create a list of radio buttons
B
To combine a radio button with a title and subtitle
C
To manage radio groups automatically
D
To style radio buttons
Q3
of 4

How do you disable a radio button?

A
Set `enabled: false`
B
Set `onChanged: null`
C
Set `disabled: true`
D
Set `active: false`
Q4
of 4

What happens if `groupValue` does not match any radio's `value`?

A
No radio is selected
B
The first radio is selected
C
An error is thrown
D
The last radio is selected

Frequently Asked Questions

How do I set a default selected radio?

Initialize the groupValue with one of the radio values. For example, int? _selected = 1; makes the radio with value: 1 selected initially.

Can I use radio buttons with a `ListTile` without `RadioListTile`?

Yes, you can combine a Radio widget with a Text inside a Row or a ListTile. But RadioListTile is built specifically for that purpose and handles layout and touch events better.

How do I make a radio button disabled?

Set onChanged: null. The radio will appear disabled (usually greyed out) and won't respond to taps.

Can I use radio buttons with a `Form` without creating a custom field?

Yes, you can manage the radio selection in the widget state and read the value on form submission, but you won't get automatic validation. A custom FormField is cleaner for validation.

What is the difference between `Radio` and `RadioListTile`?

Radio is just the button itself. RadioListTile combines the radio button with a title, subtitle, and optional leading/trailing widgets. It uses the same tap area for the entire tile, making it easier to select.

Previous

flutter checkbox

Next

flutter dropdown

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.