This commit is contained in:
Christian Pauly 2021-04-09 14:56:23 +02:00
parent 9f8fa5fc1e
commit 829b3b2660
9 changed files with 323 additions and 254 deletions

BIN
assets/banner_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class FluffyBanner extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Image.asset(Theme.of(context).brightness == Brightness.dark
? 'assets/banner_dark.png'
: 'assets/banner.png');
}
}

View File

@ -0,0 +1,40 @@
import 'dart:math';
import 'package:flutter/material.dart';
class OnePageCard extends StatelessWidget {
final Widget child;
const OnePageCard({Key key, this.child}) : super(key: key);
static const int alpha = 64;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
stops: [
0.1,
0.4,
0.6,
0.9,
],
colors: [
Theme.of(context).secondaryHeaderColor.withAlpha(alpha),
Theme.of(context).primaryColor.withAlpha(alpha),
Theme.of(context).accentColor.withAlpha(alpha),
Theme.of(context).backgroundColor.withAlpha(alpha),
],
),
),
padding: EdgeInsets.symmetric(
horizontal: max((MediaQuery.of(context).size.width - 600) / 2, 0),
vertical: max((MediaQuery.of(context).size.height - 800) / 2, 0),
),
child: Card(child: child),
);
}
}

View File

@ -66,6 +66,13 @@ abstract class FluffyThemes {
padding: EdgeInsets.all(12), padding: EdgeInsets.all(12),
), ),
), ),
cardTheme: CardTheme(
elevation: 7,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
clipBehavior: Clip.hardEdge,
),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius)), borderRadius: BorderRadius.circular(AppConfig.borderRadius)),
@ -112,6 +119,13 @@ abstract class FluffyThemes {
borderRadius: BorderRadius.circular(AppConfig.borderRadius), borderRadius: BorderRadius.circular(AppConfig.borderRadius),
), ),
), ),
cardTheme: CardTheme(
elevation: 7,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
clipBehavior: Clip.hardEdge,
),
floatingActionButtonTheme: FloatingActionButtonThemeData( floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: AppConfig.primaryColor, backgroundColor: AppConfig.primaryColor,
foregroundColor: Colors.white, foregroundColor: Colors.white,
@ -128,6 +142,17 @@ abstract class FluffyThemes {
), ),
), ),
), ),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
primary: AppConfig.primaryColor,
onPrimary: Colors.white,
elevation: 7,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
padding: EdgeInsets.all(12),
),
),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
brightness: Brightness.dark, brightness: Brightness.dark,
color: Color(0xff1D1D1D), color: Color(0xff1D1D1D),

View File

