diff --git a/assets/banner_dark.png b/assets/banner_dark.png new file mode 100644 index 00000000..3a2fb2a7 Binary files /dev/null and b/assets/banner_dark.png differ diff --git a/lib/components/fluffy_banner.dart b/lib/components/fluffy_banner.dart new file mode 100644 index 00000000..a57ef3aa --- /dev/null +++ b/lib/components/fluffy_banner.dart @@ -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'); + } +} diff --git a/lib/components/one_page_card.dart b/lib/components/one_page_card.dart new file mode 100644 index 00000000..0e78e5b2 --- /dev/null +++ b/lib/components/one_page_card.dart @@ -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), + ); + } +} diff --git a/lib/config/themes.dart b/lib/config/themes.dart index e988fbcb..7fd63550 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -66,6 +66,13 @@ abstract class FluffyThemes { padding: EdgeInsets.all(12), ), ), + cardTheme: CardTheme( + elevation: 7, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + clipBehavior: Clip.hardEdge, + ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppConfig.borderRadius)), @@ -112,6 +119,13 @@ abstract class FluffyThemes { borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), ), + cardTheme: CardTheme( + elevation: 7, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + clipBehavior: Clip.hardEdge, + ), floatingActionButtonTheme: FloatingActionButtonThemeData( backgroundColor: AppConfig.primaryColor, 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( brightness: Brightness.dark, color: Color(0xff1D1D1D), diff --git a/lib/views/chat_list.dart b/lib/views/chat_list.dart index d26f693e..f46e2931 100644 --- a/lib/views/chat_list.dart +++ b/lib/views/chat_list.dart @@ -204,6 +204,9 @@ class _ChatListState extends State { return Scaffold( appBar: appBar ?? AppBar( + elevation: AdaptivePageLayout.of(context).columnMode(context) + ? 1 + : null, leading: selectMode == SelectMode.normal ? null : IconButton( diff --git a/lib/views/homeserver_picker.dart b/lib/views/homeserver_picker.dart index 9f6c0878..62e2025c 100644 --- a/lib/views/homeserver_picker.dart +++ b/lib/views/homeserver_picker.dart @@ -1,11 +1,12 @@ import 'dart:async'; -import 'dart:math'; import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.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/app_config.dart'; +import 'package:fluffychat/components/one_page_card.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -120,9 +121,6 @@ class _HomeserverPickerState extends State { @override Widget build(BuildContext context) { - final padding = EdgeInsets.symmetric( - horizontal: max((MediaQuery.of(context).size.width - 600) / 2, 0), - ); if (kIsWeb) { WidgetsBinding.instance.addPostFrameCallback((_) { final token = @@ -130,28 +128,28 @@ class _HomeserverPickerState extends State { _loginWithToken(token); }); } - return Scaffold( - appBar: AppBar( - title: DefaultAppBarSearchField( - prefixText: 'https://', - hintText: L10n.of(context).enterYourHomeserver, - searchController: _controller, - suffix: Icon(Icons.edit_outlined), - padding: padding, - onChanged: (s) => _domain = s, - readOnly: !AppConfig.allowOtherHomeservers, - onSubmit: (_) => _checkHomeserverAction(context), + return OnePageCard( + child: Scaffold( + appBar: AppBar( + titleSpacing: 8, + title: DefaultAppBarSearchField( + prefixText: 'https://', + hintText: L10n.of(context).enterYourHomeserver, + searchController: _controller, + suffix: Icon(Icons.edit_outlined), + padding: EdgeInsets.zero, + onChanged: (s) => _domain = s, + readOnly: !AppConfig.allowOtherHomeservers, + onSubmit: (_) => _checkHomeserverAction(context), + ), + elevation: 0, ), - elevation: 0, - ), - body: SafeArea( - child: Padding( - padding: padding, + body: SafeArea( child: ListView( children: [ Hero( tag: 'loginBanner', - child: Image.asset('assets/banner.png'), + child: FluffyBanner(), ), Padding( padding: const EdgeInsets.all(16.0), @@ -167,10 +165,7 @@ class _HomeserverPickerState extends State { ], ), ), - ), - bottomNavigationBar: Padding( - padding: padding, - child: Column( + bottomNavigationBar: Column( mainAxisSize: MainAxisSize.min, children: [ Hero( diff --git a/lib/views/login.dart b/lib/views/login.dart index dcbd3877..95dd4f73 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'dart:math'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.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:fluffychat/components/matrix.dart'; @@ -184,96 +184,95 @@ class _LoginState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: loading ? Container() : BackButton(), - elevation: 0, - title: Text( - L10n.of(context).logInTo(Matrix.of(context) - .client - .homeserver - .toString() - .replaceFirst('https://', '')), + return OnePageCard( + child: Scaffold( + appBar: AppBar( + leading: loading ? Container() : BackButton(), + elevation: 0, + title: Text( + L10n.of(context).logInTo(Matrix.of(context) + .client + .homeserver + .toString() + .replaceFirst('https://', '')), + ), ), - ), - body: Builder(builder: (context) { - return ListView( - padding: EdgeInsets.symmetric( - horizontal: - max((MediaQuery.of(context).size.width - 600) / 2, 0)), - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: TextField( - readOnly: loading, - autocorrect: false, - autofocus: true, - onChanged: (t) => _checkWellKnownWithCoolDown(t, context), - controller: usernameController, - autofillHints: loading ? null : [AutofillHints.username], - decoration: InputDecoration( - prefixIcon: Icon(Icons.account_box_outlined), - 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), - ), + body: Builder(builder: (context) { + return ListView( + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: TextField( + readOnly: loading, + autocorrect: false, + autofocus: true, + onChanged: (t) => _checkWellKnownWithCoolDown(t, context), + controller: usernameController, + autofillHints: loading ? null : [AutofillHints.username], + decoration: InputDecoration( + prefixIcon: Icon(Icons.account_box_outlined), + hintText: + '@${L10n.of(context).username.toLowerCase()}:domain', + errorText: usernameError, + labelText: L10n.of(context).username), ), ), - ), - Center( - child: TextButton( - onPressed: () => _passwordForgotten(context), - child: Text( - L10n.of(context).passwordForgotten, - style: TextStyle( - color: Colors.blue, - decoration: TextDecoration.underline, + 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), + ), ), ), ), - ), - ], - ); - }), + Center( + child: TextButton( + onPressed: () => _passwordForgotten(context), + child: Text( + L10n.of(context).passwordForgotten, + style: TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + ), + ), + ), + ], + ); + }), + ), ); } } diff --git a/lib/views/sign_up.dart b/lib/views/sign_up.dart index dfadf5df..a78092fe 100644 --- a/lib/views/sign_up.dart +++ b/lib/views/sign_up.dart @@ -1,10 +1,10 @@ -import 'dart:math'; - import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.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/one_page_card.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -67,103 +67,101 @@ class _SignUpState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - elevation: 0, - leading: loading ? Container() : BackButton(), - title: Text( - Matrix.of(context) - .client - .homeserver - .toString() - .replaceFirst('https://', ''), + return OnePageCard( + child: Scaffold( + appBar: AppBar( + elevation: 0, + leading: loading ? Container() : BackButton(), + title: Text( + Matrix.of(context) + .client + .homeserver + .toString() + .replaceFirst('https://', ''), + ), ), - ), - body: ListView( - padding: EdgeInsets.symmetric( - horizontal: - max((MediaQuery.of(context).size.width - 600) / 2, 0)), - children: [ - Hero( - tag: 'loginBanner', - child: Image.asset('assets/banner.png'), - ), - SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: TextField( - readOnly: loading, - autocorrect: false, - controller: usernameController, - onSubmitted: (s) => signUpAction(context), - autofillHints: loading ? null : [AutofillHints.newUsername], - decoration: InputDecoration( - prefixIcon: Icon(Icons.account_circle_outlined), - hintText: L10n.of(context).username, - errorText: usernameError, - labelText: L10n.of(context).chooseAUsername, - ), + body: ListView(children: [ + Hero( + tag: 'loginBanner', + child: FluffyBanner(), + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: TextField( + readOnly: loading, + autocorrect: false, + controller: usernameController, + onSubmitted: (s) => signUpAction(context), + autofillHints: loading ? null : [AutofillHints.newUsername], + decoration: InputDecoration( + prefixIcon: Icon(Icons.account_circle_outlined), + hintText: L10n.of(context).username, + errorText: usernameError, + labelText: L10n.of(context).chooseAUsername, ), ), - SizedBox(height: 8), - ListTile( - leading: CircleAvatar( - backgroundImage: - avatar == null ? null : MemoryImage(avatar.bytes), - backgroundColor: avatar == null - ? Theme.of(context).brightness == Brightness.dark - ? Color(0xff121212) - : Colors.white - : Theme.of(context).secondaryHeaderColor, - child: avatar == null - ? Icon(Icons.camera_alt_outlined, - 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: 8), + ListTile( + leading: CircleAvatar( + backgroundImage: + avatar == null ? null : MemoryImage(avatar.bytes), + backgroundColor: avatar == null + ? Theme.of(context).brightness == Brightness.dark + ? Color(0xff121212) + : Colors.white + : Theme.of(context).secondaryHeaderColor, + child: avatar == null + ? Icon(Icons.camera_alt_outlined, + color: Theme.of(context).primaryColor) + : 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, + 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), + 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, ), ), ), - ]), + ), + ]), + ), ); } } diff --git a/lib/views/sign_up_password.dart b/lib/views/sign_up_password.dart index 9eb140ff..33feccef 100644 --- a/lib/views/sign_up_password.dart +++ b/lib/views/sign_up_password.dart @@ -1,11 +1,10 @@ -import 'dart:math'; - import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/components/matrix.dart'; +import 'package:fluffychat/components/one_page_card.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -131,60 +130,60 @@ class _SignUpPasswordState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - elevation: 0, - leading: loading ? Container() : BackButton(), - title: Text( - L10n.of(context).chooseAStrongPassword, - ), - ), - body: ListView( - padding: EdgeInsets.symmetric( - horizontal: max((MediaQuery.of(context).size.width - 600) / 2, 0)), - children: [ - 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), - ), + return OnePageCard( + child: Scaffold( + appBar: AppBar( + elevation: 0, + leading: loading ? Container() : BackButton(), + title: Text( + L10n.of(context).chooseAStrongPassword, ), - 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), - ), + ), + body: ListView( + children: [ + 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( + 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), + ), + ), + ), + ), + ], + ), ), ); }