Skip to content

Commit ca2a751

Browse files
authored
Migrate NavigationBar to M3 tokens. (flutter#98285)
1 parent ccaf515 commit ca2a751

File tree

11 files changed

+353
-50
lines changed

11 files changed

+353
-50
lines changed

dev/tools/gen_defaults/bin/gen_defaults.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'dart:convert';
1818
import 'dart:io';
1919

2020
import 'package:gen_defaults/fab_template.dart';
21+
import 'package:gen_defaults/navigation_bar_template.dart';
2122
import 'package:gen_defaults/typography_template.dart';
2223

2324
Map<String, dynamic> _readTokenFile(String fileName) {
@@ -64,5 +65,6 @@ Future<void> main(List<String> args) async {
6465
tokens['colorsDark'] = _readTokenFile('color_dark.json');
6566

6667
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
68+
NavigationBarTemplate('$materialLib/navigation_bar.dart', tokens).updateFile();
6769
TypographyTemplate('$materialLib/typography.dart', tokens).updateFile();
6870
}

dev/tools/gen_defaults/data/shape.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
"bottomRight": 0.0
3434
},
3535

36+
"md.sys.shape.corner.full": {
37+
"family": "SHAPE_FAMILY_CIRCULAR"
38+
},
39+
3640
"md.sys.shape.corner.large": {
3741
"family": "SHAPE_FAMILY_ROUNDED_CORNERS",
3842
"topLeft": 16.0,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 NavigationBarTemplate extends TokenTemplate {
8+
const NavigationBarTemplate(String fileName, Map<String, dynamic> tokens) : super(fileName, tokens);
9+
10+
@override
11+
String generate() => '''
12+
// Generated version ${tokens["version"]}
13+
class _TokenDefaultsM3 extends NavigationBarThemeData {
14+
_TokenDefaultsM3(BuildContext context)
15+
: _theme = Theme.of(context),
16+
_colors = Theme.of(context).colorScheme,
17+
super(
18+
height: ${tokens["md.comp.navigation-bar.container.height"]},
19+
elevation: ${elevation("md.comp.navigation-bar.container")},
20+
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
21+
);
22+
23+
final ThemeData _theme;
24+
final ColorScheme _colors;
25+
26+
// With Material 3, the NavigationBar uses an overlay blend for the
27+
// default color regardless of light/dark mode. This should be handled
28+
// in the Material widget based off of elevation, but for now we will do
29+
// it here in the defaults.
30+
@override Color? get backgroundColor => ElevationOverlay.colorWithOverlay(_colors.${color("md.comp.navigation-bar.container")}, _colors.primary, ${elevation("md.comp.navigation-bar.container")});
31+
32+
@override MaterialStateProperty<IconThemeData?>? get iconTheme {
33+
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
34+
return IconThemeData(
35+
size: ${tokens["md.comp.navigation-bar.icon.size"]},
36+
color: states.contains(MaterialState.selected)
37+
? _colors.${color("md.comp.navigation-bar.active.icon")}
38+
: _colors.${color("md.comp.navigation-bar.inactive.icon")},
39+
);
40+
});
41+
}
42+
43+
@override Color? get indicatorColor => _colors.${color("md.comp.navigation-bar.active-indicator")};
44+
@override ShapeBorder? get indicatorShape => ${shape("md.comp.navigation-bar.active-indicator")};
45+
46+
@override MaterialStateProperty<TextStyle?>? get labelTextStyle {
47+
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
48+
final TextStyle style = _theme.textTheme.${textStyle("md.comp.navigation-bar.label-text")}!;
49+
return style.apply(color: states.contains(MaterialState.selected)
50+
? _colors.${color("md.comp.navigation-bar.active.label-text")}
51+
: _colors.${color("md.comp.navigation-bar.inactive.label-text")}
52+
);
53+
});
54+
}
55+
}
56+
''';
57+
}

dev/tools/gen_defaults/lib/template.dart

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,24 @@ abstract class TokenTemplate {
7676

7777
/// Generate a shape constant for the given component token.
7878
///
79-
/// Currently only supports "SHAPE_FAMILY_ROUNDED_CORNERS" which it
80-
/// maps to a [RoundedRectangleBorder] expression.
79+
/// Currently supports family:
80+
/// - "SHAPE_FAMILY_ROUNDED_CORNERS" which maps to [RoundedRectangleBorder].
81+
/// - "SHAPE_FAMILY_CIRCULAR" which maps to a [StadiumBorder].
8182
String shape(String componentToken) {
82-
// TODO(darrenaustin): handle more than just rounded rectangle shapes
8383
final Map<String, dynamic> shape = tokens[tokens['$componentToken.shape']!]! as Map<String, dynamic>;
84-
return 'const RoundedRectangleBorder(borderRadius: '
85-
'BorderRadius.only('
86-
'topLeft: Radius.circular(${shape['topLeft']}), '
87-
'topRight: Radius.circular(${shape['topRight']}), '
88-
'bottomLeft: Radius.circular(${shape['bottomLeft']}), '
89-
'bottomRight: Radius.circular(${shape['bottomRight']})))';
84+
switch (shape['family']) {
85+
case 'SHAPE_FAMILY_ROUNDED_CORNERS':
86+
return 'const RoundedRectangleBorder(borderRadius: '
87+
'BorderRadius.only('
88+
'topLeft: Radius.circular(${shape['topLeft']}), '
89+
'topRight: Radius.circular(${shape['topRight']}), '
90+
'bottomLeft: Radius.circular(${shape['bottomLeft']}), '
91+
'bottomRight: Radius.circular(${shape['bottomRight']})))';
92+
case 'SHAPE_FAMILY_CIRCULAR':
93+
return 'const StadiumBorder()';
94+
}
95+
print('Unsupported shape family type: ${shape['family']} for $componentToken');
96+
return '';
9097
}
9198

9299
/// Generate a [TextTheme] text style name for the given component token.

dev/tools/gen_defaults/test/gen_defaults_test.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,21 @@ static final String tokenBar = 'bar';
101101
test('Templates can get proper shapes from given data', () {
102102
const Map<String, dynamic> tokens = <String, dynamic>{
103103
'foo.shape': 'shape.large',
104+
'bar.shape': 'shape.full',
104105
'shape.large': <String, dynamic>{
105106
'family': 'SHAPE_FAMILY_ROUNDED_CORNERS',
106107
'topLeft': 1.0,
107108
'topRight': 2.0,
108109
'bottomLeft': 3.0,
109110
'bottomRight': 4.0,
110-
}
111+
},
112+
'shape.full': <String, dynamic>{
113+
'family': 'SHAPE_FAMILY_CIRCULAR',
114+
},
111115
};
112116
final TestTemplate template = TestTemplate('foobar.dart', tokens);
113117
expect(template.shape('foo'), 'const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(1.0), topRight: Radius.circular(2.0), bottomLeft: Radius.circular(3.0), bottomRight: Radius.circular(4.0)))');
118+
expect(template.shape('bar'), 'const StadiumBorder()');
114119
});
115120
}
116121

0 commit comments

Comments
 (0)