@ -204,6 +204,9 @@ class _ChatListState extends State<ChatList> {
return Scaffold( return Scaffold(
appBar: appBar ?? appBar: appBar ??
AppBar( AppBar(
elevation: AdaptivePageLayout.of(context).columnMode(context)
? 1
: null,
leading: selectMode == SelectMode.normal leading: selectMode == SelectMode.normal
? null ? null
: IconButton( : IconButton(

View File

@ -1,11 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/default_app_bar_search_field.dart'; import 'package:fluffychat/components/default_app_bar_search_field.dart';
import 'package:fluffychat/components/fluffy_banner.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/app_config.dart';
import 'package:fluffychat/components/one_page_card.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
@ -120,9 +121,6 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final padding = EdgeInsets.symmetric(
horizontal: max((MediaQuery.of(context).size.width - 600) / 2, 0),
);
if (kIsWeb) { if (kIsWeb) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final token = final token =
@ -130,28 +128,28 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
_loginWithToken(token); _loginWithToken(token);
}); });
} }
return Scaffold( return OnePageCard(
appBar: AppBar( child: Scaffold(
title: DefaultAppBarSearchField( appBar: AppBar(
prefixText: 'https://', titleSpacing: 8,
hintText: L10n.of(context).enterYourHomeserver, title: DefaultAppBarSearchField(
searchController: _controller, prefixText: 'https://',
suffix: Icon(Icons.edit_outlined), hintText: L10n.of(context).enterYourHomeserver,
padding: padding, searchController: _controller,
onChanged: (s) => _domain = s, suffix: Icon(Icons.edit_outlined),
readOnly: !AppConfig.allowOtherHomeservers, padding: EdgeInsets.zero,
onSubmit: (_) => _checkHomeserverAction(context), onChanged: (s) => _domain = s,
readOnly: !AppConfig.allowOtherHomeservers,
onSubmit: (_) => _checkHomeserverAction(context),
),
elevation: 0,
), ),
elevation: 0, body: SafeArea(
),
body: SafeArea(
child: Padding(
padding: padding,
child: ListView( child: ListView(
children: [ children: [
Hero( Hero(
tag: 'loginBanner', tag: 'loginBanner',
child: Image.asset('assets/banner.png'), child: FluffyBanner(),
), ),
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
@ -167,10 +165,7 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
], ],
), ),
), ),
), bottomNavigationBar: Column(
bottomNavigationBar: Padding(
padding: padding,
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Hero( Hero(

View File

@ -1,9 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/one_page_card.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
@ -184,96 +184,95 @@ class _LoginState extends State<Login> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return OnePageCard(
appBar: AppBar( child: Scaffold(
leading: loading ? Container() : BackButton(), appBar: AppBar(
elevation: 0, leading: loading ? Container() : BackButton(),
title: Text( elevation: 0,
L10n.of(context).logInTo(Matrix.of(context) title: Text(
.client L10n.of(context).logInTo(Matrix.of(context)
.homeserver .client
.toString() .homeserver
.replaceFirst('https://', '')), .toString()
.replaceFirst('https://', '')),
),
), ),
), body: Builder(builder: (context) {
body: Builder(builder: (context) { return ListView(
return ListView( children: <Widget>[
padding: EdgeInsets.symmetric( Padding(
horizontal: padding: const EdgeInsets.all(12.0),
max((MediaQuery.of(context).size.width - 600) / 2, 0)), child: TextField(
children: <Widget>[ readOnly: loading,
Padding( autocorrect: false,
padding: const EdgeInsets.all(12.0), autofocus: true,
child: TextField( onChanged: (t) => _checkWellKnownWithCoolDown(t, context),
readOnly: loading, controller: usernameController,
autocorrect: false, autofillHints: loading ? null : [AutofillHints.username],
autofocus: true, decoration: InputDecoration(
onChanged: (t) => _checkWellKnownWithCoolDown(t, context), prefixIcon: Icon(Icons.account_box_outlined),
controller: usernameController, hintText:
autofillHints: loading ? null : [AutofillHints.username], '@${L10n.of(context).username.toLowerCase()}:domain',
decoration: InputDecoration( errorText: usernameError,
prefixIcon: Icon(Icons.account_box_outlined), labelText: L10n.of(context).username),
hintText:
'@${L10n.of(context).username.toLowerCase()}:domain',
errorText: usernameError,
labelText: L10n.of(context).username),
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
readOnly: loading,
autocorrect: false,
autofillHints: loading ? null : [AutofillHints.password],
controller: passwordController,
obscureText: !showPassword,
onSubmitted: (t) => login(context),
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outlined),
hintText: '****',
errorText: passwordError,
suffixIcon: IconButton(
tooltip: L10n.of(context).showPassword,
icon: Icon(showPassword
? Icons.visibility_off_outlined
: Icons.visibility_outlined),
onPressed: () =>
setState(() => showPassword = !showPassword),
),
labelText: L10n.of(context).password),
),
),
SizedBox(height: 12),
Hero(
tag: 'loginButton',
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: ElevatedButton(
onPressed: loading ? null : () => login(context),
child: loading
? LinearProgressIndicator()
: Text(
L10n.of(context).login.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16),
),
), ),
), ),
), Padding(
Center( padding: const EdgeInsets.all(12.0),
child: TextButton( child: TextField(
onPressed: () => _passwordForgotten(context), readOnly: loading,
child: Text( autocorrect: false,
L10n.of(context).passwordForgotten, autofillHints: loading ? null : [AutofillHints.password],
style: TextStyle( controller: passwordController,
color: Colors.blue, obscureText: !showPassword,
decoration: TextDecoration.underline, onSubmitted: (t) => login(context),
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outlined),
hintText: '****',
errorText: passwordError,
suffixIcon: IconButton(
tooltip: L10n.of(context).showPassword,
icon: Icon(showPassword
? Icons.visibility_off_outlined
: Icons.visibility_outlined),
onPressed: () =>
setState(() => showPassword = !showPassword),
),
labelText: L10n.of(context).password),
),
),
SizedBox(height: 12),
Hero(
tag: 'loginButton',
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: ElevatedButton(
onPressed: loading ? null : () => login(context),
child: loading
? LinearProgressIndicator()
: Text(
L10n.of(context).login.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16),
),
), ),
), ),
), ),
), Center(
], child: TextButton(
); onPressed: () => _passwordForgotten(context),
}), child: Text(
L10n.of(context).passwordForgotten,
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
),
),
],
);
}),
),
); );
} }
} }

