| // Copyright 2019 The Flutter team. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| import 'package:gallery/data/gallery_options.dart'; |
| import 'package:gallery/l10n/gallery_localizations.dart'; |
| import 'package:gallery/layout/adaptive.dart'; |
| import 'package:gallery/studies/shrine/backdrop.dart'; |
| import 'package:gallery/studies/shrine/category_menu_page.dart'; |
| import 'package:gallery/studies/shrine/colors.dart'; |
| import 'package:gallery/studies/shrine/expanding_bottom_sheet.dart'; |
| import 'package:gallery/studies/shrine/home.dart'; |
| import 'package:gallery/studies/shrine/login.dart'; |
| import 'package:gallery/studies/shrine/model/app_state_model.dart'; |
| import 'package:gallery/studies/shrine/page_status.dart'; |
| import 'package:gallery/studies/shrine/scrim.dart'; |
| import 'package:gallery/studies/shrine/supplemental/cut_corners_border.dart'; |
| import 'package:scoped_model/scoped_model.dart'; |
| |
| class ShrineApp extends StatefulWidget { |
| const ShrineApp({Key key, this.navigatorKey}) : super(key: key); |
| |
| final GlobalKey<NavigatorState> navigatorKey; |
| |
| @override |
| _ShrineAppState createState() => _ShrineAppState(); |
| } |
| |
| class _ShrineAppState extends State<ShrineApp> with TickerProviderStateMixin { |
| // Controller to coordinate both the opening/closing of backdrop and sliding |
| // of expanding bottom sheet |
| AnimationController _controller; |
| |
| // Animation Controller for expanding/collapsing the cart menu. |
| AnimationController _expandingController; |
| |
| AppStateModel _model; |
| |
| @override |
| void initState() { |
| super.initState(); |
| _controller = AnimationController( |
| vsync: this, |
| duration: const Duration(milliseconds: 450), |
| value: 1, |
| ); |
| _expandingController = AnimationController( |
| duration: const Duration(milliseconds: 500), |
| vsync: this, |
| ); |
| _model = AppStateModel()..loadProducts(); |
| } |
| |
| @override |
| void dispose() { |
| _controller.dispose(); |
| _expandingController.dispose(); |
| super.dispose(); |
| } |
| |
| Widget mobileBackdrop() { |
| return Backdrop( |
| frontLayer: const ProductPage(), |
| backLayer: CategoryMenuPage(onCategoryTap: () => _controller.forward()), |
| frontTitle: const Text('SHRINE'), |
| backTitle: Text(GalleryLocalizations.of(context).shrineMenuCaption), |
| controller: _controller, |
| ); |
| } |
| |
| Widget desktopBackdrop() { |
| return DesktopBackdrop( |
| frontLayer: ProductPage(), |
| backLayer: CategoryMenuPage(), |
| ); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| final bool isDesktop = isDisplayDesktop(context); |
| |
| final Widget backdrop = isDesktop ? desktopBackdrop() : mobileBackdrop(); |
| |
| return ScopedModel<AppStateModel>( |
| model: _model, |
| child: MaterialApp( |
| navigatorKey: widget.navigatorKey, |
| title: 'Shrine', |
| debugShowCheckedModeBanner: false, |
| home: PageStatus( |
| menuController: _controller, |
| cartController: _expandingController, |
| child: HomePage( |
| backdrop: backdrop, |
| scrim: Scrim(controller: _expandingController), |
| expandingBottomSheet: ExpandingBottomSheet( |
| hideController: _controller, |
| expandingController: _expandingController, |
| ), |
| ), |
| ), |
| initialRoute: '/login', |
| onGenerateRoute: _getRoute, |
| theme: _shrineTheme.copyWith( |
| platform: GalleryOptions.of(context).platform, |
| ), |
| // L10n settings. |
| localizationsDelegates: GalleryLocalizations.localizationsDelegates, |
| supportedLocales: GalleryLocalizations.supportedLocales, |
| locale: GalleryOptions.of(context).locale, |
| ), |
| ); |
| } |
| } |
| |
| Route<dynamic> _getRoute(RouteSettings settings) { |
| if (settings.name != '/login') { |
| return null; |
| } |
| |
| return MaterialPageRoute<void>( |
| settings: settings, |
| builder: (context) => LoginPage(), |
| fullscreenDialog: true, |
| ); |
| } |
| |
| final ThemeData _shrineTheme = _buildShrineTheme(); |
| |
| IconThemeData _customIconTheme(IconThemeData original) { |
| return original.copyWith(color: shrineBrown900); |
| } |
| |
| ThemeData _buildShrineTheme() { |
| final ThemeData base = ThemeData.light(); |
| return base.copyWith( |
| colorScheme: shrineColorScheme, |
| accentColor: shrineBrown900, |
| primaryColor: shrinePink100, |
| buttonColor: shrinePink100, |
| scaffoldBackgroundColor: shrineBackgroundWhite, |
| cardColor: shrineBackgroundWhite, |
| textSelectionColor: shrinePink100, |
| errorColor: shrineErrorRed, |
| buttonTheme: const ButtonThemeData( |
| colorScheme: shrineColorScheme, |
| textTheme: ButtonTextTheme.normal, |
| ), |
| primaryIconTheme: _customIconTheme(base.iconTheme), |
| inputDecorationTheme: const InputDecorationTheme( |
| border: CutCornersBorder( |
| borderSide: BorderSide(color: shrineBrown900, width: 0.5), |
| ), |
| contentPadding: EdgeInsets.symmetric(vertical: 20, horizontal: 16), |
| ), |
| textTheme: _buildShrineTextTheme(base.textTheme), |
| primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme), |
| accentTextTheme: _buildShrineTextTheme(base.accentTextTheme), |
| iconTheme: _customIconTheme(base.iconTheme), |
| ); |
| } |
| |
| TextTheme _buildShrineTextTheme(TextTheme base) { |
| return base |
| .copyWith( |
| headline: base.headline.copyWith(fontWeight: FontWeight.w500), |
| title: base.title.copyWith(fontSize: 18), |
| caption: |
| base.caption.copyWith(fontWeight: FontWeight.w400, fontSize: 14), |
| body2: base.body2.copyWith(fontWeight: FontWeight.w500, fontSize: 16), |
| button: base.button.copyWith(fontWeight: FontWeight.w500, fontSize: 14), |
| ) |
| .apply( |
| fontFamily: 'Rubik', |
| displayColor: shrineBrown900, |
| bodyColor: shrineBrown900, |
| ); |
| } |
| |
| const ColorScheme shrineColorScheme = ColorScheme( |
| primary: shrinePink100, |
| primaryVariant: shrineBrown900, |
| secondary: shrinePink50, |
| secondaryVariant: shrineBrown900, |
| surface: shrineSurfaceWhite, |
| background: shrineBackgroundWhite, |
| error: shrineErrorRed, |
| onPrimary: shrineBrown900, |
| onSecondary: shrineBrown900, |
| onSurface: shrineBrown900, |
| onBackground: shrineBrown900, |
| onError: shrineSurfaceWhite, |
| brightness: Brightness.light, |
| ); |