// 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 'dart:math'; import 'package:flutter/material.dart'; import 'package:gallery/data/gallery_options.dart'; import 'package:gallery/layout/adaptive.dart'; import 'package:gallery/layout/text_scale.dart'; import 'package:gallery/l10n/gallery_localizations.dart'; import 'package:gallery/studies/shrine/colors.dart'; import 'package:gallery/studies/shrine/theme.dart'; const _horizontalPadding = 24.0; double desktopLoginScreenMainAreaWidth({BuildContext context}) { return min( 360 * reducedTextScale(context), MediaQuery.of(context).size.width - 2 * _horizontalPadding, ); } class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State { @override Widget build(BuildContext context) { final bool isDesktop = isDisplayDesktop(context); return ApplyTextOptions( child: isDesktop ? LayoutBuilder( builder: (context, constraints) => Scaffold( body: SafeArea( child: Center( child: Container( width: desktopLoginScreenMainAreaWidth(context: context), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: const [ _ShrineLogo(), SizedBox(height: 40), _UsernameTextField(), SizedBox(height: 16), _PasswordTextField(), SizedBox(height: 24), _CancelAndNextButtons(), SizedBox(height: 62), ], ), ), ), ), ), ) : Scaffold( body: SafeArea( child: ListView( physics: ClampingScrollPhysics(), padding: const EdgeInsets.symmetric( horizontal: _horizontalPadding, ), children: const [ SizedBox(height: 80), _ShrineLogo(), SizedBox(height: 120), _UsernameTextField(), SizedBox(height: 12), _PasswordTextField(), _CancelAndNextButtons(), ], ), ), ), ); } } class _ShrineLogo extends StatelessWidget { const _ShrineLogo(); @override Widget build(BuildContext context) { return ExcludeSemantics( child: Column( children: [ Image.asset('packages/shrine_images/diamond.png'), const SizedBox(height: 16), Text( 'SHRINE', style: Theme.of(context).textTheme.headline, ), ], ), ); } } class _UsernameTextField extends StatelessWidget { const _UsernameTextField(); @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context).colorScheme; final TextEditingController _usernameController = TextEditingController(); return PrimaryColorOverride( color: shrineBrown900, child: Container( child: TextField( controller: _usernameController, cursorColor: colorScheme.onSurface, decoration: InputDecoration( labelText: GalleryLocalizations.of(context).shrineLoginUsernameLabel, labelStyle: TextStyle(letterSpacing: mediumLetterSpacing), ), ), ), ); } } class _PasswordTextField extends StatelessWidget { const _PasswordTextField(); @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context).colorScheme; final TextEditingController _passwordController = TextEditingController(); return PrimaryColorOverride( color: shrineBrown900, child: Container( child: TextField( controller: _passwordController, cursorColor: colorScheme.onSurface, obscureText: true, decoration: InputDecoration( labelText: GalleryLocalizations.of(context).shrineLoginPasswordLabel, labelStyle: TextStyle(letterSpacing: mediumLetterSpacing), ), ), ), ); } } class _CancelAndNextButtons extends StatelessWidget { const _CancelAndNextButtons(); @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context).colorScheme; final bool isDesktop = isDisplayDesktop(context); final EdgeInsets buttonTextPadding = isDesktop ? EdgeInsets.symmetric(horizontal: 24, vertical: 16) : EdgeInsets.zero; return Wrap( children: [ ButtonBar( buttonPadding: isDesktop ? EdgeInsets.zero : null, children: [ FlatButton( child: Padding( padding: buttonTextPadding, child: Text( GalleryLocalizations.of(context).shrineCancelButtonCaption, style: TextStyle(color: colorScheme.onSurface), ), ), shape: const BeveledRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(7)), ), onPressed: () { // The login screen is immediately displayed on top of // the Shrine home screen using onGenerateRoute and so // rootNavigator must be set to true in order to get out // of Shrine completely. Navigator.of(context, rootNavigator: true).pop(); }, ), RaisedButton( child: Padding( padding: buttonTextPadding, child: Text( GalleryLocalizations.of(context).shrineNextButtonCaption, style: TextStyle(letterSpacing: largeLetterSpacing), ), ), elevation: 8, shape: const BeveledRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(7)), ), onPressed: () { Navigator.pop(context); }, ), ], ), ], ); } } 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), ); } }