View File

@ -1,10 +1,10 @@
import 'dart:math';
import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/components/fluffy_banner.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/components/one_page_card.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -67,103 +67,101 @@ class _SignUpState extends State<SignUp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return OnePageCard(
appBar: AppBar( child: Scaffold(
elevation: 0, appBar: AppBar(
leading: loading ? Container() : BackButton(), elevation: 0,
title: Text( leading: loading ? Container() : BackButton(),
Matrix.of(context) title: Text(
.client Matrix.of(context)
.homeserver .client
.toString() .homeserver
.replaceFirst('https://', ''), .toString()
.replaceFirst('https://', ''),
),
), ),
), body: ListView(children: <Widget>[
body: ListView( Hero(
padding: EdgeInsets.symmetric( tag: 'loginBanner',
horizontal: child: FluffyBanner(),
max((MediaQuery.of(context).size.width - 600) / 2, 0)), ),
children: <Widget>[ SizedBox(height: 16),
Hero( Padding(
tag: 'loginBanner', padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Image.asset('assets/banner.png'), child: TextField(
), readOnly: loading,
SizedBox(height: 16), autocorrect: false,
Padding( controller: usernameController,
padding: const EdgeInsets.symmetric(horizontal: 12.0), onSubmitted: (s) => signUpAction(context),
child: TextField( autofillHints: loading ? null : [AutofillHints.newUsername],
readOnly: loading, decoration: InputDecoration(
autocorrect: false, prefixIcon: Icon(Icons.account_circle_outlined),
controller: usernameController, hintText: L10n.of(context).username,
onSubmitted: (s) => signUpAction(context), errorText: usernameError,
autofillHints: loading ? null : [AutofillHints.newUsername], labelText: L10n.of(context).chooseAUsername,
decoration: InputDecoration(
prefixIcon: Icon(Icons.account_circle_outlined),
hintText: L10n.of(context).username,
errorText: usernameError,
labelText: L10n.of(context).chooseAUsername,
),
), ),
), ),
SizedBox(height: 8), ),
ListTile( SizedBox(height: 8),
leading: CircleAvatar( ListTile(
backgroundImage: leading: CircleAvatar(
avatar == null ? null : MemoryImage(avatar.bytes), backgroundImage:
backgroundColor: avatar == null avatar == null ? null : MemoryImage(avatar.bytes),
? Theme.of(context).brightness == Brightness.dark backgroundColor: avatar == null
? Color(0xff121212) ? Theme.of(context).brightness == Brightness.dark
: Colors.white ? Color(0xff121212)
: Theme.of(context).secondaryHeaderColor, : Colors.white
child: avatar == null : Theme.of(context).secondaryHeaderColor,
? Icon(Icons.camera_alt_outlined, child: avatar == null
color: Theme.of(context).primaryColor) ? Icon(Icons.camera_alt_outlined,
: null, color: Theme.of(context).primaryColor)
), : null,
trailing: avatar == null
? null
: Icon(
Icons.close,
color: Colors.red,
),
title: Text(avatar == null
? L10n.of(context).setAProfilePicture
: L10n.of(context).discardPicture),
onTap: avatar == null
? setAvatarAction
: () => setState(() => avatar = null),
), ),
SizedBox(height: 16), trailing: avatar == null
Hero( ? null
tag: 'loginButton', : Icon(
child: Padding( Icons.close,
padding: EdgeInsets.symmetric(horizontal: 12), color: Colors.red,
child: ElevatedButton(
onPressed: loading ? null : () => signUpAction(context),
child: loading
? LinearProgressIndicator()
: Text(
L10n.of(context).signUp.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
),
Center(
child: TextButton(
onPressed: () =>
AdaptivePageLayout.of(context).pushNamed('/login'),
child: Text(
L10n.of(context).alreadyHaveAnAccount,
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.blue,
fontSize: 16,
), ),
title: Text(avatar == null
? L10n.of(context).setAProfilePicture
: L10n.of(context).discardPicture),
onTap: avatar == null
? setAvatarAction
: () => setState(() => avatar = null),
),
SizedBox(height: 16),
Hero(
tag: 'loginButton',
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: ElevatedButton(
onPressed: loading ? null : () => signUpAction(context),
child: loading
? LinearProgressIndicator()
: Text(
L10n.of(context).signUp.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
),
Center(
child: TextButton(
onPressed: () =>
AdaptivePageLayout.of(context).pushNamed('/login'),
child: Text(
L10n.of(context).alreadyHaveAnAccount,
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.blue,
fontSize: 16,
), ),
), ),
), ),
]), ),
]),
),
); );
} }
} }

View File

@ -1,11 +1,10 @@
import 'dart:math';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/components/one_page_card.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -131,60 +130,60 @@ class _SignUpPasswordState extends State<SignUpPassword> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return OnePageCard(
appBar: AppBar( child: Scaffold(
elevation: 0, appBar: AppBar(
leading: loading ? Container() : BackButton(), elevation: 0,
title: Text( leading: loading ? Container() : BackButton(),
L10n.of(context).chooseAStrongPassword, title: Text(
), L10n.of(context).chooseAStrongPassword,
),
body: ListView(
padding: EdgeInsets.symmetric(
horizontal: max((MediaQuery.of(context).size.width - 600) / 2, 0)),
children: <Widget>[
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
controller: passwordController,
obscureText: !showPassword,
autofocus: true,
readOnly: loading,
autocorrect: false,
onSubmitted: (t) => _signUpAction(context),
autofillHints: loading ? null : [AutofillHints.newPassword],
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outlined),
hintText: '****',
errorText: passwordError,
suffixIcon: IconButton(
tooltip: L10n.of(context).showPassword,
icon: Icon(showPassword
? Icons.visibility_off_outlined
: Icons.visibility_outlined),
onPressed: () =>
setState(() => showPassword = !showPassword),
),
labelText: L10n.of(context).password),
),
), ),
SizedBox(height: 12), ),
Hero( body: ListView(
tag: 'loginButton', children: <Widget>[
child: Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.all(12.0),
child: ElevatedButton( child: TextField(
onPressed: loading ? null : () => _signUpAction(context), controller: passwordController,
child: loading obscureText: !showPassword,
? LinearProgressIndicator() autofocus: true,
: Text( readOnly: loading,
L10n.of(context).createAccountNow.toUpperCase(), autocorrect: false,
style: TextStyle(color: Colors.white, fontSize: 16), onSubmitted: (t) => _signUpAction(context),
), autofillHints: loading ? null : [AutofillHints.newPassword],
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outlined),
hintText: '****',
errorText: passwordError,
suffixIcon: IconButton(
tooltip: L10n.of(context).showPassword,
icon: Icon(showPassword
? Icons.visibility_off_outlined
: Icons.visibility_outlined),
onPressed: () =>
setState(() => showPassword = !showPassword),
),
labelText: L10n.of(context).password),
), ),
), ),
), SizedBox(height: 12),
], Hero(
tag: 'loginButton',
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: ElevatedButton(
onPressed: loading ? null : () => _signUpAction(context),
child: loading
? LinearProgressIndicator()
: Text(
L10n.of(context).createAccountNow.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
),
],
),
), ),
); );
} }