-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Similar to YaruCheckButton & YaruRadioButton but with a leading Switch.
- Loading branch information
Showing
25 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:yaru_widgets/yaru_widgets.dart'; | ||
|
||
class SwitchButtonPage extends StatefulWidget { | ||
const SwitchButtonPage({super.key}); | ||
|
||
@override | ||
_SwitchButtonPageState createState() => _SwitchButtonPageState(); | ||
} | ||
|
||
class _SwitchButtonPageState extends State<SwitchButtonPage> { | ||
final _values = List.generate(3, (i) => i % 2 == 0); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return ListView( | ||
padding: const EdgeInsets.all(kYaruPagePadding), | ||
children: [ | ||
for (var i = 0; i < _values.length; ++i) ...[ | ||
YaruSwitchButton( | ||
value: _values[i], | ||
onChanged: (v) => setState(() => _values[i] = v), | ||
title: const Text('YaruSwitchButton'), | ||
), | ||
const SizedBox(height: 10), | ||
], | ||
], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
import 'yaru_toggle_button.dart'; | ||
|
||
/// A desktop style switch button with an interactive label. | ||
class YaruSwitchButton extends StatelessWidget { | ||
/// Creates a new switch button. | ||
const YaruSwitchButton({ | ||
super.key, | ||
required this.value, | ||
required this.onChanged, | ||
required this.title, | ||
this.subtitle, | ||
this.contentPadding, | ||
this.autofocus = false, | ||
this.focusNode, | ||
}); | ||
|
||
/// See [Switch.value] | ||
final bool value; | ||
|
||
/// See [Switch.onChanged] | ||
final ValueChanged<bool>? onChanged; | ||
|
||
/// See [YaruToggleButton.title] | ||
final Widget title; | ||
|
||
/// See [YaruToggleButton.subtitle] | ||
final Widget? subtitle; | ||
|
||
/// See [YaruToggleButton.contentPadding] | ||
final EdgeInsetsGeometry? contentPadding; | ||
|
||
/// See [Switch.focusNode]. | ||
final FocusNode? focusNode; | ||
|
||
/// See [Switch.autofocus]. | ||
final bool autofocus; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return YaruToggleButton( | ||
title: title, | ||
subtitle: subtitle, | ||
contentPadding: contentPadding, | ||
leading: SizedBox( | ||
width: kMinInteractiveDimension + 8, | ||
height: kMinInteractiveDimension - 8, | ||
child: Center( | ||
child: Switch( | ||
value: value, | ||
onChanged: onChanged, | ||
focusNode: focusNode, | ||
autofocus: autofocus, | ||
), | ||
), | ||
), | ||
onToggled: onChanged != null ? () => onChanged!(!value) : null, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
import 'package:flutter/gestures.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/rendering.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:yaru_widgets/yaru_widgets.dart'; | ||
|
||
import '../yaru_golden_tester.dart'; | ||
|
||
void main() { | ||
testWidgets('contains switch and labels', (tester) async { | ||
Widget builder({required Widget title, required Widget? subtitle}) { | ||
return MaterialApp( | ||
home: Scaffold( | ||
body: YaruSwitchButton( | ||
title: title, | ||
subtitle: subtitle, | ||
value: false, | ||
onChanged: (_) {}, | ||
), | ||
), | ||
); | ||
} | ||
|
||
await tester | ||
.pumpWidget(builder(title: const Text('title'), subtitle: null)); | ||
expect(find.text('title'), findsOneWidget); | ||
expect(find.text('subtitle'), findsNothing); | ||
expect(find.byType(Switch), findsOneWidget); | ||
|
||
await tester.pumpWidget( | ||
builder(title: const Text('title'), subtitle: const Text('subtitle')), | ||
); | ||
expect(find.text('title'), findsOneWidget); | ||
expect(find.text('subtitle'), findsOneWidget); | ||
expect(find.byType(Switch), findsOneWidget); | ||
}); | ||
|
||
testWidgets('the labels react to taps', (tester) async { | ||
bool? changedValue; | ||
Widget builder({required bool initialValue}) { | ||
return MaterialApp( | ||
home: Scaffold( | ||
body: YaruSwitchButton( | ||
title: const Text('title'), | ||
subtitle: const Text('subtitle'), | ||
value: initialValue, | ||
onChanged: (v) => changedValue = v, | ||
), | ||
), | ||
); | ||
} | ||
|
||
await tester.pumpWidget(builder(initialValue: false)); | ||
await tester.tap(find.text('title')); | ||
expect(changedValue, isTrue); | ||
|
||
await tester.pumpWidget(builder(initialValue: false)); | ||
await tester.tap(find.text('subtitle')); | ||
expect(changedValue, isTrue); | ||
|
||
await tester.pumpWidget(builder(initialValue: true)); | ||
await tester.tap(find.text('title')); | ||
expect(changedValue, isFalse); | ||
|
||
await tester.pumpWidget(builder(initialValue: true)); | ||
await tester.tap(find.text('subtitle')); | ||
expect(changedValue, isFalse); | ||
}); | ||
|
||
testWidgets('mouse cursor changes depending on the state', (tester) async { | ||
await tester.pumpWidget( | ||
MaterialApp( | ||
home: Scaffold( | ||
body: Column( | ||
children: [ | ||
YaruSwitchButton( | ||
title: const Text('enabled'), | ||
value: false, | ||
onChanged: (_) {}, | ||
), | ||
const YaruSwitchButton( | ||
title: Text('disabled'), | ||
value: false, | ||
onChanged: null, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
|
||
final gesture = | ||
await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1); | ||
await gesture.addPointer(location: Offset.zero); | ||
addTearDown(gesture.removePointer); | ||
|
||
await gesture | ||
.moveTo(tester.getCenter(find.widgetWithText(MouseRegion, 'enabled'))); | ||
await tester.pump(); | ||
expect( | ||
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), | ||
SystemMouseCursors.click, | ||
); | ||
|
||
await gesture | ||
.moveTo(tester.getCenter(find.widgetWithText(MouseRegion, 'disabled'))); | ||
await tester.pump(); | ||
expect( | ||
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), | ||
SystemMouseCursors.basic, | ||
); | ||
}); | ||
|
||
testWidgets('text color changes depending on the state', (tester) async { | ||
await tester.pumpWidget( | ||
MaterialApp( | ||
home: Scaffold( | ||
body: Column( | ||
children: [ | ||
YaruSwitchButton( | ||
title: const Text('enabled'), | ||
value: false, | ||
onChanged: (_) {}, | ||
), | ||
const YaruSwitchButton( | ||
title: Text('disabled'), | ||
value: false, | ||
onChanged: null, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
|
||
final enabled = tester.element(find.text('enabled')); | ||
expect( | ||
DefaultTextStyle.of(enabled).style.color, | ||
isNot(equals(Theme.of(enabled).disabledColor)), | ||
); | ||
|
||
final disabled = tester.element(find.text('disabled')); | ||
expect( | ||
DefaultTextStyle.of(disabled).style.color, | ||
equals(Theme.of(disabled).disabledColor), | ||
); | ||
}); | ||
|
||
testWidgets( | ||
'golden images', | ||
(tester) async { | ||
final variant = goldenVariant.currentValue!; | ||
|
||
// ensure traditional focus highlight | ||
FocusManager.instance.highlightStrategy = | ||
FocusHighlightStrategy.alwaysTraditional; | ||
|
||
await tester.pumpScaffold( | ||
YaruSwitchButton( | ||
autofocus: variant.hasState(MaterialState.focused), | ||
value: variant.hasState(MaterialState.selected), | ||
onChanged: variant.hasState(MaterialState.disabled) ? null : (_) {}, | ||
title: const Text('YaruSwitchButton'), | ||
subtitle: const Text('Lorem ipsum dolor sit amet'), | ||
), | ||
themeMode: variant.themeMode, | ||
size: const Size(248, 56), | ||
); | ||
await tester.pumpAndSettle(); | ||
|
||
if (variant.hasState(MaterialState.pressed)) { | ||
await tester.down(find.byType(Switch)); | ||
await tester.pumpAndSettle(); | ||
} else if (variant.hasState(MaterialState.hovered)) { | ||
await tester.hover(find.byType(Switch)); | ||
await tester.pumpAndSettle(); | ||
} | ||
|
||
await expectLater( | ||
find.byType(YaruSwitchButton), | ||
matchesGoldenFile('goldens/yaru_switch_button-${variant.label}.png'), | ||
); | ||
}, | ||
variant: goldenVariant, | ||
tags: 'golden', | ||
); | ||
} | ||
|
||
final goldenVariant = ValueVariant({ | ||
...goldenThemeVariants('off', <MaterialState>{}), | ||
...goldenThemeVariants('off-disabled', {MaterialState.disabled}), | ||
...goldenThemeVariants('off-focused', {MaterialState.focused}), | ||
...goldenThemeVariants('off-hovered', {MaterialState.hovered}), | ||
...goldenThemeVariants('off-pressed', {MaterialState.pressed}), | ||
...goldenThemeVariants('on', {MaterialState.selected}), | ||
...goldenThemeVariants('on-disabled', { | ||
MaterialState.selected, | ||
MaterialState.disabled, | ||
}), | ||
...goldenThemeVariants('on-focused', { | ||
MaterialState.selected, | ||
MaterialState.focused, | ||
}), | ||
...goldenThemeVariants('on-hovered', { | ||
MaterialState.selected, | ||
MaterialState.hovered, | ||
}), | ||
...goldenThemeVariants('on-pressed', { | ||
MaterialState.selected, | ||
MaterialState.pressed, | ||
}), | ||
}); |