How to Programmatically Set a Radio Button in Flutter to Be Checked by Default

Initialize a state variable with your target value and assign it to the groupValue parameter of RadioGroup (or groupValue in legacy Radio widgets) before the first build, and Flutter's declarative framework will automatically render the matching radio button as selected.

To programmatically set a radio button in Flutter to be checked by default when the widget first renders, you initialize the backing state variable with your target selection value before the widget tree builds. The flutter/flutter repository implements this pattern through the modern RadioGroup widget (recommended) and the underlying Radio widget, both of which determine the selected state by comparing the groupValue against each individual radio's value property.

Understanding Flutter's Declarative Radio Architecture

Flutter treats radio button selection as data-driven rather than imperative. In packages/flutter/lib/src/widgets/radio_group.dart, the RadioGroup widget manages selection by storing the current value in widget.groupValue (lines 61–66). The framework then iterates through child Radio widgets and marks the one whose value equals groupValue as selected.

This declarative approach means you never "check" a radio button directly. Instead, you provide the value that should be selected, and the framework renders the appropriate visual state.

The RadioGroup API is the modern, preferred approach in the Flutter SDK. It handles focus management, accessibility, and group semantics automatically.

Initializing the Default Selection

Create a state variable initialized to your desired default value, then pass it to RadioGroup.groupValue:

import 'package:flutter/material.dart';

enum SingingCharacter { lafayette, jefferson }

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

  @override
  State<RadioExample> createState() => _RadioExampleState();
}

class _RadioExampleState extends State<RadioExample> {
  // ✅ Initialize with the default selection
  SingingCharacter? _selected = SingingCharacter.lafayette;

  @override
  Widget build(BuildContext context) {
    return RadioGroup<SingingCharacter>(
      // 👉 The group uses this value to determine which radio is selected
      groupValue: _selected,
      onChanged: (SingingCharacter? value) {
        setState(() => _selected = value);
      },
      child: const Column(
        children: [
          ListTile(
            title: Text('Lafayette'),
            leading: Radio<SingingCharacter>(value: SingingCharacter.lafayette),
          ),
          ListTile(
            title: Text('Jefferson'),
            leading: Radio<SingingCharacter>(value: SingingCharacter.jefferson),
          ),
        ],
      ),
    );
  }
}

In this implementation, SingingCharacter.lafayette is checked by default because _selected is initialized to that value before the first build call. The RadioGroup reads this value (as seen in the RadioGroupState getter at line 29 of the source) and marks the corresponding Radio as selected.

Method 2: Using the Legacy Radio API

If you are maintaining older code or using Flutter versions prior to the RadioGroup introduction, you can set the default selection using the Radio widget's groupValue parameter directly.

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

  @override
  State<LegacyRadioExample> createState() => _LegacyRadioExampleState();
}

class _LegacyRadioExampleState extends State<LegacyRadioExample> {
  // Default selection for the legacy API
  SingingCharacter? _character = SingingCharacter.lafayette;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Radio<SingingCharacter>(
          value: SingingCharacter.lafayette,
          groupValue: _character,               // ← pre‑selected
          onChanged: (value) => setState(() => _character = value),
        ),
        Radio<SingingCharacter>(
          value: SingingCharacter.jefferson,
          groupValue: _character,
          onChanged: (value) => setState(() => _character = value),
        ),
      ],
    );
  }
}

Note: The groupValue property on individual Radio widgets is deprecated after v3.32.0‑0.0.pre. The Flutter team recommends migrating to the RadioGroup pattern for new applications.

How the Selection Logic Works Under the Hood

According to the flutter/flutter source code, the selection mechanism relies on value comparison rather than imperative state setting:

  1. RadioGroup Implementation: In packages/flutter/lib/src/widgets/radio_group.dart, the RadioGroupState class maintains the selection through the groupValue property (lines 61–66). The RadioGroup registers each child Radio and compares their value with groupValue to determine which should appear selected.

  2. Radio Widget Construction: In packages/flutter/lib/src/material/radio.dart (line 94), the Radio constructor accepts the value parameter but does not store its own "checked" state. Instead, it forwards value and groupValue to the internal RawRadio widget, which renders the appropriate visual state based on whether the values match.

  3. Declarative Rendering: Because the selected state is purely a function of the data (groupValue), not of any imperative "set‑checked" call, initializing your state variable before the first build ensures the radio appears selected immediately without flicker or additional animation.

Summary

  • Initialize state first: Set your state variable to the desired default value in the constructor or at declaration time.
  • Use RadioGroup: Pass the initialized variable to RadioGroup.groupValue for modern Flutter applications (v3.32.0+).
  • Value matching: The framework selects the Radio whose value equals the groupValue—selection is declarative, not imperative.
  • Legacy support: For older code, use Radio.groupValue (deprecated) with the same initialization pattern.

Frequently Asked Questions

How do I set a radio button as checked by default in Flutter without using a stateful widget?

You cannot. Flutter's radio button architecture requires a state variable to hold the groupValue that determines which radio is selected. Even if you use a StatelessWidget with a final field, the parent widget must manage the state. The RadioGroup or Radio widget reads the groupValue from the widget tree's state, so a StatefulWidget (or state management solution like Provider or Riverpod) is mandatory to initialize and update the selection.

Can I have multiple radio groups with different default selections in the same screen?

Yes. Each RadioGroup widget manages its own scope and groupValue. Instantiate separate state variables for each group and initialize them with different default values. For example, _selectedColor = Colors.red for a color group and _selectedSize = Sizes.large for a size group. Ensure each RadioGroup receives its corresponding state variable as its groupValue parameter so that each group maintains independent default selections.

Why is my radio button not showing as selected even though I set the initial value?

This typically occurs when the initial value does not match the value property of the Radio widget exactly, or when the state variable is being reset after initialization. Verify that the type of your state variable matches the generic type T of Radio<T> and RadioGroup<T>. Also, ensure you are not reinitializing the variable inside build or in a didChangeDependencies method without checking if the value is already set. The comparison uses Dart's equality operator (==), so custom objects must implement == and hashCode correctly for the selection to register.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →