Redesigned the Radio widget
Summary
#
Introduced the RadioGroup widget to centralize groupValue management and the
onChanged
callback for a set of Radio widgets. As a result, the individual Radio.groupValue
and
Radio.onChanged properties have been deprecated.
Context
#
To meet APG (ARIA Practices Guide) requirements for keyboard navigation and
semantic properties in radio button groups, Flutter needed a dedicated radio group concept.
Introducing a wrapper widget, RadioGroup, provides this out-of-the-box support.
This change also presented an opportunity to simplify the API for individual Radio
widgets.
Description of change
#The following API is deprecated:
Radio.onChangedRadio.groupValueCupertinoRadio.onChangedCupertinoRadio.groupValueRadioListTile.groupValueRadioListTile.onChanged.
Migration guide
#If you are using these properties, you can refactor them with RadioGroup.
Case 1: trivial case
#Code before migration:
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Radio<int>(
value: 0,
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
},
),
Radio<int>(
value: 2,
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
},
),
],
);
}
Code after migration:
Widget build(BuildContext context) {
return RadioGroup<int>(
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
},
child: Column(
children: <Widget>[
Radio<int>(value: 0),
Radio<int>(value: 2),
],
),
);
}
Case 2: disabled radio
#Code before migration:
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Radio<int>(
value: 0,
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
},
),
Radio<int>(
value: 2,
groupValue: _groupValue,
onChanged: null, // disabled
),
],
);
}
Code after migration:
Widget build(BuildContext context) {
return RadioGroup<int>(
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
},
child: Column(
children: <Widget>[
Radio<int>(value: 0),
Radio<int>(value: 2, enabled: false),
],
),
);
}
Case 3: mixed group or multi-selection
#Code before migration:
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Radio<int>(
value: 1,
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
}, // disabled
),
Radio<String>(
value: 'a',
groupValue: _stringValue,
onChanged: (String? value) {
setState(() {
_stringValue = value;
});
},
),
Radio<String>(
value: 'b',
groupValue: _stringValue,
onChanged: (String? value) {
setState(() {
_stringValue = value;
});
},
),
Radio<int>(
value: 2,
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
}, // disabled
),
],
);
}
Code after migration:
Widget build(BuildContext context) {
return RadioGroup<int>(
groupValue: _groupValue,
onChanged: (int? value) {
setState(() {
_groupValue = value;
});
},
child: Column(
children: <Widget>[
Radio<int>(value: 1),
RadioGroup<String>(
child: Column(
children: <Widget>[
Radio<String>(value: 'a'),
Radio<String>(value: 'b'),
]
),
),
Radio<int>(value: 2),
],
),
);
}
Timeline
#
Landed in version: 3.34.0-0.0.pre
In stable release: 3.35
References
#API documentation:
Relevant issue:
Relevant PR:
Unless stated otherwise, the documentation on this site reflects Flutter 3.35.5. Page last updated on 2025-10-28. View source or report an issue.