switch expressions: finale (#148711)

### fixes #136139

<br>

<details open> <summary><b>getting sentimental in the PR description</b> (click to collapse)<br><br></summary>

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

<br>

</details>

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 🙂
This commit is contained in:
Nate 2024-05-21 10:18:05 -06:00 committed by GitHub
parent 454dd7e29c
commit 870c5541c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 214 additions and 466 deletions

View File

@ -47,20 +47,14 @@ class StocksAppState extends State<StocksApp> {
}
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<dynamic>? _getRoute(RouteSettings settings) {

View File

@ -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} '

View File

@ -377,22 +377,17 @@ class _CardsDemoState extends State<CardsDemo> {
child: ListView(
primary: true,
padding: const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0),
children: destinations.map<Widget>((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: <Widget>[
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),
},
),
],
),
),
);

View File

@ -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);
}
}
}

View File

@ -298,37 +298,26 @@ class _CupertinoAlertDemoState extends State<CupertinoAlertDemo>
),
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: <Widget>[
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,
),

View File

@ -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(),
},
);
}
}

View File

@ -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(),
},
);
}
}

View File

@ -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(),
},
);
}
}

View File

@ -172,14 +172,12 @@ class _PickerDemoState extends State<PickerDemo> 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<PickerDemo> 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,
),

View File

@ -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(),
},
);
}
}

View File

@ -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)!,
);
}

View File

@ -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);
}
}
}

View File

@ -400,17 +400,12 @@ $factoryDeclaration
///
/// Used by [generateGetter] below.
String generateType(Map<String, dynamic>? 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 ? '?' : '');
}

View File

@ -35,33 +35,17 @@ class _MyWidgetState extends State<MyWidget> {
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: <Widget>[arrow, arrow],
);
case AxisDirection.down:
arrow = const Icon(Icons.arrow_downward_rounded);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[arrow, arrow],
);
case AxisDirection.left:
arrow = const Icon(Icons.arrow_back_rounded);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[arrow, arrow],
);
case AxisDirection.right:
arrow = const Icon(Icons.arrow_forward_rounded);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[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: <Widget>[arrow, arrow],
);
}
void _onAxisDirectionChanged(AxisDirection? axisDirection) {

View File

@ -37,33 +37,17 @@ class _MyWidgetState extends State<MyWidget> {
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: <Widget>[arrow, arrow],
);
case AxisDirection.down:
arrow = const Icon(Icons.arrow_downward_rounded);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[arrow, arrow],
);
case AxisDirection.left:
arrow = const Icon(Icons.arrow_back_rounded);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[arrow, arrow],
);
case AxisDirection.right:
arrow = const Icon(Icons.arrow_forward_rounded);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[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: <Widget>[arrow, arrow],
);
}
void _onAxisDirectionChanged(AxisDirection? axisDirection) {

View File

@ -37,33 +37,17 @@ class _MyWidgetState extends State<MyWidget> {
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: <Widget>[arrow, arrow],
);
case AxisDirection.down:
arrow = const Icon(Icons.arrow_downward_rounded);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[arrow, arrow],
);
case AxisDirection.left:
arrow = const Icon(Icons.arrow_back_rounded);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[arrow, arrow],
);
case AxisDirection.right:
arrow = const Icon(Icons.arrow_forward_rounded);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[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: <Widget>[arrow, arrow],
);
}
void _onAxisDirectionChanged(AxisDirection? axisDirection) {

View File

@ -39,6 +39,24 @@ class _OverlayExampleState extends State<OverlayExample> {
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: <Widget>[
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<OverlayExample> {
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Tap here for'),
Builder(builder: (BuildContext context) {
switch (currentPageIndex) {
case 0:
return const Column(
children: <Widget>[
Text(
'Explore page',
style: TextStyle(
color: Colors.red,
),
),
Icon(
Icons.arrow_downward,
color: Colors.red,
),
],
);
case 1:
return const Column(
children: <Widget>[
Text(
'Commute page',
style: TextStyle(
color: Colors.green,
),
),
Icon(
Icons.arrow_downward,
color: Colors.green,
),
],
);
case 2:
return const Column(
children: <Widget>[
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,

View File

@ -199,17 +199,12 @@ class _CupertinoScrollbarState extends RawScrollbarState<CupertinoScrollbar> {
}
_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();
}
}

View File

@ -560,20 +560,17 @@ class DrawerControllerState extends State<DrawerController> 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<DrawerController> 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 {

View File

@ -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);
}
}

View File

@ -456,36 +456,20 @@ class _StepperState extends State<Stepper> 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) {

View File

@ -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<FocusNode> nodes = currentNode.nearestScope!.traversalDescendants;
final List<FocusNode> 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<FocusNode>(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<FocusNode> nodes,
) {
assert(direction == TraversalDirection.left || direction == TraversalDirection.right);
final Iterable<FocusNode> 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<FocusNode> sorted = filtered.toList();
final List<FocusNode> 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<FocusNode>(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<FocusNode> nodes,
) {
assert(direction == TraversalDirection.up || direction == TraversalDirection.down);
final Iterable<FocusNode> 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<FocusNode> sorted = filtered.toList();
final List<FocusNode> 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<FocusNode>(sorted, compare: (FocusNode a, FocusNode b) => a.rect.center.dy.compareTo(b.rect.center.dy));
return sorted;
}