Skip to content

Commit 16bbef1

Browse files
authored
Migrate IconButton to Material 3 - Part 2 (#106437)
1 parent 5a5721c commit 16bbef1

File tree

4 files changed

+654
-19
lines changed

4 files changed

+654
-19
lines changed

dev/tools/gen_defaults/lib/icon_button_template.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,26 @@ class _TokenDefaultsM3 extends ButtonStyle {
3535
if (states.contains(MaterialState.disabled)) {
3636
return ${componentColor('md.comp.icon-button.disabled.icon')};
3737
}
38+
if (states.contains(MaterialState.selected)) {
39+
return ${componentColor('md.comp.icon-button.selected.icon')};
40+
}
3841
return ${componentColor('md.comp.icon-button.unselected.icon')};
3942
});
4043
4144
@override
4245
MaterialStateProperty<Color?>? get overlayColor =>
4346
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
47+
if (states.contains(MaterialState.selected)) {
48+
if (states.contains(MaterialState.hovered)) {
49+
return ${componentColor('md.comp.icon-button.selected.hover.state-layer')};
50+
}
51+
if (states.contains(MaterialState.focused)) {
52+
return ${componentColor('md.comp.icon-button.selected.focus.state-layer')};
53+
}
54+
if (states.contains(MaterialState.pressed)) {
55+
return ${componentColor('md.comp.icon-button.selected.pressed.state-layer')};
56+
}
57+
}
4458
if (states.contains(MaterialState.hovered)) {
4559
return ${componentColor('md.comp.icon-button.unselected.hover.state-layer')};
4660
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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+
// Flutter code sample for IconButton with toggle feature
6+
7+
import 'package:flutter/material.dart';
8+
9+
void main() {
10+
runApp(const IconButtonToggleApp());
11+
}
12+
13+
class IconButtonToggleApp extends StatelessWidget {
14+
const IconButtonToggleApp({super.key});
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return MaterialApp(
19+
theme: ThemeData(
20+
colorSchemeSeed: const Color(0xff6750a4),
21+
useMaterial3: true,
22+
// Desktop and web platforms have a compact visual density by default.
23+
// To see buttons with circular background on desktop/web, the "visualDensity"
24+
// needs to be set to "VisualDensity.standard".
25+
visualDensity: VisualDensity.standard,
26+
),
27+
title: 'Icon Button Types',
28+
home: const Scaffold(
29+
body: DemoIconToggleButtons(),
30+
),
31+
);
32+
}
33+
}
34+
35+
class DemoIconToggleButtons extends StatefulWidget {
36+
const DemoIconToggleButtons({super.key});
37+
38+
@override
39+
State<DemoIconToggleButtons> createState() => _DemoIconToggleButtonsState();
40+
}
41+
42+
class _DemoIconToggleButtonsState extends State<DemoIconToggleButtons> {
43+
@override
44+
Widget build(BuildContext context) {
45+
return Padding(
46+
padding: const EdgeInsets.all(8.0),
47+
child: Column(
48+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
49+
children: <Widget>[
50+
Row(
51+
mainAxisAlignment: MainAxisAlignment.center,
52+
// Standard IconButton
53+
children: const <Widget>[
54+
DemoIconToggleButton(isEnabled: true),
55+
SizedBox(width: 10),
56+
DemoIconToggleButton(isEnabled: false),
57+
]
58+
),
59+
Row(
60+
mainAxisAlignment: MainAxisAlignment.center,
61+
children: const <Widget>[
62+
// Filled IconButton
63+
DemoIconToggleButton(isEnabled: true, getDefaultStyle: enabledFilledButtonStyle,),
64+
SizedBox(width: 10),
65+
DemoIconToggleButton(isEnabled: false, getDefaultStyle: disabledFilledButtonStyle,)
66+
]
67+
),
68+
Row(
69+
mainAxisAlignment: MainAxisAlignment.center,
70+
children: const <Widget>[
71+
// Filled Tonal IconButton
72+
DemoIconToggleButton(isEnabled: true, getDefaultStyle: enabledFilledTonalButtonStyle,),
73+
SizedBox(width: 10),
74+
DemoIconToggleButton(isEnabled: false, getDefaultStyle: disabledFilledTonalButtonStyle,),
75+
]
76+
),
77+
Row(
78+
mainAxisAlignment: MainAxisAlignment.center,
79+
children: const <Widget>[
80+
// Outlined IconButton
81+
DemoIconToggleButton(isEnabled: true, getDefaultStyle: enabledOutlinedButtonStyle,),
82+
SizedBox(width: 10),
83+
DemoIconToggleButton(isEnabled: false, getDefaultStyle: disabledOutlinedButtonStyle,),
84+
]
85+
),
86+
]
87+
),
88+
);
89+
}
90+
}
91+
92+
class DemoIconToggleButton extends StatefulWidget {
93+
const DemoIconToggleButton({required this.isEnabled, this.getDefaultStyle, super.key});
94+
95+
final bool isEnabled;
96+
final ButtonStyle? Function(bool, ColorScheme)? getDefaultStyle;
97+
98+
@override
99+
State<DemoIconToggleButton> createState() => _DemoIconToggleButtonState();
100+
}
101+
102+
class _DemoIconToggleButtonState extends State<DemoIconToggleButton> {
103+
bool selected = false;
104+
105+
@override
106+
Widget build(BuildContext context) {
107+
final ColorScheme colors = Theme.of(context).colorScheme;
108+
final VoidCallback? onPressed = widget.isEnabled
109+
? () {
110+
setState(() {
111+
selected = !selected;
112+
});
113+
}
114+
: null;
115+
ButtonStyle? style;
116+
if (widget.getDefaultStyle != null) {
117+
style = widget.getDefaultStyle!(selected, colors);
118+
}
119+
120+
return IconButton(
121+
isSelected: selected,
122+
icon: const Icon(Icons.settings_outlined),
123+
selectedIcon: const Icon(Icons.settings),
124+
onPressed: onPressed,
125+
style: style,
126+
);
127+
}
128+
}
129+
130+
ButtonStyle enabledFilledButtonStyle(bool selected, ColorScheme colors) {
131+
return IconButton.styleFrom(
132+
foregroundColor: selected ? colors.onPrimary : colors.primary,
133+
backgroundColor: selected ? colors.primary : colors.surfaceVariant,
134+
disabledForegroundColor: colors.onSurface.withOpacity(0.38),
135+
disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
136+
hoverColor: selected ? colors.onPrimary.withOpacity(0.08) : colors.primary.withOpacity(0.08),
137+
focusColor: selected ? colors.onPrimary.withOpacity(0.12) : colors.primary.withOpacity(0.12),
138+
highlightColor: selected ? colors.onPrimary.withOpacity(0.12) : colors.primary.withOpacity(0.12),
139+
);
140+
}
141+
142+
ButtonStyle disabledFilledButtonStyle(bool selected, ColorScheme colors) {
143+
return IconButton.styleFrom(
144+
disabledForegroundColor: colors.onSurface.withOpacity(0.38),
145+
disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
146+
);
147+
}
148+
149+
ButtonStyle enabledFilledTonalButtonStyle(bool selected, ColorScheme colors) {
150+
return IconButton.styleFrom(
151+
foregroundColor: selected ? colors.onSecondaryContainer : colors.onSurfaceVariant,
152+
backgroundColor: selected ? colors.secondaryContainer : colors.surfaceVariant,
153+
hoverColor: selected ? colors.onSecondaryContainer.withOpacity(0.08) : colors.onSurfaceVariant.withOpacity(0.08),
154+
focusColor: selected ? colors.onSecondaryContainer.withOpacity(0.12) : colors.onSurfaceVariant.withOpacity(0.12),
155+
highlightColor: selected ? colors.onSecondaryContainer.withOpacity(0.12) : colors.onSurfaceVariant.withOpacity(0.12),
156+
);
157+
}
158+
159+
ButtonStyle disabledFilledTonalButtonStyle(bool selected, ColorScheme colors) {
160+
return IconButton.styleFrom(
161+
disabledForegroundColor: colors.onSurface.withOpacity(0.38),
162+
disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
163+
);
164+
}
165+
166+
ButtonStyle enabledOutlinedButtonStyle(bool selected, ColorScheme colors) {
167+
return IconButton.styleFrom(
168+
backgroundColor: selected ? colors.inverseSurface : null,
169+
hoverColor: selected ? colors.onInverseSurface.withOpacity(0.08) : colors.onSurfaceVariant.withOpacity(0.08),
170+
focusColor: selected ? colors.onInverseSurface.withOpacity(0.12) : colors.onSurfaceVariant.withOpacity(0.12),
171+
highlightColor: selected ? colors.onInverseSurface.withOpacity(0.12) : colors.onSurface.withOpacity(0.12),
172+
side: BorderSide(color: colors.outline),
173+
).copyWith(
174+
foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
175+
if (states.contains(MaterialState.selected)) {
176+
return colors.onInverseSurface;
177+
}
178+
if (states.contains(MaterialState.pressed)) {
179+
return colors.onSurface;
180+
}
181+
return null;
182+
}),
183+
);
184+
}
185+
186+
ButtonStyle disabledOutlinedButtonStyle(bool selected, ColorScheme colors) {
187+
return IconButton.styleFrom(
188+
disabledForegroundColor: colors.onSurface.withOpacity(0.38),
189+
disabledBackgroundColor: selected ? colors.onSurface.withOpacity(0.12) : null,
190+
side: selected ? null : BorderSide(color: colors.outline.withOpacity(0.12)),
191+
);
192+
}

0 commit comments

Comments
 (0)