mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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:
parent
454dd7e29c
commit
870c5541c3
@ -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) {
|
||||
|
@ -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} '
|
||||
|
@ -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),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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)!,
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ? '?' : '');
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user