Switch
An on and off switch.
import 'package:flutter/widgets.dart';
import 'ui/switch.dart';
class SwitchDemo extends StatefulWidget {
const SwitchDemo({super.key});
@override
State<SwitchDemo> createState() => _SwitchDemoState();
}
class _SwitchDemoState extends State<SwitchDemo> {
bool airplane = false;
@override
Widget build(BuildContext context) => Row(
mainAxisSize: MainAxisSize.min,
children: [
Switch(value: airplane, onChanged: (v) => setState(() => airplane = v), semanticLabel: 'Airplane mode'),
const SizedBox(width: 8),
const Text('Airplane mode'),
],
);
}
Try its options
Each option its constructor takes, to change and see; the code below it is what you chose, to copy.
Add it
In your Flutter project:
ashlar add switch
It writes theme.dart, palette.dart, group.dart, pressable.dart and switch.dart into lib/ui, keeping any you already have. It needs pressable, which come with it.
Examples
Other ways to use it, each running live, with its code.
Small
For a dense row.
// Small: For a dense row.
import 'package:flutter/widgets.dart';
import 'ui/field.dart';
import 'ui/switch.dart';
class SwitchSmallExample extends StatefulWidget {
const SwitchSmallExample({super.key});
@override
State<SwitchSmallExample> createState() => _SwitchSmallExampleState();
}
class _SwitchSmallExampleState extends State<SwitchSmallExample> {
var on = true;
@override
Widget build(BuildContext context) => SizedBox(
width: 220,
child: Field(
axis: Axis.horizontal,
label: const Text('Compact rows'),
child: Switch(size: SwitchSize.sm, value: on, onChanged: (v) => setState(() => on = v)),
),
);
}
In a card
The whole card flips it.
// In a card: The whole card flips it.
import 'package:flutter/widgets.dart';
import 'ui/field.dart';
import 'ui/switch.dart';
class SwitchCardExample extends StatefulWidget {
const SwitchCardExample({super.key});
@override
State<SwitchCardExample> createState() => _SwitchCardExampleState();
}
class _SwitchCardExampleState extends State<SwitchCardExample> {
var on = false;
@override
Widget build(BuildContext context) => SizedBox(
width: 360,
child: Field(
card: true,
selected: on,
onPressed: () => setState(() => on = !on),
label: const Text('Share across devices'),
description: const Text('Focus is shared, so others see you are busy.'),
responsive: true,
child: Switch(value: on, onChanged: (v) => setState(() => on = v)),
),
);
}
API
Read from the source above: each parameter with its type, its default, and what it is for.
SwitchSizeenum
An on and off switch, for a setting that takes effect at once. Give it a semanticLabel, or put it in a row with a Text.
| Value | What it is for |
|---|---|
sm | 24 by 14. |
md | 32 by 18. |
Switchclass
| Parameter | Type | Default | What it is for |
|---|---|---|---|
valuerequired |
bool |
Whether it is on. | |
onChangedrequired |
ValueChanged<bool>? |
Null disables the switch. | |
semanticLabel |
String? |
What a screen reader calls it when its visible text does not say, as for an icon alone. | |
size |
SwitchSize |
SwitchSize.md |
Medium, or small to sit in a dense row. |
focusNode |
FocusNode? |
Its focus node, to move focus to it or follow it from outside; one is made when not given. | |
autofocus |
bool |
false |
Takes the keyboard's focus when it first appears. |
Source
What lands in your app. It is yours from then on: change it as you would your own code, and ashlar add
will not write over it.
lib/ui/switch.dart 105 lines
// Ashlar UI: Switch. Copied into your app by `ashlar add switch`; it is yours to change.
import 'package:flutter/widgets.dart';
import 'pressable.dart';
import 'theme.dart';
/// An on and off switch, for a setting that takes effect at once. Give it a
/// [semanticLabel], or put it in a row with a Text.
///
/// ```dart
/// Switch(value: notify, onChanged: (v) => setState(() => notify = v), semanticLabel: 'Notifications')
/// ```
enum SwitchSize {
/// 24 by 14.
sm,
/// 32 by 18.
md,
}
class Switch extends StatelessWidget {
const Switch({
super.key,
required this.value,
required this.onChanged,
this.semanticLabel,
this.size = SwitchSize.md,
this.focusNode,
this.autofocus = false,
});
/// Whether it is on.
final bool value;
/// Null disables the switch.
final ValueChanged<bool>? onChanged;
/// What a screen reader calls it when its visible text does not say, as for an icon
/// alone.
final String? semanticLabel;
/// Medium, or small to sit in a dense row.
final SwitchSize size;
/// Its focus node, to move focus to it or follow it from outside; one is made when not
/// given.
final FocusNode? focusNode;
/// Takes the keyboard's focus when it first appears.
final bool autofocus;
// Its own size, at the start, even where its parent stretches what it holds: a
// stacked Field, a column that fills its width.
@override
Widget build(BuildContext context) => Align(
alignment: AlignmentDirectional.centerStart,
widthFactor: 1,
heightFactor: 1,
child: _control(context),
);
Widget _control(BuildContext context) {
final t = AshlarTheme.of(context);
final c = t.colors;
return Pressable(
onPressed: onChanged == null ? null : () => onChanged!(!value),
toggled: value,
semanticLabel: semanticLabel,
focusNode: focusNode,
autofocus: autofocus,
builder: (context, state) => Opacity(
opacity: state.enabled ? 1 : .5,
child: AnimatedContainer(
duration: t.duration,
width: size == SwitchSize.sm ? 24 : 32,
height: size == SwitchSize.sm ? 14 : 18,
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: value ? c.primary : c.input,
borderRadius: BorderRadius.circular(9),
boxShadow: [if (state.focused) ...t.focusRing, ...t.shadowXs],
),
child: AnimatedAlign(
duration: t.duration,
curve: Curves.easeOutCubic,
alignment: value
? AlignmentDirectional.centerEnd
: AlignmentDirectional.centerStart,
child: Container(
width: size == SwitchSize.sm ? 12 : 16,
height: size == SwitchSize.sm ? 12 : 16,
decoration: BoxDecoration(
color: t.brightness == Brightness.dark && !value
? c.foreground
: c.background,
shape: BoxShape.circle,
),
),
),
),
),
);
}
}