diff --git a/src/content/release/breaking-changes/index.md b/src/content/release/breaking-changes/index.md index e52f3cbb5c9..08f8f659aca 100644 --- a/src/content/release/breaking-changes/index.md +++ b/src/content/release/breaking-changes/index.md @@ -42,10 +42,12 @@ They're sorted by release and listed in alphabetical order: ### Not yet released to stable +* [Redesigned the Radio Widget][] * [Removed semantics elevation and thickness][] * [Deprecate `TextField.canRequestFocus`][] * [Stop generating `AssetManifest.json`][] +[Redesigned the Radio Widget]: /release/breaking-changes/radio-api-redesign [Removed semantics elevation and thickness]: /release/breaking-changes/remove-semantics-elevation-and-thickness [Deprecate `TextField.canRequestFocus`]: /release/breaking-changes/can-request-focus [Stop generating `AssetManifest.json`]: /release/breaking-changes/asset-manifest-dot-json diff --git a/src/content/release/breaking-changes/radio-api-redesign.md b/src/content/release/breaking-changes/radio-api-redesign.md new file mode 100644 index 00000000000..bf7379e4538 --- /dev/null +++ b/src/content/release/breaking-changes/radio-api-redesign.md @@ -0,0 +1,242 @@ +--- +title: Redesigned the Radio Widget +description: >- + Changes to 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: + +```dart +Widget build(BuildContext context) { + return Column( + children: [ + Radio( + value: 0, + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, + ), + Radio( + value: 2, + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, + ), + ], + ); +} +``` + +Code after migration: + +```dart +Widget build(BuildContext context) { + return RadioGroup( + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, + child: Column( + children: [ + Radio(value: 0), + Radio(value: 2), + ], + ), + ); +} +``` + +### Case 2: disabled radio + +Code before migration: + +```dart +Widget build(BuildContext context) { + return Column( + children: [ + Radio( + value: 0, + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, + ), + Radio( + value: 2, + groupValue: _groupValue, + onChanged: null, // disabled + ), + ], + ); +} +``` + +Code after migration: + +```dart +Widget build(BuildContext context) { + return RadioGroup( + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, + child: Column( + children: [ + Radio(value: 0), + Radio(value: 2, enabled: false), + ], + ), + ); +} +``` + +### Case 3: mixed group or multi-selection + +Code before migration: + +```dart +Widget build(BuildContext context) { + return Column( + children: [ + Radio( + value: 1, + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, // disabled + ), + Radio( + value: 'a', + groupValue: _stringValue, + onChanged: (String? value) { + setState(() { + _stringValue = value; + }); + }, + ), + Radio( + value: 'b', + groupValue: _stringValue, + onChanged: (String? value) { + setState(() { + _stringValue = value; + }); + }, + ), + Radio( + value: 2, + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, // disabled + ), + ], + ); +} +``` + +Code after migration: + +```dart +Widget build(BuildContext context) { + return RadioGroup( + groupValue: _groupValue, + onChanged: (int? value) { + setState(() { + _groupValue = value; + }); + }, + child: Column( + children: [ + Radio(value: 1), + RadioGroup( + child: Column( + children: [ + Radio(value: 'a'), + Radio(value: 'b'), + ] + ), + ), + Radio(value: 2), + ], + ), + ); +} +``` + +## Timeline + +Landed in version: Not yet
+In stable release: Not yet + +## References + +* [`APG`][] + +API documentation: + +* [`Radio`][] +* [`CupertinoRadio`][] +* [`RadioListTile`][] +* [`RadioGroup`][] + +Relevant issue: + +* [Issue 113562][] + +Relevant PR: + +* [PR 168161][] + +[`APG`]: https://www.w3.org/WAI/ARIA/apg/patterns/radio +[`Radio`]: {{site.api}}/flutter/material/Radio-class.html +[`RadioListTile`]: {{site.api}}/flutter/material/RadioListTile-class.html +[`CupertinoRadio`]: {{site.api}}/flutter/cupertino/CupertinoRadio-class.html +[`RadioGroup`]: {{site.api}}/flutter/widgets/RadioGroup-class.html +[Issue 113562]: {{site.repo.flutter}}/issues/113562 +[PR 168161]: {{site.repo.flutter}}/pull/168161