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.onChanged
Radio.groupValue
CupertinoRadio.onChanged
CupertinoRadio.groupValue
RadioListTile.groupValue
RadioListTile.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: Not yet
In stable release: Not yet
References
#API documentation:
Relevant issue:
Relevant PR:
Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2025-05-29. View source or report an issue.