diff --git a/README.md b/README.md index 514bb2d760..0233d57b03 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Codelabs MDC-101 through MDC-104 will guide you through building and integrating The starter and completed code is in the various branches of this repo. ## Getting Started -Visit the [Google codelabs site](https://codelabs.developers.google.com/), or [codelabs.developers.google.com/codelabs/mdc-103-flutter](https://codelabs.developers.google.com/codelabs/mdc-103-flutter), to follow along the guided steps. +Visit the [Google codelabs site](https://codelabs.developers.google.com/), or [codelabs.developers.google.com/codelabs/mdc-104-flutter](https://codelabs.developers.google.com/codelabs/mdc-104-flutter), to follow along the guided steps. ## Support diff --git a/mdc_100_series/lib/app.dart b/mdc_100_series/lib/app.dart index 092fa2d467..3bcfbd4b88 100644 --- a/mdc_100_series/lib/app.dart +++ b/mdc_100_series/lib/app.dart @@ -14,8 +14,10 @@ import 'package:flutter/material.dart'; +import 'colors.dart'; import 'home.dart'; import 'login.dart'; +import 'supplemental/cut_corners_border.dart'; // TODO: Convert ShrineApp to stateful widget (104) class ShrineApp extends StatelessWidget { @@ -31,7 +33,7 @@ class ShrineApp extends StatelessWidget { // TODO: Change backLayer field value to CategoryMenuPage (104) initialRoute: '/login', onGenerateRoute: _getRoute, - // TODO: Add a theme (103) + theme: _kShrineTheme, ); } @@ -48,5 +50,52 @@ class ShrineApp extends StatelessWidget { } } -// TODO: Build a Shrine Theme (103) -// TODO: Build a Shrine Text Theme (103) +final ThemeData _kShrineTheme = _buildShrineTheme(); + +ThemeData _buildShrineTheme() { + final ThemeData base = ThemeData.light(); + return base.copyWith( + accentColor: kShrineBrown900, + primaryColor: kShrinePink100, + buttonColor: kShrinePink100, + scaffoldBackgroundColor: kShrineBackgroundWhite, + cardColor: kShrineBackgroundWhite, + textSelectionColor: kShrinePink100, + errorColor: kShrineErrorRed, + buttonTheme: ButtonThemeData( + textTheme: ButtonTextTheme.accent, + ), + primaryIconTheme: base.iconTheme.copyWith( + color: kShrineBrown900 + ), + inputDecorationTheme: InputDecorationTheme( + border: CutCornersBorder(), + ), + textTheme: _buildShrineTextTheme(base.textTheme), + primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme), + accentTextTheme: _buildShrineTextTheme(base.accentTextTheme), + ); +} + +TextTheme _buildShrineTextTheme(TextTheme base) { + return base.copyWith( + headline: base.headline.copyWith( + fontWeight: FontWeight.w500, + ), + title: base.title.copyWith( + fontSize: 18.0 + ), + caption: base.caption.copyWith( + fontWeight: FontWeight.w400, + fontSize: 14.0, + ), + body2: base.body2.copyWith( + fontWeight: FontWeight.w500, + fontSize: 16.0, + ), + ).apply( + fontFamily: 'Rubik', + displayColor: kShrineBrown900, + bodyColor: kShrineBrown900, + ); +} diff --git a/mdc_100_series/lib/colors.dart b/mdc_100_series/lib/colors.dart new file mode 100644 index 0000000000..b1ecd49a9d --- /dev/null +++ b/mdc_100_series/lib/colors.dart @@ -0,0 +1,27 @@ +// Copyright 2018-present the Flutter authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:flutter/material.dart'; + +const kShrinePink50 = const Color(0xFFFEEAE6); +const kShrinePink100 = const Color(0xFFFEDBD0); +const kShrinePink300 = const Color(0xFFFBB8AC); +const kShrinePink400 = const Color(0xFFEAA4A4); + +const kShrineBrown900 = const Color(0xFF442B2D); + +const kShrineErrorRed = const Color(0xFFC5032B); + +const kShrineSurfaceWhite = const Color(0xFFFFFBFA); +const kShrineBackgroundWhite = Colors.white; \ No newline at end of file diff --git a/mdc_100_series/lib/home.dart b/mdc_100_series/lib/home.dart index 83ab8802db..c7695b1ebc 100644 --- a/mdc_100_series/lib/home.dart +++ b/mdc_100_series/lib/home.dart @@ -13,77 +13,21 @@ // limitations under the License. import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'model/data.dart'; import 'model/product.dart'; +import 'supplemental/asymmetric_view.dart'; class HomePage extends StatelessWidget { // TODO: Add a variable for Category (104) - List _buildGridCards(BuildContext context) { - List products = getProducts(Category.all); - - if (products == null || products.isEmpty) { - return []; - } - - final ThemeData theme = Theme.of(context); - final NumberFormat formatter = NumberFormat.simpleCurrency( - locale: Localizations.localeOf(context).toString()); - - return products.map((product) { - return Card( - // TODO: Adjust card heights (103) - child: Column( - // TODO: Center items on the card (103) - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: 18 / 11, - child: Image.asset( - product.assetName, - package: product.assetPackage, - fit: BoxFit.fitWidth, - ), - ), - Expanded( - child: Padding( - padding: EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0), - child: Column( - // TODO: Align labels to the bottom and center (103) - crossAxisAlignment: CrossAxisAlignment.start, - // TODO: Change innermost Column (103) - children: [ - // TODO(larche): Make headline6 when available - Text( - product.name, - style: theme.textTheme.title, - maxLines: 1, - ), - SizedBox(height: 8.0), - // TODO(larche): Make subtitle2 when available - Text( - formatter.format(product.price), - style: theme.textTheme.body2, - ), - ], - ), - ), - ), - ], - ), - ); - }).toList(); - } - @override Widget build(BuildContext context) { // TODO: Return an AsymmetricView (104) // TODO: Pass Category variable to AsymmetricView (104) - return Scaffold( appBar: AppBar( + brightness: Brightness.light, leading: IconButton( icon: Icon(Icons.menu), onPressed: () { @@ -106,14 +50,7 @@ class HomePage extends StatelessWidget { ), ], ), - body: Center( - child: GridView.count( - crossAxisCount: 2, - padding: EdgeInsets.all(16.0), - childAspectRatio: 8.0 / 9.0, - children: _buildGridCards(context), - ), - ), + body: AsymmetricView(products: getProducts(Category.all)), ); } } diff --git a/mdc_100_series/lib/login.dart b/mdc_100_series/lib/login.dart index 50eb0471a7..4fd9272ffd 100644 --- a/mdc_100_series/lib/login.dart +++ b/mdc_100_series/lib/login.dart @@ -14,6 +14,8 @@ import 'package:flutter/material.dart'; +import 'colors.dart'; + class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); @@ -35,43 +37,50 @@ class _LoginPageState extends State { children: [ Image.asset('assets/diamond.png'), SizedBox(height: 16.0), - Text('SHRINE'), + Text( + 'SHRINE', + style: Theme.of(context).textTheme.headline, + ), ], ), SizedBox(height: 120.0), - // TODO: Wrap Username with PrimaryColorOverride (103) - // TODO: Remove filled: true values (103) - TextField( - controller: _usernameController, - decoration: InputDecoration( - filled: true, - labelText: 'Username', + PrimaryColorOverride( + color: kShrineBrown900, + child: TextField( + controller: _usernameController, + decoration: InputDecoration( + labelText: 'Username', + ), ), ), - SizedBox(height: 12.0), - // TODO: Wrap Password with PrimaryColorOverride (103) - TextField( - controller: _passwordController, - decoration: InputDecoration( - filled: true, - labelText: 'Password', + const SizedBox(height: 12.0), + new PrimaryColorOverride( + color: kShrineBrown900, + child: TextField( + controller: _passwordController, + decoration: InputDecoration( + labelText: 'Password', + ), ), - obscureText: true, ), ButtonBar( children: [ - // TODO: Add a beveled rectangular border to CANCEL (103) FlatButton( child: Text('CANCEL'), + shape: BeveledRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(7.0)), + ), onPressed: () { _usernameController.clear(); _passwordController.clear(); }, ), - // TODO: Add an elevation to NEXT (103) - // TODO: Add a beveled rectangular border to NEXT (103) RaisedButton( child: Text('NEXT'), + elevation: 8.0, + shape: BeveledRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(7.0)), + ), onPressed: () { Navigator.pop(context); }, @@ -85,4 +94,18 @@ class _LoginPageState extends State { } } -// TODO: Add PrimaryColorOverride (103) \ No newline at end of file +class PrimaryColorOverride extends StatelessWidget { + const PrimaryColorOverride({Key key, this.color, this.child}) + : super(key: key); + + final Color color; + final Widget child; + + @override + Widget build(BuildContext context) { + return Theme( + child: child, + data: Theme.of(context).copyWith(primaryColor: color), + ); + } +} diff --git a/mdc_100_series/pubspec.yaml b/mdc_100_series/pubspec.yaml index f0dd7a3dd9..3d2e358621 100644 --- a/mdc_100_series/pubspec.yaml +++ b/mdc_100_series/pubspec.yaml @@ -1,5 +1,5 @@ name: Shrine -description: Learn how to use Material for structure and layout. +description: Take your design up a notch and learn to use our advanced component backdrop menu. dependencies: flutter: @@ -55,3 +55,9 @@ flutter: - packages/shrine_images/35-0.jpg - packages/shrine_images/36-0.jpg - packages/shrine_images/37-0.jpg + fonts: + - family: Rubik + fonts: + - asset: fonts/Rubik-Regular.ttf + - asset: fonts/Rubik-Medium.ttf + weight: 500