Skip to content

Commit ed70f4e

Browse files
authored
Adaptive Switch (flutter#130425)
Currently, `Switch.factory` delegates to `CupertinoSwitch` when platform is iOS or macOS. This PR is to: * have the factory configure the Material `Switch` for the expected look and feel. * introduce `Adaptation` class to customize themes for the adaptive components.
1 parent a76720e commit ed70f4e

File tree

9 files changed

+1462
-265
lines changed

9 files changed

+1462
-265
lines changed

dev/tools/gen_defaults/lib/switch_template.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ class _${blockName}DefaultsM3 extends SwitchThemeData {
126126
});
127127
}
128128
129+
@override
130+
MaterialStateProperty<MouseCursor> get mouseCursor {
131+
return MaterialStateProperty.resolveWith((Set<MaterialState> states)
132+
=> MaterialStateMouseCursor.clickable.resolve(states));
133+
}
134+
129135
@override
130136
MaterialStatePropertyAll<double> get trackOutlineWidth => const MaterialStatePropertyAll<double>(${getToken('md.comp.switch.track.outline.width')});
131137
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
/// Flutter code sample for [Switch.adaptive].
8+
9+
void main() => runApp(const SwitchApp());
10+
11+
class SwitchApp extends StatefulWidget {
12+
const SwitchApp({super.key});
13+
14+
@override
15+
State<SwitchApp> createState() => _SwitchAppState();
16+
}
17+
18+
class _SwitchAppState extends State<SwitchApp> {
19+
bool isMaterial = true;
20+
bool isCustomized = false;
21+
22+
@override
23+
Widget build(BuildContext context) {
24+
final ThemeData theme = ThemeData(
25+
platform: isMaterial ? TargetPlatform.android : TargetPlatform.iOS,
26+
adaptations: <Adaptation<Object>>[
27+
if (isCustomized) const _SwitchThemeAdaptation()
28+
]
29+
);
30+
final ButtonStyle style = OutlinedButton.styleFrom(
31+
fixedSize: const Size(220, 40),
32+
);
33+
34+
return MaterialApp(
35+
theme: theme,
36+
home: Scaffold(
37+
appBar: AppBar(title: const Text('Adaptive Switches')),
38+
body: Column(
39+
mainAxisAlignment: MainAxisAlignment.center,
40+
children: <Widget>[
41+
OutlinedButton(
42+
style: style,
43+
onPressed: () {
44+
setState(() {
45+
isMaterial = !isMaterial;
46+
});
47+
},
48+
child: isMaterial ? const Text('Show cupertino style') : const Text('Show material style'),
49+
),
50+
OutlinedButton(
51+
style: style,
52+
onPressed: () {
53+
setState(() {
54+
isCustomized = !isCustomized;
55+
});
56+
},
57+
child: isCustomized ? const Text('Remove customization') : const Text('Add customization'),
58+
),
59+
const SizedBox(height: 20),
60+
const SwitchWithLabel(label: 'enabled', enabled: true),
61+
const SwitchWithLabel(label: 'disabled', enabled: false),
62+
],
63+
),
64+
),
65+
);
66+
}
67+
}
68+
69+
class SwitchWithLabel extends StatefulWidget {
70+
const SwitchWithLabel({
71+
super.key,
72+
required this.enabled,
73+
required this.label,
74+
});
75+
76+
final bool enabled;
77+
final String label;
78+
79+
@override
80+
State<SwitchWithLabel> createState() => _SwitchWithLabelState();
81+
}
82+
83+
class _SwitchWithLabelState extends State<SwitchWithLabel> {
84+
bool active = true;
85+
86+
@override
87+
Widget build(BuildContext context) {
88+
return Row(
89+
mainAxisAlignment: MainAxisAlignment.center,
90+
children: <Widget>[
91+
Container(
92+
width: 150,
93+
padding: const EdgeInsets.only(right: 20),
94+
child: Text(widget.label)
95+
),
96+
Switch.adaptive(
97+
value: active,
98+
onChanged: !widget.enabled ? null : (bool value) {
99+
setState(() {
100+
active = value;
101+
});
102+
},
103+
),
104+
],
105+
);
106+
}
107+
}
108+
109+
110+
class _SwitchThemeAdaptation extends Adaptation<SwitchThemeData> {
111+
const _SwitchThemeAdaptation();
112+
113+
@override
114+
SwitchThemeData adapt(ThemeData theme, SwitchThemeData defaultValue) {
115+
switch (theme.platform) {
116+
case TargetPlatform.android:
117+
case TargetPlatform.fuchsia:
118+
case TargetPlatform.linux:
119+
case TargetPlatform.windows:
120+
return defaultValue;
121+
case TargetPlatform.iOS:
122+
case TargetPlatform.macOS:
123+
return SwitchThemeData(
124+
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
125+
if (states.contains(MaterialState.selected)) {
126+
return Colors.yellow;
127+
}
128+
return null; // Use the default.
129+
}),
130+
trackColor: const MaterialStatePropertyAll<Color>(Colors.brown),
131+
);
132+
}
133+
}
134+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_api_samples/material/switch/switch.4.dart' as example;
7+
import 'package:flutter_test/flutter_test.dart';
8+
9+
void main() {
10+
testWidgets('Show adaptive switch theme', (WidgetTester tester) async {
11+
await tester.pumpWidget(
12+
const example.SwitchApp(),
13+
);
14+
15+
// Default is material style switches
16+
expect(find.text('Show cupertino style'), findsOneWidget);
17+
expect(find.text('Show material style'), findsNothing);
18+
19+
Finder adaptiveSwitch = find.byType(Switch).first;
20+
expect(
21+
adaptiveSwitch,
22+
paints
23+
..rrect(color: const Color(0xff6750a4)) // M3 primary color.
24+
..rrect()
25+
..rrect(color: Colors.white), // Thumb color
26+
);
27+
28+
await tester.tap(find.widgetWithText(OutlinedButton, 'Add customization'));
29+
await tester.pumpAndSettle();
30+
31+
// Theme adaptation does not affect material-style switch.
32+
adaptiveSwitch = find.byType(Switch).first;
33+
expect(
34+
adaptiveSwitch,
35+
paints
36+
..rrect(color: const Color(0xff6750a4)) // M3 primary color.
37+
..rrect()
38+
..rrect(color: Colors.white), // Thumb color
39+
);
40+
41+
await tester.tap(find.widgetWithText(OutlinedButton, 'Show cupertino style'));
42+
await tester.pumpAndSettle();
43+
44+
expect(
45+
adaptiveSwitch,
46+
paints
47+
..rrect(color: const Color(0xff795548)) // Customized track color only for cupertino.
48+
..rrect()..rrect()..rrect()..rrect()
49+
..rrect(color: const Color(0xffffeb3b)), // Customized thumb color only for cupertino.
50+
);
51+
52+
await tester.tap(find.widgetWithText(OutlinedButton, 'Remove customization'));
53+
await tester.pumpAndSettle();
54+
55+
expect(
56+
adaptiveSwitch,
57+
paints
58+
..rrect(color: const Color(0xff34c759)) // Cupertino system green.
59+
..rrect()..rrect()..rrect()..rrect()
60+
..rrect(color: Colors.white), // Thumb color
61+
);
62+
});
63+
}

0 commit comments

Comments
 (0)