Skip to content

Commit 1f8558a

Browse files
willlarchetianlunlee
authored andcommitted
[102] Completed code.
[101] Starter code. [101] Making AppBar something you add in 102. [Model] Adding model from the start. [103] Adding fonts. [103] Supplemental files. [101] Correcting supplemental files. [101] Copy correction. [101] Minor renaming. [101] Update for Dart 2. [101] Missing dependency. [101] Dart 2. [101] Dart 2. [101] Dart 2. [101] Dart 2. [101] Correcting model. [Meta] README file. [Meta] Clarification. [101] Copy correction. [104] Updating data. [101] Correcting widget class. (#23) Add link to actual codelab in the README [Meta] README correction. [101] README specificity. [101] Complete code. [102] Starter code. [102] Minor copy correction. [101] Update for Dart 2 [102] Rebase. Add the slanted_menu.png to assets for 101 branch so its ready when needed. (#44) (#45) [102] Complete code. [103] Starter code. [102] Correcting home being stateful. [103] Correcting order of fields. (#10) [102] Removing unneeded value. [102] Dart 2. [102] Data correction. [103] README specificity. [META] Readme correction. [104] Completed code. [101] Starter code. [101] Making AppBar something you add in 102. [Model] Adding model from the start. [103] Adding fonts. [103] Supplemental files. [101] Correcting supplemental files. [101] Copy correction. [101] Minor renaming. [101] Update for Dart 2. [101] Missing dependency. [101] Dart 2. [101] Dart 2. [101] Dart 2. [101] Dart 2. [101] Correcting model. [Meta] README file. [Meta] Clarification. [101] Copy correction. [104] Updating data. [101] Correcting widget class. (#23) Add link to actual codelab in the README [Meta] README correction. [101] README specificity. [101] Complete code. [102] Starter code. [102] Minor copy correction. [101] Update for Dart 2 [102] Rebase. Add the slanted_menu.png to assets for 101 branch so its ready when needed. (#44) (#45) [102] Complete code. [103] Starter code. [102] Correcting home being stateful. [103] Correcting order of fields. (#10) [102] Removing unneeded value. [102] Dart 2. [102] Data correction. [103] README specificity. [103] Completed code. [104] Starter code. [103] Removing unneeded directory. [103] Unused icons code. [103] Moving supplemental files. [103] Using recent changes. [103] Removing unused import. [103] Data correction. [103] Adding body2 styling. [103] Dart 2 [103] Color const correction. [104] README specificity. [103] Completed code. [104] Starter code. [102] Complete code. [103] Starter code. [103] Correcting order of fields. (#10) [103] Completed code. [104] Starter code. [103] Using recent changes. add backdrop and working menu with filtering [104] Minor visual changes. [104] Visual detail. [104] Newline at EOF. [104] License stanzas. [104] Removing files from merge. [104] Sync. [104] Correcting license [104] Adding license stanza [104] Decrease backdrop radius size to match mocks. (#21) [104] PR feedback. [104] Removing an unnecessary widget. [104] Formatting. [104] Replaces regular hamburger menu behavior with a branded version (#22) * [104] Replaces regular hamburger menu behavior with a branded icon version. * Fixed space nit and refactored branded icon into its own var. * whitespace [104] Changing "Panel" to "Layer" (#24) * [104] Clarifying backdrop. * [104] Changing panel to layer. [104] Merge sync. (#27) [104] Removing log statement. [104] Correcting import order. [104] Adding a little whitespace [104] Backdrop code review feedback (#29) * [104] PR feedback. * [104] PR feedback. * [104] Some whitespace. [104] Add branded icon menu nav animation (#28) * [101] Correcting widget class. * [101] Complete code. [102] Starter code. * [102] Minor copy correction. * [101] Update for Dart 2 * [102] Complete code. [103] Starter code. * [102] Correcting home being stateful. * [103] Correcting order of fields. (#10) * [102] Removing unneeded value. * [102] Dart 2. * [102] Data correction. * [103] Completed code. [104] Starter code. * [103] Removing unneeded directory. * [103] Unused icons code. * [103] Moving supplemental files. * [103] Using recent changes. * [103] Removing unused import. * [103] Data correction. * [103] Adding body2 styling. * [103] Dart 2 * [103] Color const correction. * [102] Complete code. [103] Starter code. * [103] Completed code. [104] Starter code. * [103] Completed code. [104] Starter code. * [103] Correcting order of fields. (#10) * add backdrop and working menu with filtering * [104] Minor visual changes. * [103] Using recent changes. * [104] Visual detail. * [104] Newline at EOF. * [104] License stanzas. * [104] Removing files from merge. * [104] Sync. * [104] Correcting license * [104] Adding license stanza * [104] Decrease backdrop radius size to match mocks. (#21) * [104] PR feedback. * [104] Removing an unnecessary widget. * [104] Formatting. * [104] Replaces regular hamburger menu behavior with a branded version (#22) * [104] Replaces regular hamburger menu behavior with a branded icon version. * Fixed space nit and refactored branded icon into its own var. * whitespace * [104] Changing "Panel" to "Layer" (#24) * [104] Clarifying backdrop. * [104] Changing panel to layer. * Start to reconfigure the _BackdropTitle to handle custom transition animations. * Added a customIcon to _BackdropTitle with the correct animations for branded icon. * Added front and back title sliding to the branded icon animation. * [104] Merge sync. (#27) * Start to reconfigure the _BackdropTitle to handle custom transition animations. * Added a customIcon to _BackdropTitle with the correct animations for branded icon. * Added front and back title sliding to the branded icon animation. * rebase fixes * Responded to comments * merge fix * Rearranged the animations and used nested widgets instead of transitions with no local vars. * Fix bad rebase * Respond to comments by replacing .animate with .evaluate [104] PR feedback. [104] Padding correction. [TextFields] PR feedback.
1 parent 574253b commit 1f8558a

File tree

8 files changed

+434
-55
lines changed

8 files changed

+434
-55
lines changed

mdc_100_series/lib/app.dart

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,68 @@
1414

1515
import 'package:flutter/material.dart';
1616

17+
import 'backdrop.dart';
1718
import 'colors.dart';
1819
import 'home.dart';
1920
import 'login.dart';
21+
import 'category_menu_page.dart';
22+
import 'model/product.dart';
2023
import 'supplemental/cut_corners_border.dart';
2124

22-
class ShrineApp extends StatelessWidget {
25+
class ShrineApp extends StatefulWidget {
26+
@override
27+
_ShrineAppState createState() => _ShrineAppState();
28+
}
29+
30+
class _ShrineAppState extends State<ShrineApp> {
31+
Category _currentCategory = Category.all;
32+
2333
@override
2434
Widget build(BuildContext context) {
2535
return MaterialApp(
2636
title: 'Shrine',
27-
home: HomePage(),
37+
home: Backdrop(
38+
currentCategory: _currentCategory,
39+
frontLayer: HomePage(category: _currentCategory),
40+
backLayer: CategoryMenuPage(
41+
currentCategory: _currentCategory,
42+
onCategoryTap: _onCategoryTap,
43+
),
44+
frontTitle: Text('SHRINE'),
45+
backTitle: Text('MENU'),
46+
),
2847
initialRoute: '/login',
2948
onGenerateRoute: _getRoute,
3049
theme: _kShrineTheme,
3150
);
3251
}
3352

34-
Route<dynamic> _getRoute(RouteSettings settings) {
35-
if (settings.name == '/login') {
36-
return MaterialPageRoute<void>(
37-
settings: settings,
38-
builder: (BuildContext context) => LoginPage(),
39-
fullscreenDialog: true,
40-
);
41-
}
53+
/// Function to call when a [Category] is tapped.
54+
void _onCategoryTap(Category category) {
55+
setState(() {
56+
_currentCategory = category;
57+
});
58+
}
59+
}
4260

43-
return null;
61+
Route<dynamic> _getRoute(RouteSettings settings) {
62+
if (settings.name == '/login') {
63+
return MaterialPageRoute<void>(
64+
settings: settings,
65+
builder: (BuildContext context) => LoginPage(),
66+
fullscreenDialog: true,
67+
);
4468
}
69+
70+
return null;
4571
}
4672

4773
final ThemeData _kShrineTheme = _buildShrineTheme();
4874

75+
IconThemeData _customIconTheme(IconThemeData original) {
76+
return original.copyWith(color: kShrineBrown900);
77+
}
78+
4979
ThemeData _buildShrineTheme() {
5080
final ThemeData base = ThemeData.light();
5181
return base.copyWith(
@@ -59,15 +89,14 @@ ThemeData _buildShrineTheme() {
5989
buttonTheme: ButtonThemeData(
6090
textTheme: ButtonTextTheme.accent,
6191
),
62-
primaryIconTheme: base.iconTheme.copyWith(
63-
color: kShrineBrown900
64-
),
92+
primaryIconTheme: base.iconTheme.copyWith(color: kShrineBrown900),
6593
inputDecorationTheme: InputDecorationTheme(
6694
border: CutCornersBorder(),
6795
),
6896
textTheme: _buildShrineTextTheme(base.textTheme),
6997
primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme),
7098
accentTextTheme: _buildShrineTextTheme(base.accentTextTheme),
99+
iconTheme: _customIconTheme(base.iconTheme),
71100
);
72101
}
73102

mdc_100_series/lib/backdrop.dart

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
// Copyright 2018-present the Flutter authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import 'package:flutter/material.dart';
16+
import 'package:meta/meta.dart';
17+
18+
import 'model/product.dart';
19+
import 'login.dart';
20+
21+
const double _kFlingVelocity = 2.0;
22+
23+
class _FrontLayer extends StatelessWidget {
24+
const _FrontLayer({
25+
Key key,
26+
this.onTap,
27+
this.child,
28+
}) : super(key: key);
29+
30+
final VoidCallback onTap;
31+
final Widget child;
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
return Material(
36+
elevation: 16.0,
37+
shape: BeveledRectangleBorder(
38+
borderRadius: BorderRadius.only(topLeft: Radius.circular(46.0)),
39+
),
40+
child: Column(
41+
crossAxisAlignment: CrossAxisAlignment.stretch,
42+
children: <Widget>[
43+
GestureDetector(
44+
behavior: HitTestBehavior.opaque,
45+
onTap: onTap,
46+
child: Container(
47+
height: 40.0,
48+
alignment: AlignmentDirectional.centerStart,
49+
),
50+
),
51+
Expanded(
52+
child: child,
53+
),
54+
],
55+
),
56+
);
57+
}
58+
}
59+
60+
class _BackdropTitle extends AnimatedWidget {
61+
final Function onPress;
62+
final Widget frontTitle;
63+
final Widget backTitle;
64+
65+
const _BackdropTitle({
66+
Key key,
67+
Listenable listenable,
68+
this.onPress,
69+
@required this.frontTitle,
70+
@required this.backTitle,
71+
}) : assert(frontTitle != null),
72+
assert(backTitle != null),
73+
super(key: key, listenable: listenable);
74+
75+
@override
76+
Widget build(BuildContext context) {
77+
final Animation<double> animation = this.listenable;
78+
79+
return DefaultTextStyle(
80+
style: Theme.of(context).primaryTextTheme.title,
81+
softWrap: false,
82+
overflow: TextOverflow.ellipsis,
83+
child: Row(children: <Widget>[
84+
// branded icon
85+
SizedBox(
86+
width: 72.0,
87+
child: IconButton(
88+
padding: EdgeInsets.only(right: 8.0),
89+
onPressed: this.onPress,
90+
icon: Stack(children: <Widget>[
91+
Opacity(
92+
opacity: animation.value,
93+
child: ImageIcon(AssetImage('assets/slanted_menu.png')),
94+
),
95+
FractionalTranslation(
96+
translation: Tween<Offset>(
97+
begin: Offset.zero,
98+
end: Offset(1.0, 0.0),
99+
).evaluate(animation),
100+
child: ImageIcon(AssetImage('assets/diamond.png')),
101+
)]),
102+
),
103+
),
104+
// Here, we do a custom cross fade between backTitle and frontTitle.
105+
// This makes a smooth animation between the two texts.
106+
Stack(
107+
children: <Widget>[
108+
Opacity(
109+
opacity: CurvedAnimation(
110+
parent: ReverseAnimation(animation),
111+
curve: Interval(0.5, 1.0),
112+
).value,
113+
child: FractionalTranslation(
114+
translation: Tween<Offset>(
115+
begin: Offset.zero,
116+
end: Offset(0.5, 0.0),
117+
).evaluate(animation),
118+
child: backTitle,
119+
),
120+
),
121+
Opacity(
122+
opacity: CurvedAnimation(
123+
parent: animation,
124+
curve: Interval(0.5, 1.0),
125+
).value,
126+
child: FractionalTranslation(
127+
translation: Tween<Offset>(
128+
begin: Offset(-0.25, 0.0),
129+
end: Offset.zero,
130+
).evaluate(animation),
131+
child: frontTitle,
132+
),
133+
),
134+
],
135+
)
136+
]),
137+
);
138+
}
139+
}
140+
141+
/// Builds a Backdrop.
142+
///
143+
/// A Backdrop widget has two layers, front and back. The front layer is shown
144+
/// by default, and slides down to show the back layer, from which a user
145+
/// can make a selection. The user can also configure the titles for when the
146+
/// front or back layer is showing.
147+
class Backdrop extends StatefulWidget {
148+
final Category currentCategory;
149+
final Widget frontLayer;
150+
final Widget backLayer;
151+
final Widget frontTitle;
152+
final Widget backTitle;
153+
154+
const Backdrop({
155+
@required this.currentCategory,
156+
@required this.frontLayer,
157+
@required this.backLayer,
158+
@required this.frontTitle,
159+
@required this.backTitle,
160+
}) : assert(currentCategory != null),
161+
assert(frontLayer != null),
162+
assert(backLayer != null),
163+
assert(frontTitle != null),
164+
assert(backTitle != null);
165+
166+
@override
167+
_BackdropState createState() => _BackdropState();
168+
}
169+
170+
class _BackdropState extends State<Backdrop>
171+
with SingleTickerProviderStateMixin {
172+
final GlobalKey _backdropKey = GlobalKey(debugLabel: 'Backdrop');
173+
AnimationController _controller;
174+
175+
@override
176+
void initState() {
177+
super.initState();
178+
_controller = AnimationController(
179+
duration: Duration(milliseconds: 300),
180+
value: 1.0,
181+
vsync: this,
182+
);
183+
}
184+
185+
@override
186+
void didUpdateWidget(Backdrop old) {
187+
super.didUpdateWidget(old);
188+
189+
if (widget.currentCategory != old.currentCategory) {
190+
_toggleBackdropLayerVisibility();
191+
} else if (!_frontLayerVisible) {
192+
_controller.fling(velocity: _kFlingVelocity);
193+
}
194+
}
195+
196+
@override
197+
void dispose() {
198+
_controller.dispose();
199+
super.dispose();
200+
}
201+
202+
bool get _frontLayerVisible {
203+
final AnimationStatus status = _controller.status;
204+
return status == AnimationStatus.completed ||
205+
status == AnimationStatus.forward;
206+
}
207+
208+
void _toggleBackdropLayerVisibility() {
209+
_controller.fling(
210+
velocity: _frontLayerVisible ? -_kFlingVelocity : _kFlingVelocity);
211+
}
212+
213+
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
214+
const double layerTitleHeight = 48.0;
215+
final Size layerSize = constraints.biggest;
216+
final double layerTop = layerSize.height - layerTitleHeight;
217+
218+
Animation<RelativeRect> layerAnimation = RelativeRectTween(
219+
begin: RelativeRect.fromLTRB(
220+
0.0, layerTop, 0.0, layerTop - layerSize.height),
221+
end: RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
222+
).animate(_controller.view);
223+
224+
return Stack(
225+
key: _backdropKey,
226+
children: <Widget>[
227+
widget.backLayer,
228+
PositionedTransition(
229+
rect: layerAnimation,
230+
child: _FrontLayer(
231+
onTap: _toggleBackdropLayerVisibility,
232+
child: widget.frontLayer,
233+
),
234+
),
235+
],
236+
);
237+
}
238+
239+
@override
240+
Widget build(BuildContext context) {
241+
var appBar = AppBar(
242+
brightness: Brightness.light,
243+
elevation: 0.0,
244+
titleSpacing: 0.0,
245+
title: _BackdropTitle(
246+
listenable: _controller.view,
247+
onPress: _toggleBackdropLayerVisibility,
248+
frontTitle: widget.frontTitle,
249+
backTitle: widget.backTitle,
250+
),
251+
actions: <Widget>[
252+
new IconButton(
253+
icon: const Icon(Icons.search),
254+
onPressed: () {
255+
Navigator.push(
256+
context,
257+
MaterialPageRoute(builder: (BuildContext context) => LoginPage()),
258+
);
259+
},
260+
),
261+
new IconButton(
262+
icon: const Icon(Icons.tune),
263+
onPressed: () {
264+
Navigator.push(
265+
context,
266+
MaterialPageRoute(builder: (BuildContext context) => LoginPage()),
267+
);
268+
},
269+
),
270+
],
271+
);
272+
return Scaffold(
273+
appBar: appBar,
274+
body: LayoutBuilder(
275+
builder: _buildStack,
276+
),
277+
);
278+
}
279+
}

0 commit comments

Comments
 (0)