mirror of
https://github.com/flutter/samples.git
synced 2025-11-12 07:48:55 +00:00
231 lines
6.8 KiB
Dart
231 lines
6.8 KiB
Dart
// 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<LoginPage> {
|
|
@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),
|
|
);
|
|
}
|
|
}
|