Skip to content

Commit 79bd629

Browse files
authored
Improvements to M3 demo app (#1647)
* improvements * fix focus * add comment * add comment * copy changes to root material_3_demo * fix large breakpoint * fix large breakpoint * Create integration_test.dart * refactor main.dart into home.dart and constants.dart * add integration_test to pubspec * copy to root material_3_demo * remove removal of constraints * address feedback
1 parent cdc9025 commit 79bd629

20 files changed

+1708
-1500
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2021 The Flutter team. 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_test/flutter_test.dart';
6+
import 'package:integration_test/integration_test.dart';
7+
import 'package:material_3_demo/main.dart' as app;
8+
9+
void main() {
10+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
11+
12+
testWidgets('verify app can run', (tester) async {
13+
app.main();
14+
});
15+
}

experimental/material_3_demo/lib/component_screen.dart

+103-69
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,28 @@ class FirstComponentList extends StatelessWidget {
2626

2727
@override
2828
Widget build(BuildContext context) {
29-
return ListView(
30-
padding: showSecondList
31-
? const EdgeInsetsDirectional.only(end: smallSpacing)
32-
: EdgeInsets.zero,
33-
children: [
34-
const Actions(),
35-
colDivider,
36-
const Communication(),
37-
colDivider,
38-
const Containment(),
39-
if (!showSecondList) ...[
40-
colDivider,
41-
Navigation(scaffoldKey: scaffoldKey),
29+
// Fully traverse this list before moving on.
30+
return FocusTraversalGroup(
31+
child: ListView(
32+
padding: showSecondList
33+
? const EdgeInsetsDirectional.only(end: smallSpacing)
34+
: EdgeInsets.zero,
35+
children: [
36+
const Actions(),
4237
colDivider,
43-
const Selection(),
38+
const Communication(),
4439
colDivider,
45-
const TextInputs()
40+
const Containment(),
41+
if (!showSecondList) ...[
42+
colDivider,
43+
Navigation(scaffoldKey: scaffoldKey),
44+
colDivider,
45+
const Selection(),
46+
colDivider,
47+
const TextInputs()
48+
],
4649
],
47-
],
50+
),
4851
);
4952
}
5053
}
@@ -59,15 +62,18 @@ class SecondComponentList extends StatelessWidget {
5962

6063
@override
6164
Widget build(BuildContext context) {
62-
return ListView(
63-
padding: const EdgeInsetsDirectional.only(end: smallSpacing),
64-
children: <Widget>[
65-
Navigation(scaffoldKey: scaffoldKey),
66-
colDivider,
67-
const Selection(),
68-
colDivider,
69-
const TextInputs(),
70-
],
65+
// Fully traverse this list before moving on.
66+
return FocusTraversalGroup(
67+
child: ListView(
68+
padding: const EdgeInsetsDirectional.only(end: smallSpacing),
69+
children: <Widget>[
70+
Navigation(scaffoldKey: scaffoldKey),
71+
colDivider,
72+
const Selection(),
73+
colDivider,
74+
const TextInputs(),
75+
],
76+
),
7177
);
7278
}
7379
}
@@ -1011,13 +1017,13 @@ class NavigationBars extends StatefulWidget {
10111017
this.onSelectItem,
10121018
required this.selectedIndex,
10131019
required this.isExampleBar,
1014-
this.isBadgeExample,
1020+
this.isBadgeExample = false,
10151021
});
10161022

10171023
final void Function(int)? onSelectItem;
10181024
final int selectedIndex;
10191025
final bool isExampleBar;
1020-
final bool? isBadgeExample;
1026+
final bool isBadgeExample;
10211027

10221028
@override
10231029
State<NavigationBars> createState() => _NavigationBarsState();
@@ -1042,23 +1048,26 @@ class _NavigationBarsState extends State<NavigationBars> {
10421048

10431049
@override
10441050
Widget build(BuildContext context) {
1045-
bool isBadgeExample = widget.isBadgeExample ?? false;
1046-
Widget navigationBar = NavigationBar(
1047-
selectedIndex: selectedIndex,
1048-
onDestinationSelected: (index) {
1049-
setState(() {
1050-
selectedIndex = index;
1051-
});
1052-
if (!widget.isExampleBar) widget.onSelectItem!(index);
1053-
},
1054-
destinations: widget.isExampleBar && isBadgeExample
1055-
? barWithBadgeDestinations
1056-
: widget.isExampleBar
1057-
? exampleBarDestinations
1058-
: appBarDestinations,
1051+
// App NavigationBar should get first focus.
1052+
Widget navigationBar = Focus(
1053+
autofocus: !(widget.isExampleBar || widget.isBadgeExample),
1054+
child: NavigationBar(
1055+
selectedIndex: selectedIndex,
1056+
onDestinationSelected: (index) {
1057+
setState(() {
1058+
selectedIndex = index;
1059+
});
1060+
if (!widget.isExampleBar) widget.onSelectItem!(index);
1061+
},
1062+
destinations: widget.isExampleBar && widget.isBadgeExample
1063+
? barWithBadgeDestinations
1064+
: widget.isExampleBar
1065+
? exampleBarDestinations
1066+
: appBarDestinations,
1067+
),
10591068
);
10601069

1061-
if (widget.isExampleBar && isBadgeExample) {
1070+
if (widget.isExampleBar && widget.isBadgeExample) {
10621071
navigationBar = ComponentDecoration(
10631072
label: 'Badges',
10641073
tooltipMessage: 'Use Badge or Badge.count',
@@ -2188,7 +2197,7 @@ class _SlidersState extends State<Sliders> {
21882197
}
21892198
}
21902199

2191-
class ComponentDecoration extends StatelessWidget {
2200+
class ComponentDecoration extends StatefulWidget {
21922201
const ComponentDecoration({
21932202
super.key,
21942203
required this.label,
@@ -2200,6 +2209,13 @@ class ComponentDecoration extends StatelessWidget {
22002209
final Widget child;
22012210
final String? tooltipMessage;
22022211

2212+
@override
2213+
State<ComponentDecoration> createState() => _ComponentDecorationState();
2214+
}
2215+
2216+
class _ComponentDecorationState extends State<ComponentDecoration> {
2217+
final focusNode = FocusNode();
2218+
22032219
@override
22042220
Widget build(BuildContext context) {
22052221
return RepaintBoundary(
@@ -2210,9 +2226,10 @@ class ComponentDecoration extends StatelessWidget {
22102226
Row(
22112227
mainAxisAlignment: MainAxisAlignment.center,
22122228
children: [
2213-
Text(label, style: Theme.of(context).textTheme.titleSmall),
2229+
Text(widget.label,
2230+
style: Theme.of(context).textTheme.titleSmall),
22142231
Tooltip(
2215-
message: tooltipMessage,
2232+
message: widget.tooltipMessage,
22162233
child: const Padding(
22172234
padding: EdgeInsets.symmetric(horizontal: 5.0),
22182235
child: Icon(Icons.info_outline, size: 16)),
@@ -2222,18 +2239,32 @@ class ComponentDecoration extends StatelessWidget {
22222239
ConstrainedBox(
22232240
constraints:
22242241
const BoxConstraints.tightFor(width: widthConstraint),
2225-
child: Card(
2226-
elevation: 0,
2227-
shape: RoundedRectangleBorder(
2228-
side: BorderSide(
2229-
color: Theme.of(context).colorScheme.outlineVariant,
2242+
// Tapping within the a component card should request focus
2243+
// for that component's children.
2244+
child: Focus(
2245+
focusNode: focusNode,
2246+
canRequestFocus: true,
2247+
child: GestureDetector(
2248+
onTapDown: (_) {
2249+
focusNode.requestFocus();
2250+
},
2251+
behavior: HitTestBehavior.opaque,
2252+
child: Card(
2253+
elevation: 0,
2254+
shape: RoundedRectangleBorder(
2255+
side: BorderSide(
2256+
color: Theme.of(context).colorScheme.outlineVariant,
2257+
),
2258+
borderRadius: const BorderRadius.all(Radius.circular(12)),
2259+
),
2260+
child: Padding(
2261+
padding: const EdgeInsets.symmetric(
2262+
horizontal: 5.0, vertical: 20.0),
2263+
child: Center(
2264+
child: widget.child,
2265+
),
2266+
),
22302267
),
2231-
borderRadius: const BorderRadius.all(Radius.circular(12)),
2232-
),
2233-
child: Padding(
2234-
padding: const EdgeInsets.symmetric(
2235-
horizontal: 5.0, vertical: 20.0),
2236-
child: Center(child: child),
22372268
),
22382269
),
22392270
),
@@ -2253,19 +2284,22 @@ class ComponentGroupDecoration extends StatelessWidget {
22532284

22542285
@override
22552286
Widget build(BuildContext context) {
2256-
return Card(
2257-
margin: EdgeInsets.zero,
2258-
elevation: 0,
2259-
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
2260-
child: Padding(
2261-
padding: const EdgeInsets.symmetric(vertical: 20.0),
2262-
child: Center(
2263-
child: Column(
2264-
children: [
2265-
Text(label, style: Theme.of(context).textTheme.titleLarge),
2266-
colDivider,
2267-
...children
2268-
],
2287+
// Fully traverse this component group before moving on
2288+
return FocusTraversalGroup(
2289+
child: Card(
2290+
margin: EdgeInsets.zero,
2291+
elevation: 0,
2292+
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
2293+
child: Padding(
2294+
padding: const EdgeInsets.symmetric(vertical: 20.0),
2295+
child: Center(
2296+
child: Column(
2297+
children: [
2298+
Text(label, style: Theme.of(context).textTheme.titleLarge),
2299+
colDivider,
2300+
...children
2301+
],
2302+
),
22692303
),
22702304
),
22712305
),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2021 The Flutter team. 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+
// NavigationRail shows if the screen width is greater or equal to
8+
// narrowScreenWidthThreshold; otherwise, NavigationBar is used for navigation.
9+
const double narrowScreenWidthThreshold = 450;
10+
11+
const double mediumWidthBreakpoint = 1000;
12+
const double largeWidthBreakpoint = 1500;
13+
14+
const double transitionLength = 500;
15+
16+
enum ColorSeed {
17+
baseColor('M3 Baseline', Color(0xff6750a4)),
18+
indigo('Indigo', Colors.indigo),
19+
blue('Blue', Colors.blue),
20+
teal('Teal', Colors.teal),
21+
green('Green', Colors.green),
22+
yellow('Yellow', Colors.yellow),
23+
orange('Orange', Colors.orange),
24+
deepOrange('Deep Orange', Colors.deepOrange),
25+
pink('Pink', Colors.pink);
26+
27+
const ColorSeed(this.label, this.color);
28+
final String label;
29+
final Color color;
30+
}
31+
32+
enum ScreenSelected {
33+
component(0),
34+
color(1),
35+
typography(2),
36+
elevation(3);
37+
38+
const ScreenSelected(this.value);
39+
final int value;
40+
}

0 commit comments

Comments
 (0)