From 870c5541c31964408dce206dca2071b62df6c656 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 21 May 2024 10:18:05 -0600 Subject: [PATCH] `switch` expressions: finale (#148711) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### fixes #136139
getting sentimental in the PR description (click to collapse)

The past 7 months have been quite the journey—I made some huge blunders and some huge accomplishments—a very fun time overall. I really appreciate the people who took the time to perform code review for my refactoring shenanigans: **christopherfujino**, **andrewkolos**, **LongCatIsLooong**, **gspencergoog**, **loic-sharma**, **Piinks**, **bernaferrari**, **bartekpacia**, **bleroux**, **kevmoo**, **rakudrama**, **XilaiZhang**, **QuncCccccc**, **MominRaza**, and **victorsanni**. And a huge shoutout to 2 individuals: - @justinmc, for offering to sponsor me for commit access (words could not describe my excitement) - @goderbauer, for being super duper proactive and consistent with code review
This pull request makes 13 "switch statements → switch expressions" PRs in total, reducing the LOC in this repo by **1,974**! From now on, I'll make sure to request a test exemption for each refactoring PR 🙂 --- dev/benchmarks/test_apps/stocks/lib/main.dart | 22 +++--- dev/conductor/core/lib/src/globals.dart | 22 ++---- .../lib/demo/material/cards_demo.dart | 27 +++---- .../supplemental/cut_corners_border.dart | 21 ++---- .../demos/cupertino/cupertino_alert_demo.dart | 39 ++++------- .../lib/demos/material/button_demo.dart | 22 ++---- .../lib/demos/material/chip_demo.dart | 19 ++--- .../lib/demos/material/divider_demo.dart | 28 +++----- .../lib/demos/material/picker_demo.dart | 29 +++----- .../material/selection_controls_demo.dart | 16 ++--- .../lib/demos/material/sliders_demo.dart | 39 +++-------- .../supplemental/cut_corners_border.dart | 23 ++---- .../localization/bin/gen_localizations.dart | 17 ++--- .../axis_direction/axis_direction.0.dart | 38 +++------- .../growth_direction/growth_direction.0.dart | 38 +++------- .../scroll_direction/scroll_direction.0.dart | 38 +++------- .../api/lib/widgets/overlay/overlay.0.dart | 70 +++++-------------- .../flutter/lib/src/cupertino/scrollbar.dart | 17 ++--- packages/flutter/lib/src/material/drawer.dart | 45 +++++------- .../lib/src/material/input_border.dart | 15 ++-- .../flutter/lib/src/material/stepper.dart | 34 +++------ .../lib/src/widgets/focus_traversal.dart | 61 +++++----------- 22 files changed, 214 insertions(+), 466 deletions(-) diff --git a/dev/benchmarks/test_apps/stocks/lib/main.dart b/dev/benchmarks/test_apps/stocks/lib/main.dart index 536e906c3c2..996f0a31369 100644 --- a/dev/benchmarks/test_apps/stocks/lib/main.dart +++ b/dev/benchmarks/test_apps/stocks/lib/main.dart @@ -47,20 +47,14 @@ class StocksAppState extends State { } ThemeData get theme { - switch (_configuration.stockMode) { - case StockMode.optimistic: - return ThemeData( - useMaterial3: false, - brightness: Brightness.light, - primarySwatch: Colors.purple, - ); - case StockMode.pessimistic: - return ThemeData( - useMaterial3: false, - brightness: Brightness.dark, - primarySwatch: Colors.purple, - ); - } + return ThemeData( + useMaterial3: false, + brightness: switch (_configuration.stockMode) { + StockMode.optimistic => Brightness.light, + StockMode.pessimistic => Brightness.dark, + }, + primarySwatch: Colors.purple, + ); } Route? _getRoute(RouteSettings settings) { diff --git a/dev/conductor/core/lib/src/globals.dart b/dev/conductor/core/lib/src/globals.dart index 30a6c635d6c..83f17a128cf 100644 --- a/dev/conductor/core/lib/src/globals.dart +++ b/dev/conductor/core/lib/src/globals.dart @@ -152,21 +152,13 @@ String getNewPrLink({ }) { assert(state.releaseChannel.isNotEmpty); assert(state.releaseVersion.isNotEmpty); - late final String candidateBranch; - late final String workingBranch; - late final String repoLabel; - switch (repoName) { - case 'flutter': - candidateBranch = state.framework.candidateBranch; - workingBranch = state.framework.workingBranch; - repoLabel = 'Framework'; - case 'engine': - candidateBranch = state.engine.candidateBranch; - workingBranch = state.engine.workingBranch; - repoLabel = 'Engine'; - default: - throw ConductorException('Expected repoName to be one of flutter or engine but got $repoName.'); - } + final (pb.Repository repository, String repoLabel) = switch (repoName) { + 'flutter' => (state.framework, 'Framework'), + 'engine' => (state.engine, 'Engine'), + _ => throw ConductorException('Expected repoName to be one of flutter or engine but got $repoName.'), + }; + final String candidateBranch = repository.candidateBranch; + final String workingBranch = repository.workingBranch; assert(candidateBranch.isNotEmpty); assert(workingBranch.isNotEmpty); final String title = '[flutter_releases] Flutter ${state.releaseChannel} ' diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/cards_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/cards_demo.dart index 10831fe40eb..e050920ef57 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/material/cards_demo.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/material/cards_demo.dart @@ -377,22 +377,17 @@ class _CardsDemoState extends State { child: ListView( primary: true, padding: const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0), - children: destinations.map((TravelDestination destination) { - Widget? child; - switch (destination.type) { - case CardDemoType.standard: - child = TravelDestinationItem(destination: destination, shape: _shape); - case CardDemoType.tappable: - child = TappableTravelDestinationItem(destination: destination, shape: _shape); - case CardDemoType.selectable: - child = SelectableTravelDestinationItem(destination: destination, shape: _shape); - } - - return Container( - margin: const EdgeInsets.only(bottom: 8.0), - child: child, - ); - }).toList(), + children: [ + for (final TravelDestination destination in destinations) + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: switch (destination.type) { + CardDemoType.standard => TravelDestinationItem(destination: destination, shape: _shape), + CardDemoType.tappable => TappableTravelDestinationItem(destination: destination, shape: _shape), + CardDemoType.selectable => SelectableTravelDestinationItem(destination: destination, shape: _shape), + }, + ), + ], ), ), ); diff --git a/dev/integration_tests/flutter_gallery/lib/demo/shrine/supplemental/cut_corners_border.dart b/dev/integration_tests/flutter_gallery/lib/demo/shrine/supplemental/cut_corners_border.dart index 0b04d512751..8ae8e88733a 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/shrine/supplemental/cut_corners_border.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/shrine/supplemental/cut_corners_border.dart @@ -100,20 +100,13 @@ class CutCornersBorder extends OutlineInputBorder { if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) { canvas.drawPath(_notchedCornerPath(outer.middleRect), paint); } else { - final double? extent = lerpDouble(0.0, gapExtent + gapPadding * 2.0, gapPercentage); - switch (textDirection) { - case TextDirection.rtl: { - final Path path = _notchedCornerPath(outer.middleRect, gapStart + gapPadding - extent!, extent); - canvas.drawPath(path, paint); - break; - } - case TextDirection.ltr: { - final Path path = _notchedCornerPath(outer.middleRect, gapStart - gapPadding, extent); - canvas.drawPath(path, paint); - break; - } - case null: - break; + final double extent = lerpDouble(0.0, gapExtent + gapPadding * 2.0, gapPercentage)!; + if (textDirection != null) { + final double start = switch (textDirection) { + TextDirection.rtl => gapStart + gapPadding - extent, + TextDirection.ltr => gapStart - gapPadding, + }; + canvas.drawPath(_notchedCornerPath(outer.middleRect, start, extent), paint); } } } diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart index bcc856f58c1..76ef67faf74 100644 --- a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart @@ -298,37 +298,26 @@ class _CupertinoAlertDemoState extends State ), child: Builder( builder: (BuildContext context) { + final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; + final Widget showAlertButton = CupertinoButton.filled( + onPressed: () => switch (widget.type) { + AlertDemoType.alert => _alertDialogRoute, + AlertDemoType.alertTitle => _alertWithTitleDialogRoute, + AlertDemoType.alertButtons => _alertWithButtonsDialogRoute, + AlertDemoType.alertButtonsOnly => _alertWithButtonsOnlyDialogRoute, + AlertDemoType.actionSheet => _modalPopupRoute, + }.present(), + child: Text(localizations.cupertinoShowAlert), + ); + return Column( children: [ - Expanded( - child: Center( - child: CupertinoButton.filled( - onPressed: () { - switch (widget.type) { - case AlertDemoType.alert: - _alertDialogRoute.present(); - case AlertDemoType.alertTitle: - _alertWithTitleDialogRoute.present(); - case AlertDemoType.alertButtons: - _alertWithButtonsDialogRoute.present(); - case AlertDemoType.alertButtonsOnly: - _alertWithButtonsOnlyDialogRoute.present(); - case AlertDemoType.actionSheet: - _modalPopupRoute.present(); - } - }, - child: Text( - GalleryLocalizations.of(context)!.cupertinoShowAlert, - ), - ), - ), - ), + Expanded(child: Center(child: showAlertButton)), if (lastSelectedValue.value != null) Padding( padding: const EdgeInsets.all(16), child: Text( - GalleryLocalizations.of(context)! - .dialogSelectedOption(lastSelectedValue.value!), + localizations.dialogSelectedOption(lastSelectedValue.value!), style: CupertinoTheme.of(context).textTheme.textStyle, textAlign: TextAlign.center, ), diff --git a/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart index e28262d1fcb..af05b1bc610 100644 --- a/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart @@ -24,26 +24,18 @@ class ButtonDemo extends StatelessWidget { @override Widget build(BuildContext context) { - Widget? buttons; - switch (type) { - case ButtonDemoType.text: - buttons = _TextButtonDemo(); - case ButtonDemoType.elevated: - buttons = _ElevatedButtonDemo(); - case ButtonDemoType.outlined: - buttons = _OutlinedButtonDemo(); - case ButtonDemoType.toggle: - buttons = _ToggleButtonsDemo(); - case ButtonDemoType.floating: - buttons = _FloatingActionButtonDemo(); - } - return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text(_title(context)), ), - body: buttons, + body: switch (type) { + ButtonDemoType.text => _TextButtonDemo(), + ButtonDemoType.elevated => _ElevatedButtonDemo(), + ButtonDemoType.outlined => _OutlinedButtonDemo(), + ButtonDemoType.toggle => _ToggleButtonsDemo(), + ButtonDemoType.floating => _FloatingActionButtonDemo(), + }, ); } } diff --git a/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart index 48f842e88cc..86a277a452e 100644 --- a/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart @@ -26,24 +26,17 @@ class ChipDemo extends StatelessWidget { @override Widget build(BuildContext context) { - Widget? buttons; - switch (type) { - case ChipDemoType.action: - buttons = _ActionChipDemo(); - case ChipDemoType.choice: - buttons = _ChoiceChipDemo(); - case ChipDemoType.filter: - buttons = _FilterChipDemo(); - case ChipDemoType.input: - buttons = _InputChipDemo(); - } - return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text(_title(context)), ), - body: buttons, + body: switch (type) { + ChipDemoType.action => _ActionChipDemo(), + ChipDemoType.choice => _ChoiceChipDemo(), + ChipDemoType.filter => _FilterChipDemo(), + ChipDemoType.input => _InputChipDemo(), + }, ); } } diff --git a/dev/integration_tests/new_gallery/lib/demos/material/divider_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/divider_demo.dart index 1fc13d23aba..b078cda22aa 100644 --- a/dev/integration_tests/new_gallery/lib/demos/material/divider_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/material/divider_demo.dart @@ -11,33 +11,23 @@ class DividerDemo extends StatelessWidget { final DividerDemoType type; - String _title(BuildContext context) { - switch (type) { - case DividerDemoType.horizontal: - return GalleryLocalizations.of(context)!.demoDividerTitle; - case DividerDemoType.vertical: - return GalleryLocalizations.of(context)!.demoVerticalDividerTitle; - } - } - @override Widget build(BuildContext context) { - late Widget dividers; - switch (type) { - case DividerDemoType.horizontal: - dividers = _HorizontalDividerDemo(); - case DividerDemoType.vertical: - dividers = _VerticalDividerDemo(); - } - + final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text( - _title(context), + switch (type) { + DividerDemoType.horizontal => localizations.demoDividerTitle, + DividerDemoType.vertical => localizations.demoVerticalDividerTitle, + }, ), ), - body: dividers, + body: switch (type) { + DividerDemoType.horizontal => _HorizontalDividerDemo(), + DividerDemoType.vertical => _VerticalDividerDemo(), + }, ); } } diff --git a/dev/integration_tests/new_gallery/lib/demos/material/picker_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/picker_demo.dart index 26bfba5d3f5..a9b899bde56 100644 --- a/dev/integration_tests/new_gallery/lib/demos/material/picker_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/material/picker_demo.dart @@ -172,14 +172,12 @@ class _PickerDemoState extends State with RestorationMixin { } String get _labelText { - switch (widget.type) { - case PickerDemoType.date: - return DateFormat.yMMMd().format(_fromDate.value); - case PickerDemoType.time: - return _fromTime.value.format(context); - case PickerDemoType.range: - return '${DateFormat.yMMMd().format(_startDate.value)} - ${DateFormat.yMMMd().format(_endDate.value)}'; - } + final DateFormat yMMMd = DateFormat.yMMMd(); + return switch (widget.type) { + PickerDemoType.date => yMMMd.format(_fromDate.value), + PickerDemoType.time => _fromTime.value.format(context), + PickerDemoType.range => '${yMMMd.format(_startDate.value)} - ${yMMMd.format(_endDate.value)}', + }; } @override @@ -199,16 +197,11 @@ class _PickerDemoState extends State with RestorationMixin { Text(_labelText), const SizedBox(height: 16), ElevatedButton( - onPressed: () { - switch (widget.type) { - case PickerDemoType.date: - _restorableDatePickerRouteFuture.present(); - case PickerDemoType.time: - _restorableTimePickerRouteFuture.present(); - case PickerDemoType.range: - _restorableDateRangePickerRouteFuture.present(); - } - }, + onPressed: () => switch (widget.type) { + PickerDemoType.date => _restorableDatePickerRouteFuture, + PickerDemoType.time => _restorableTimePickerRouteFuture, + PickerDemoType.range => _restorableDateRangePickerRouteFuture, + }.present(), child: Text( GalleryLocalizations.of(context)!.demoPickersShowPicker, ), diff --git a/dev/integration_tests/new_gallery/lib/demos/material/selection_controls_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/selection_controls_demo.dart index 7d1ca8a6d3c..72d5f347b32 100644 --- a/dev/integration_tests/new_gallery/lib/demos/material/selection_controls_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/material/selection_controls_demo.dart @@ -27,22 +27,16 @@ class SelectionControlsDemo extends StatelessWidget { @override Widget build(BuildContext context) { - Widget? controls; - switch (type) { - case SelectionControlsDemoType.checkbox: - controls = _CheckboxDemo(); - case SelectionControlsDemoType.radio: - controls = _RadioDemo(); - case SelectionControlsDemoType.switches: - controls = _SwitchDemo(); - } - return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text(_title(context)), ), - body: controls, + body: switch (type) { + SelectionControlsDemoType.checkbox => _CheckboxDemo(), + SelectionControlsDemoType.radio => _RadioDemo(), + SelectionControlsDemoType.switches => _SwitchDemo(), + }, ); } } diff --git a/dev/integration_tests/new_gallery/lib/demos/material/sliders_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/sliders_demo.dart index a7ed6d8ebd3..252b3feff60 100644 --- a/dev/integration_tests/new_gallery/lib/demos/material/sliders_demo.dart +++ b/dev/integration_tests/new_gallery/lib/demos/material/sliders_demo.dart @@ -27,21 +27,16 @@ class SlidersDemo extends StatelessWidget { @override Widget build(BuildContext context) { - Widget sliders; - switch (type) { - case SlidersDemoType.sliders: - sliders = _Sliders(); - case SlidersDemoType.rangeSliders: - sliders = _RangeSliders(); - case SlidersDemoType.customSliders: - sliders = _CustomSliders(); - } return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text(_title(context)), ), - body: sliders, + body: switch (type) { + SlidersDemoType.sliders => _Sliders(), + SlidersDemoType.rangeSliders => _RangeSliders(), + SlidersDemoType.customSliders => _CustomSliders(), + }, ); } } @@ -345,25 +340,13 @@ class _CustomRangeThumbShape extends RangeSliderThumbShape { ); final double size = _thumbSize * sizeTween.evaluate(enableAnimation); - Path thumbPath; - switch (textDirection!) { - case TextDirection.rtl: - switch (thumb!) { - case Thumb.start: - thumbPath = _rightTriangle(size, center); - case Thumb.end: - thumbPath = _leftTriangle(size, center); - } - case TextDirection.ltr: - switch (thumb!) { - case Thumb.start: - thumbPath = _leftTriangle(size, center); - case Thumb.end: - thumbPath = _rightTriangle(size, center); - } - } canvas.drawPath( - thumbPath, + switch ((textDirection!, thumb!)) { + (TextDirection.rtl, Thumb.start) => _rightTriangle(size, center), + (TextDirection.rtl, Thumb.end) => _leftTriangle(size, center), + (TextDirection.ltr, Thumb.start) => _leftTriangle(size, center), + (TextDirection.ltr, Thumb.end) => _rightTriangle(size, center), + }, Paint()..color = colorTween.evaluate(enableAnimation)!, ); } diff --git a/dev/integration_tests/new_gallery/lib/studies/shrine/supplemental/cut_corners_border.dart b/dev/integration_tests/new_gallery/lib/studies/shrine/supplemental/cut_corners_border.dart index cdebec35de5..7dcfff1a3e3 100644 --- a/dev/integration_tests/new_gallery/lib/studies/shrine/supplemental/cut_corners_border.dart +++ b/dev/integration_tests/new_gallery/lib/studies/shrine/supplemental/cut_corners_border.dart @@ -102,23 +102,12 @@ class CutCornersBorder extends OutlineInputBorder { if (gapStart == null || gapExtent <= 0 || gapPercentage == 0) { canvas.drawPath(_notchedCornerPath(outer.middleRect), paint); } else { - final double? extent = lerpDouble(0.0, gapExtent + gapPadding * 2, gapPercentage); - switch (textDirection!) { - case TextDirection.rtl: - { - final Path path = _notchedCornerPath( - outer.middleRect, gapStart + gapPadding - extent!, extent); - canvas.drawPath(path, paint); - break; - } - case TextDirection.ltr: - { - final Path path = _notchedCornerPath( - outer.middleRect, gapStart - gapPadding, extent!); - canvas.drawPath(path, paint); - break; - } - } + final double extent = lerpDouble(0.0, gapExtent + gapPadding * 2, gapPercentage)!; + final double start = switch (textDirection!) { + TextDirection.rtl => gapStart + gapPadding - extent, + TextDirection.ltr => gapStart - gapPadding, + }; + canvas.drawPath(_notchedCornerPath(outer.middleRect, start, extent), paint); } } } diff --git a/dev/tools/localization/bin/gen_localizations.dart b/dev/tools/localization/bin/gen_localizations.dart index af70e236895..11a4356d421 100644 --- a/dev/tools/localization/bin/gen_localizations.dart +++ b/dev/tools/localization/bin/gen_localizations.dart @@ -400,17 +400,12 @@ $factoryDeclaration /// /// Used by [generateGetter] below. String generateType(Map? attributes) { - bool optional = false; - String type = 'String'; - if (attributes != null) { - optional = attributes.containsKey('optional'); - switch (attributes['x-flutter-type'] as String?) { - case 'icuShortTimePattern': - type = 'TimeOfDayFormat'; - case 'scriptCategory': - type = 'ScriptCategory'; - } - } + final bool optional = attributes?.containsKey('optional') ?? false; + final String type = switch (attributes?['x-flutter-type']) { + 'icuShortTimePattern' => 'TimeOfDayFormat', + 'scriptCategory' => 'ScriptCategory', + _ => 'String', + }; return type + (optional ? '?' : ''); } diff --git a/examples/api/lib/painting/axis_direction/axis_direction.0.dart b/examples/api/lib/painting/axis_direction/axis_direction.0.dart index e48fd0918e1..2323bd85811 100644 --- a/examples/api/lib/painting/axis_direction/axis_direction.0.dart +++ b/examples/api/lib/painting/axis_direction/axis_direction.0.dart @@ -35,33 +35,17 @@ class _MyWidgetState extends State { AxisDirection _axisDirection = AxisDirection.down; Widget _getArrows() { - final Widget arrow; - switch (_axisDirection) { - case AxisDirection.up: - arrow = const Icon(Icons.arrow_upward_rounded); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.down: - arrow = const Icon(Icons.arrow_downward_rounded); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.left: - arrow = const Icon(Icons.arrow_back_rounded); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.right: - arrow = const Icon(Icons.arrow_forward_rounded); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - } + final Widget arrow = switch (_axisDirection) { + AxisDirection.up => const Icon(Icons.arrow_upward_rounded), + AxisDirection.down => const Icon(Icons.arrow_downward_rounded), + AxisDirection.left => const Icon(Icons.arrow_back_rounded), + AxisDirection.right => const Icon(Icons.arrow_forward_rounded), + }; + return Flex( + direction: flipAxis(axisDirectionToAxis(_axisDirection)), + mainAxisAlignment: MainAxisAlignment.center, + children: [arrow, arrow], + ); } void _onAxisDirectionChanged(AxisDirection? axisDirection) { diff --git a/examples/api/lib/rendering/growth_direction/growth_direction.0.dart b/examples/api/lib/rendering/growth_direction/growth_direction.0.dart index d5ca03e434f..50e41b4f308 100644 --- a/examples/api/lib/rendering/growth_direction/growth_direction.0.dart +++ b/examples/api/lib/rendering/growth_direction/growth_direction.0.dart @@ -37,33 +37,17 @@ class _MyWidgetState extends State { AxisDirection _axisDirection = AxisDirection.down; Widget _getArrows(AxisDirection axisDirection) { - final Widget arrow; - switch (axisDirection) { - case AxisDirection.up: - arrow = const Icon(Icons.arrow_upward_rounded); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.down: - arrow = const Icon(Icons.arrow_downward_rounded); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.left: - arrow = const Icon(Icons.arrow_back_rounded); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.right: - arrow = const Icon(Icons.arrow_forward_rounded); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - } + final Widget arrow = switch (axisDirection) { + AxisDirection.up => const Icon(Icons.arrow_upward_rounded), + AxisDirection.down => const Icon(Icons.arrow_downward_rounded), + AxisDirection.left => const Icon(Icons.arrow_back_rounded), + AxisDirection.right => const Icon(Icons.arrow_forward_rounded), + }; + return Flex( + direction: flipAxis(axisDirectionToAxis(axisDirection)), + mainAxisAlignment: MainAxisAlignment.center, + children: [arrow, arrow], + ); } void _onAxisDirectionChanged(AxisDirection? axisDirection) { diff --git a/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart b/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart index 6eb3c9c9130..dbec435fb9a 100644 --- a/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart +++ b/examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart @@ -37,33 +37,17 @@ class _MyWidgetState extends State { AxisDirection _axisDirection = AxisDirection.down; Widget _getArrows() { - final Widget arrow; - switch (_axisDirection) { - case AxisDirection.up: - arrow = const Icon(Icons.arrow_upward_rounded); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.down: - arrow = const Icon(Icons.arrow_downward_rounded); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.left: - arrow = const Icon(Icons.arrow_back_rounded); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - case AxisDirection.right: - arrow = const Icon(Icons.arrow_forward_rounded); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [arrow, arrow], - ); - } + final Widget arrow = switch (_axisDirection) { + AxisDirection.up => const Icon(Icons.arrow_upward_rounded), + AxisDirection.down => const Icon(Icons.arrow_downward_rounded), + AxisDirection.left => const Icon(Icons.arrow_back_rounded), + AxisDirection.right => const Icon(Icons.arrow_forward_rounded), + }; + return Flex( + direction: flipAxis(axisDirectionToAxis(_axisDirection)), + mainAxisAlignment: MainAxisAlignment.center, + children: [arrow, arrow], + ); } void _onAxisDirectionChanged(AxisDirection? axisDirection) { diff --git a/examples/api/lib/widgets/overlay/overlay.0.dart b/examples/api/lib/widgets/overlay/overlay.0.dart index 2a69e58f3d8..d2a3ad77005 100644 --- a/examples/api/lib/widgets/overlay/overlay.0.dart +++ b/examples/api/lib/widgets/overlay/overlay.0.dart @@ -39,6 +39,24 @@ class _OverlayExampleState extends State { assert(overlayEntry == null); + Widget builder(BuildContext context) { + final (String label, Color? color) = switch (currentPageIndex) { + 0 => ('Explore page', Colors.red), + 1 => ('Commute page', Colors.green), + 2 => ('Saved page', Colors.orange), + _ => ('No page selected.', null), + }; + if (color == null) { + return Text(label); + } + return Column( + children: [ + Text(label, style: TextStyle(color: color)), + Icon(Icons.arrow_downward, color: color), + ], + ); + } + overlayEntry = OverlayEntry( // Create a new OverlayEntry. builder: (BuildContext context) { @@ -58,57 +76,7 @@ class _OverlayExampleState extends State { mainAxisSize: MainAxisSize.min, children: [ const Text('Tap here for'), - Builder(builder: (BuildContext context) { - switch (currentPageIndex) { - case 0: - return const Column( - children: [ - Text( - 'Explore page', - style: TextStyle( - color: Colors.red, - ), - ), - Icon( - Icons.arrow_downward, - color: Colors.red, - ), - ], - ); - case 1: - return const Column( - children: [ - Text( - 'Commute page', - style: TextStyle( - color: Colors.green, - ), - ), - Icon( - Icons.arrow_downward, - color: Colors.green, - ), - ], - ); - case 2: - return const Column( - children: [ - Text( - 'Saved page', - style: TextStyle( - color: Colors.orange, - ), - ), - Icon( - Icons.arrow_downward, - color: Colors.orange, - ), - ], - ); - default: - return const Text('No page selected.'); - } - }), + Builder(builder: builder), SizedBox( width: MediaQuery.of(context).size.width / 3, height: 80.0, diff --git a/packages/flutter/lib/src/cupertino/scrollbar.dart b/packages/flutter/lib/src/cupertino/scrollbar.dart index 764c76689cb..45e9a7f2c72 100644 --- a/packages/flutter/lib/src/cupertino/scrollbar.dart +++ b/packages/flutter/lib/src/cupertino/scrollbar.dart @@ -199,17 +199,12 @@ class _CupertinoScrollbarState extends RawScrollbarState { } _thicknessAnimationController.reverse(); super.handleThumbPressEnd(localPosition, velocity); - switch (direction) { - case Axis.vertical: - if (velocity.pixelsPerSecond.dy.abs() < 10 && - (localPosition.dy - _pressStartAxisPosition).abs() > 0) { - HapticFeedback.mediumImpact(); - } - case Axis.horizontal: - if (velocity.pixelsPerSecond.dx.abs() < 10 && - (localPosition.dx - _pressStartAxisPosition).abs() > 0) { - HapticFeedback.mediumImpact(); - } + final (double axisPosition, double axisVelocity) = switch (direction) { + Axis.horizontal => (localPosition.dx, velocity.pixelsPerSecond.dx), + Axis.vertical => (localPosition.dy, velocity.pixelsPerSecond.dy), + }; + if (axisPosition != _pressStartAxisPosition && axisVelocity.abs() < 10) { + HapticFeedback.mediumImpact(); } } diff --git a/packages/flutter/lib/src/material/drawer.dart b/packages/flutter/lib/src/material/drawer.dart index f8f09437ec0..0b02d193575 100644 --- a/packages/flutter/lib/src/material/drawer.dart +++ b/packages/flutter/lib/src/material/drawer.dart @@ -560,20 +560,17 @@ class DrawerControllerState extends State with SingleTickerPro bool _previouslyOpened = false; + int get _directionFactor { + return switch ((Directionality.of(context), widget.alignment)) { + (TextDirection.rtl, DrawerAlignment.start) => -1, + (TextDirection.rtl, DrawerAlignment.end) => 1, + (TextDirection.ltr, DrawerAlignment.start) => 1, + (TextDirection.ltr, DrawerAlignment.end) => -1, + }; + } + void _move(DragUpdateDetails details) { - double delta = details.primaryDelta! / _width; - switch (widget.alignment) { - case DrawerAlignment.start: - break; - case DrawerAlignment.end: - delta = -delta; - } - switch (Directionality.of(context)) { - case TextDirection.rtl: - _controller.value -= delta; - case TextDirection.ltr: - _controller.value += delta; - } + _controller.value += details.primaryDelta! / _width * _directionFactor; final bool opened = _controller.value > 0.5; if (opened != _previouslyOpened && widget.drawerCallback != null) { @@ -586,22 +583,12 @@ class DrawerControllerState extends State with SingleTickerPro if (_controller.isDismissed) { return; } - if (details.velocity.pixelsPerSecond.dx.abs() >= _kMinFlingVelocity) { - double visualVelocity = details.velocity.pixelsPerSecond.dx / _width; - switch (widget.alignment) { - case DrawerAlignment.start: - break; - case DrawerAlignment.end: - visualVelocity = -visualVelocity; - } - switch (Directionality.of(context)) { - case TextDirection.rtl: - _controller.fling(velocity: -visualVelocity); - widget.drawerCallback?.call(visualVelocity < 0.0); - case TextDirection.ltr: - _controller.fling(velocity: visualVelocity); - widget.drawerCallback?.call(visualVelocity > 0.0); - } + final double xVelocity = details.velocity.pixelsPerSecond.dx; + if (xVelocity.abs() >= _kMinFlingVelocity) { + final double visualVelocity = xVelocity / _width * _directionFactor; + + _controller.fling(velocity: visualVelocity); + widget.drawerCallback?.call(visualVelocity > 0.0); } else if (_controller.value < 0.5) { close(); } else { diff --git a/packages/flutter/lib/src/material/input_border.dart b/packages/flutter/lib/src/material/input_border.dart index 84d73aebeb9..bde98aebbde 100644 --- a/packages/flutter/lib/src/material/input_border.dart +++ b/packages/flutter/lib/src/material/input_border.dart @@ -539,15 +539,12 @@ class OutlineInputBorder extends InputBorder { canvas.drawRRect(center, paint); } else { final double extent = lerpDouble(0.0, gapExtent + gapPadding * 2.0, gapPercentage)!; - switch (textDirection!) { - case TextDirection.rtl: - final Path path = _gapBorderPath(canvas, center, math.max(0.0, gapStart + gapPadding - extent), extent); - canvas.drawPath(path, paint); - - case TextDirection.ltr: - final Path path = _gapBorderPath(canvas, center, math.max(0.0, gapStart - gapPadding), extent); - canvas.drawPath(path, paint); - } + final double start = switch (textDirection!) { + TextDirection.rtl => gapStart + gapPadding - extent, + TextDirection.ltr => gapStart - gapPadding, + }; + final Path path = _gapBorderPath(canvas, center, math.max(0.0, start), extent); + canvas.drawPath(path, paint); } } diff --git a/packages/flutter/lib/src/material/stepper.dart b/packages/flutter/lib/src/material/stepper.dart index 4b3f3a604e1..dfee7ce058c 100644 --- a/packages/flutter/lib/src/material/stepper.dart +++ b/packages/flutter/lib/src/material/stepper.dart @@ -456,36 +456,20 @@ class _StepperState extends State with TickerProviderStateMixin { Widget _buildCircleChild(int index, bool oldState) { final StepState state = oldState ? _oldStates[index]! : widget.steps[index].state; - final bool isDarkActive = _isDark() && widget.steps[index].isActive; - final Widget? icon = widget.stepIconBuilder?.call(index, state); - if (icon != null) { + if (widget.stepIconBuilder?.call(index, state) case final Widget icon) { return icon; } TextStyle? textStyle = _stepStyle(index)?.indexStyle; + final bool isDarkActive = _isDark() && widget.steps[index].isActive; + final Color iconColor = isDarkActive ? _kCircleActiveDark : _kCircleActiveLight; textStyle ??= isDarkActive ? _kStepStyle.copyWith(color: Colors.black87) : _kStepStyle; - switch (state) { - case StepState.indexed: - case StepState.disabled: - return Text( - '${index + 1}', - style: textStyle, - ); - case StepState.editing: - return Icon( - Icons.edit, - color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight, - size: 18.0, - ); - case StepState.complete: - return Icon( - Icons.check, - color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight, - size: 18.0, - ); - case StepState.error: - return const Center(child: Text('!', style: _kStepStyle)); - } + return switch (state) { + StepState.indexed || StepState.disabled => Text('${index + 1}', style: textStyle), + StepState.editing => Icon(Icons.edit, color: iconColor, size: 18.0), + StepState.complete => Icon(Icons.check, color: iconColor, size: 18.0), + StepState.error => const Center(child: Text('!', style: _kStepStyle)), + }; } Color _circleColor(int index) { diff --git a/packages/flutter/lib/src/widgets/focus_traversal.dart b/packages/flutter/lib/src/widgets/focus_traversal.dart index 45059e53d85..b27de547eca 100644 --- a/packages/flutter/lib/src/widgets/focus_traversal.dart +++ b/packages/flutter/lib/src/widgets/focus_traversal.dart @@ -705,25 +705,14 @@ mixin DirectionalFocusTraversalPolicyMixin on FocusTraversalPolicy { @override FocusNode? findFirstFocusInDirection(FocusNode currentNode, TraversalDirection direction) { - switch (direction) { - case TraversalDirection.up: - // Find the bottom-most node so we can go up from there. - return _sortAndFindInitial(currentNode, vertical: true, first: false); - case TraversalDirection.down: - // Find the top-most node so we can go down from there. - return _sortAndFindInitial(currentNode, vertical: true, first: true); - case TraversalDirection.left: - // Find the right-most node so we can go left from there. - return _sortAndFindInitial(currentNode, vertical: false, first: false); - case TraversalDirection.right: - // Find the left-most node so we can go right from there. - return _sortAndFindInitial(currentNode, vertical: false, first: true); - } - } - - FocusNode? _sortAndFindInitial(FocusNode currentNode, {required bool vertical, required bool first}) { final Iterable nodes = currentNode.nearestScope!.traversalDescendants; final List sorted = nodes.toList(); + final (bool vertical, bool first) = switch (direction) { + TraversalDirection.up => (true, false), // Start with the bottom-most node. + TraversalDirection.down => (true, true), // Start with the topmost node. + TraversalDirection.left => (false, false), // Start with the rightmost node. + TraversalDirection.right => (false, true), // Start with the leftmost node. + }; mergeSort(sorted, compare: (FocusNode a, FocusNode b) { if (vertical) { if (first) { @@ -740,11 +729,7 @@ mixin DirectionalFocusTraversalPolicyMixin on FocusTraversalPolicy { } }); - if (sorted.isNotEmpty) { - return sorted.first; - } - - return null; + return sorted.firstOrNull; } static int _verticalCompare(Offset target, Offset a, Offset b) { @@ -849,17 +834,11 @@ mixin DirectionalFocusTraversalPolicyMixin on FocusTraversalPolicy { Iterable nodes, ) { assert(direction == TraversalDirection.left || direction == TraversalDirection.right); - final Iterable filtered; - switch (direction) { - case TraversalDirection.left: - filtered = nodes.where((FocusNode node) => node.rect != target && node.rect.center.dx <= target.left); - case TraversalDirection.right: - filtered = nodes.where((FocusNode node) => node.rect != target && node.rect.center.dx >= target.right); - case TraversalDirection.up: - case TraversalDirection.down: - throw ArgumentError('Invalid direction $direction'); - } - final List sorted = filtered.toList(); + final List sorted = nodes.where(switch (direction) { + TraversalDirection.left => (FocusNode node) => node.rect != target && node.rect.center.dx <= target.left, + TraversalDirection.right => (FocusNode node) => node.rect != target && node.rect.center.dx >= target.right, + TraversalDirection.up || TraversalDirection.down => throw ArgumentError('Invalid direction $direction'), + }).toList(); // Sort all nodes from left to right. mergeSort(sorted, compare: (FocusNode a, FocusNode b) => a.rect.center.dx.compareTo(b.rect.center.dx)); return sorted; @@ -874,17 +853,11 @@ mixin DirectionalFocusTraversalPolicyMixin on FocusTraversalPolicy { Iterable nodes, ) { assert(direction == TraversalDirection.up || direction == TraversalDirection.down); - final Iterable filtered; - switch (direction) { - case TraversalDirection.up: - filtered = nodes.where((FocusNode node) => node.rect != target && node.rect.center.dy <= target.top); - case TraversalDirection.down: - filtered = nodes.where((FocusNode node) => node.rect != target && node.rect.center.dy >= target.bottom); - case TraversalDirection.left: - case TraversalDirection.right: - throw ArgumentError('Invalid direction $direction'); - } - final List sorted = filtered.toList(); + final List sorted = nodes.where(switch (direction) { + TraversalDirection.up => (FocusNode node) => node.rect != target && node.rect.center.dy <= target.top, + TraversalDirection.down => (FocusNode node) => node.rect != target && node.rect.center.dy >= target.bottom, + TraversalDirection.left || TraversalDirection.right => throw ArgumentError('Invalid direction $direction'), + }).toList(); mergeSort(sorted, compare: (FocusNode a, FocusNode b) => a.rect.center.dy.compareTo(b.rect.center.dy)); return sorted; }