Skip to content

Commit 29422d2

Browse files
authored
M3 snackbar [re-land] (#116218)
* Add M2 defaults and template skeleton * add MaterialStateColor functionality to ActionTextColor (issue #110402) * Add M2 defaults and template skeleton * updated material 3 tokens * Updated snackbar demo * add theme tests * add gen defaults * formatting * more whitespace fixes * add widget type * update docs * code review changes * Add line overflow functionality * whitespace fixes * update M3 animation * whitespace fixes * add insetPadding param * Modifed icon parameter to showCloseIcon * white space fixes * test fixes * rename iconColor to closeIconColor * debug test fix * de-britishification * g3fix * g3fix * debug test fix
1 parent 24b3c38 commit 29422d2

File tree

9 files changed

+801
-94
lines changed

9 files changed

+801
-94
lines changed

dev/tools/gen_defaults/bin/gen_defaults.dart

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import 'package:gen_defaults/progress_indicator_template.dart';
4444
import 'package:gen_defaults/radio_template.dart';
4545
import 'package:gen_defaults/segmented_button_template.dart';
4646
import 'package:gen_defaults/slider_template.dart';
47+
import 'package:gen_defaults/snackbar_template.dart';
4748
import 'package:gen_defaults/surface_tint.dart';
4849
import 'package:gen_defaults/switch_template.dart';
4950
import 'package:gen_defaults/tabs_template.dart';
@@ -162,6 +163,7 @@ Future<void> main(List<String> args) async {
162163
ProgressIndicatorTemplate('ProgressIndicator', '$materialLib/progress_indicator.dart', tokens).updateFile();
163164
RadioTemplate('Radio<T>', '$materialLib/radio.dart', tokens).updateFile();
164165
SegmentedButtonTemplate('SegmentedButton', '$materialLib/segmented_button.dart', tokens).updateFile();
166+
SnackbarTemplate('md.comp.snackbar', 'Snackbar', '$materialLib/snack_bar.dart', tokens).updateFile();
165167
SliderTemplate('md.comp.slider', 'Slider', '$materialLib/slider.dart', tokens).updateFile();
166168
SurfaceTintTemplate('SurfaceTint', '$materialLib/elevation_overlay.dart', tokens).updateFile();
167169
SwitchTemplate('Switch', '$materialLib/switch.dart', tokens).updateFile();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 'template.dart';
6+
7+
class SnackbarTemplate extends TokenTemplate {
8+
const SnackbarTemplate(
9+
this.tokenGroup, super.blockName, super.fileName, super.tokens, {
10+
super.colorSchemePrefix = '_colors.'
11+
});
12+
13+
final String tokenGroup;
14+
15+
@override
16+
String generate() => '''
17+
class _${blockName}DefaultsM3 extends SnackBarThemeData {
18+
_${blockName}DefaultsM3(this.context);
19+
20+
final BuildContext context;
21+
late final ThemeData _theme = Theme.of(context);
22+
23+
late final ColorScheme _colors = _theme.colorScheme;
24+
25+
@override
26+
Color get backgroundColor => ${componentColor("$tokenGroup.container")};
27+
28+
@override
29+
Color get actionTextColor => MaterialStateColor.resolveWith((Set<MaterialState> states) {
30+
if (states.contains(MaterialState.disabled)) {
31+
return ${componentColor("$tokenGroup.action.pressed.label-text")};
32+
}
33+
if (states.contains(MaterialState.pressed)) {
34+
return ${componentColor("$tokenGroup.action.pressed.label-text")};
35+
}
36+
if (states.contains(MaterialState.hovered)) {
37+
return ${componentColor("$tokenGroup.action.hover.label-text")};
38+
}
39+
if (states.contains(MaterialState.focused)) {
40+
return ${componentColor("$tokenGroup.action.focus.label-text")};
41+
}
42+
return ${componentColor("$tokenGroup.action.label-text")};
43+
});
44+
45+
@override
46+
Color get disabledActionTextColor =>
47+
${componentColor("$tokenGroup.action.pressed.label-text")};
48+
49+
50+
@override
51+
TextStyle get contentTextStyle =>
52+
${textStyle("$tokenGroup.supporting-text")}!.copyWith
53+
(color: ${componentColor("$tokenGroup.supporting-text")},
54+
);
55+
56+
@override
57+
double get elevation => ${elevation("$tokenGroup.container")};
58+
59+
@override
60+
ShapeBorder get shape => ${shape("$tokenGroup.container")};
61+
62+
@override
63+
SnackBarBehavior get behavior => SnackBarBehavior.fixed;
64+
65+
@override
66+
EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0);
67+
68+
@override
69+
bool get showCloseIcon => false;
70+
71+
@override
72+
Color get iconColor => _colors.onInverseSurface;
73+
}
74+
75+
''';
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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 [SnackBar] with Material 3 specifications.
6+
7+
import 'package:flutter/material.dart';
8+
9+
void main() => runApp(const MyApp());
10+
11+
// A Material 3 [SnackBar] demonstrating an optional icon, in either floating
12+
// or fixed format.
13+
class MyApp extends StatelessWidget {
14+
const MyApp({super.key});
15+
16+
static const String _title = 'Flutter Code Sample';
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return MaterialApp(
21+
title: _title,
22+
theme: ThemeData(useMaterial3: true),
23+
home: Scaffold(
24+
appBar: AppBar(title: const Text(_title)),
25+
body: const Center(
26+
child: SnackBarExample(),
27+
),
28+
),
29+
);
30+
}
31+
}
32+
33+
class SnackBarExample extends StatefulWidget {
34+
const SnackBarExample({super.key});
35+
36+
@override
37+
State<SnackBarExample> createState() => _SnackBarExampleState();
38+
}
39+
40+
class _SnackBarExampleState extends State<SnackBarExample> {
41+
SnackBarBehavior? _snackBarBehavior = SnackBarBehavior.floating;
42+
bool _withIcon = true;
43+
bool _withAction = true;
44+
bool _multiLine = false;
45+
bool _longActionLabel = false;
46+
47+
Padding _configRow(List<Widget> children) => Padding(
48+
padding: const EdgeInsets.all(8.0), child: Row(children: children));
49+
50+
@override
51+
Widget build(BuildContext context) {
52+
return Padding(padding: const EdgeInsets.only(left: 50.0), child: Column(
53+
children: <Widget>[
54+
_configRow(<Widget>[
55+
Text('Snack Bar configuration',
56+
style: Theme.of(context).textTheme.bodyLarge),
57+
]),
58+
_configRow(
59+
<Widget>[
60+
const Text('Fixed'),
61+
Radio<SnackBarBehavior>(
62+
value: SnackBarBehavior.fixed,
63+
groupValue: _snackBarBehavior,
64+
onChanged: (SnackBarBehavior? value) {
65+
setState(() {
66+
_snackBarBehavior = value;
67+
});
68+
},
69+
),
70+
const Text('Floating'),
71+
Radio<SnackBarBehavior>(
72+
value: SnackBarBehavior.floating,
73+
groupValue: _snackBarBehavior,
74+
onChanged: (SnackBarBehavior? value) {
75+
setState(() {
76+
_snackBarBehavior = value;
77+
});
78+
},
79+
),
80+
],
81+
),
82+
_configRow(
83+
<Widget>[
84+
const Text('Include Icon '),
85+
Switch(
86+
value: _withIcon,
87+
onChanged: (bool value) {
88+
setState(() {
89+
_withIcon = !_withIcon;
90+
});
91+
},
92+
),
93+
],
94+
),
95+
_configRow(
96+
<Widget>[
97+
const Text('Include Action '),
98+
Switch(
99+
value: _withAction,
100+
onChanged: (bool value) {
101+
setState(() {
102+
_withAction = !_withAction;
103+
});
104+
},
105+
),
106+
const SizedBox(width: 16.0),
107+
const Text('Long Action Label '),
108+
Switch(
109+
value: _longActionLabel,
110+
onChanged: !_withAction
111+
? null
112+
: (bool value) {
113+
setState(() {
114+
_longActionLabel = !_longActionLabel;
115+
});
116+
},
117+
),
118+
],
119+
),
120+
_configRow(
121+
<Widget>[
122+
const Text('Multi Line Text'),
123+
Switch(
124+
value: _multiLine,
125+
onChanged: _snackBarBehavior == SnackBarBehavior.fixed ? null : (bool value) {
126+
setState(() {
127+
_multiLine = !_multiLine;
128+
});
129+
},
130+
),
131+
],
132+
),
133+
const SizedBox(height: 16.0),
134+
ElevatedButton(
135+
child: const Text('Show Snackbar'),
136+
onPressed: () {
137+
ScaffoldMessenger.of(context).showSnackBar(_snackBar());
138+
}
139+
),
140+
],
141+
),
142+
);
143+
}
144+
145+
SnackBar _snackBar() {
146+
final SnackBarAction? action = _withAction
147+
? SnackBarAction(
148+
label: _longActionLabel ? 'Long Action Text' : 'Action',
149+
onPressed: () {
150+
// Code to execute.
151+
},
152+
)
153+
: null;
154+
final double? width =
155+
_snackBarBehavior == SnackBarBehavior.floating && _multiLine ? 400.0 : null;
156+
final String label = _multiLine
157+
? 'A Snack Bar with quite a lot of text which spans across multiple lines'
158+
: 'Single Line Snack Bar';
159+
return SnackBar(
160+
content: Text(label),
161+
showCloseIcon: _withIcon,
162+
width: width,
163+
behavior: _snackBarBehavior,
164+
action: action,
165+
duration: const Duration(seconds: 3),
166+
);
167+
}
168+
}

0 commit comments

Comments
 (0)