diff --git a/lib/main.dart b/lib/main.dart index ed54402e..31213e24 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -32,7 +32,6 @@ import 'package:twonly/src/services/background/callback_dispatcher.background.da import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/services/memories/memories.service.dart'; - import 'package:twonly/src/services/notifications/fcm.notifications.dart'; import 'package:twonly/src/services/notifications/setup.notifications.dart'; import 'package:twonly/src/services/user.service.dart'; diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index 2f23de79..62dfb10f 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -98,16 +98,10 @@ abstract class AppLocalizations { Locale('en'), ]; - /// No description provided for @registerTitle. - /// - /// In en, this message translates to: - /// **'Welcome to twonly!'** - String get registerTitle; - /// No description provided for @registerSlogan. /// /// In en, this message translates to: - /// **'twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing'** + /// **'Stay in touch with friends privately and securely.'** String get registerSlogan; /// No description provided for @onboardingWelcomeTitle. @@ -179,7 +173,7 @@ abstract class AppLocalizations { /// No description provided for @registerUsernameSlogan. /// /// In en, this message translates to: - /// **'Please select a username so others can find you!'** + /// **'Your public username'** String get registerUsernameSlogan; /// No description provided for @registerUsernameDecoration. @@ -191,7 +185,7 @@ abstract class AppLocalizations { /// No description provided for @registerUsernameLimits. /// /// In en, this message translates to: - /// **'Your username must be at least 3 characters long.'** + /// **'At least 3 characters.'** String get registerUsernameLimits; /// No description provided for @registerProofOfWorkFailed. @@ -1583,7 +1577,7 @@ abstract class AppLocalizations { /// No description provided for @twonlySafeRecoverTitle. /// /// In en, this message translates to: - /// **'Recovery'** + /// **'Restore backup'** String get twonlySafeRecoverTitle; /// No description provided for @twonlySafeRecoverDesc. diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart index 98774530..a18dab76 100644 --- a/lib/src/localization/generated/app_localizations_de.dart +++ b/lib/src/localization/generated/app_localizations_de.dart @@ -8,12 +8,9 @@ import 'app_localizations.dart'; class AppLocalizationsDe extends AppLocalizations { AppLocalizationsDe([String locale = 'de']) : super(locale); - @override - String get registerTitle => 'Willkommen bei twonly!'; - @override String get registerSlogan => - 'twonly, eine private und sichere Möglichkeit um mit Freunden in Kontakt zu bleiben.'; + 'Privat und sicher mit Freunden in Kontakt bleiben.'; @override String get onboardingWelcomeTitle => 'Willkommen bei twonly!'; @@ -55,15 +52,13 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardingGetStartedTitle => 'Auf geht\'s'; @override - String get registerUsernameSlogan => - 'Bitte wähle einen Benutzernamen, damit dich andere finden können!'; + String get registerUsernameSlogan => 'Dein öffentlicher Benutzername'; @override String get registerUsernameDecoration => 'Benutzername'; @override - String get registerUsernameLimits => - 'Der Benutzername muss mindestens 3 Zeichen lang sein.'; + String get registerUsernameLimits => 'Mindestens 3 Zeichen.'; @override String get registerProofOfWorkFailed => @@ -816,7 +811,7 @@ class AppLocalizationsDe extends AppLocalizations { String get backupChangePassword => 'Password ändern'; @override - String get twonlySafeRecoverTitle => 'Recovery'; + String get twonlySafeRecoverTitle => 'Backup wiederherstellen'; @override String get twonlySafeRecoverDesc => diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index 5235e81a..bcb668bc 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -8,12 +8,9 @@ import 'app_localizations.dart'; class AppLocalizationsEn extends AppLocalizations { AppLocalizationsEn([String locale = 'en']) : super(locale); - @override - String get registerTitle => 'Welcome to twonly!'; - @override String get registerSlogan => - 'twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing'; + 'Stay in touch with friends privately and securely.'; @override String get onboardingWelcomeTitle => 'Welcome to twonly!'; @@ -54,15 +51,13 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardingGetStartedTitle => 'Let\'s go!'; @override - String get registerUsernameSlogan => - 'Please select a username so others can find you!'; + String get registerUsernameSlogan => 'Your public username'; @override String get registerUsernameDecoration => 'Username'; @override - String get registerUsernameLimits => - 'Your username must be at least 3 characters long.'; + String get registerUsernameLimits => 'At least 3 characters.'; @override String get registerProofOfWorkFailed => @@ -810,7 +805,7 @@ class AppLocalizationsEn extends AppLocalizations { String get backupChangePassword => 'Change password'; @override - String get twonlySafeRecoverTitle => 'Recovery'; + String get twonlySafeRecoverTitle => 'Restore backup'; @override String get twonlySafeRecoverDesc => diff --git a/lib/src/localization/translations b/lib/src/localization/translations index c41aaeff..8660b9b1 160000 --- a/lib/src/localization/translations +++ b/lib/src/localization/translations @@ -1 +1 @@ -Subproject commit c41aaeff47007ddd5484fe1124bb15b905c3b247 +Subproject commit 8660b9b11721adda59e387a129f9017bb9e72c67 diff --git a/lib/src/visual/views/onboarding/components/link_logo_animation.dart b/lib/src/visual/views/onboarding/components/link_logo_animation.dart new file mode 100644 index 00000000..3a9dd31f --- /dev/null +++ b/lib/src/visual/views/onboarding/components/link_logo_animation.dart @@ -0,0 +1,101 @@ +import 'dart:math' as math; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class LinkLogoAnimation extends StatefulWidget { + const LinkLogoAnimation({ + super.key, + this.size = 130, + this.color = Colors.white, + }); + + final double size; + final Color color; + + @override + State createState() => _LinkLogoAnimationState(); +} + +class _LinkLogoAnimationState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _rotation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + )..repeat(reverse: true); + + _rotation = + Tween( + begin: -2.0 * (math.pi / 180.0), + end: 2.0 * (math.pi / 180.0), + ).animate( + CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + const originalViewportSize = 640.0; + + const path1 = + 'M451.5 160C434.9 160 418.8 164.5 404.7 172.7C388.9 156.7 370.5 143.3 350.2 133.2C378.4 109.2 414.3 96 451.5 96C537.9 96 608 166 608 252.5C608 294 591.5 333.8 562.2 363.1L491.1 434.2C461.8 463.5 422 480 380.5 480C294.1 480 224 410 224 323.5C224 322 224 320.5 224.1 319C224.6 301.3 239.3 287.4 257 287.9C274.7 288.4 288.6 303.1 288.1 320.8C288.1 321.7 288.1 322.6 288.1 323.4C288.1 374.5 329.5 415.9 380.6 415.9C405.1 415.9 428.6 406.2 446 388.8L517.1 317.7C534.4 300.4 544.2 276.8 544.2 252.3C544.2 201.2 502.8 159.8 451.7 159.8z'; + const path2 = + 'M307.2 237.3C305.3 236.5 303.4 235.4 301.7 234.2C289.1 227.7 274.7 224 259.6 224C235.1 224 211.6 233.7 194.2 251.1L123.1 322.2C105.8 339.5 96 363.1 96 387.6C96 438.7 137.4 480.1 188.5 480.1C205 480.1 221.1 475.7 235.2 467.5C251 483.5 269.4 496.9 289.8 507C261.6 530.9 225.8 544.2 188.5 544.2C102.1 544.2 32 474.2 32 387.7C32 346.2 48.5 306.4 77.8 277.1L148.9 206C178.2 176.7 218 160.2 259.5 160.2C346.1 160.2 416 230.8 416 317.1C416 318.4 416 319.7 416 321C415.6 338.7 400.9 352.6 383.2 352.2C365.5 351.8 351.6 337.1 352 319.4C352 318.6 352 317.9 352 317.1C352 283.4 334 253.8 307.2 237.5z'; + + return SizedBox( + width: widget.size, + height: widget.size, + child: AnimatedBuilder( + animation: _rotation, + builder: (context, child) { + return Stack( + children: [ + Positioned.fill( + child: Transform( + alignment: const Alignment( + (416 * 2 / originalViewportSize) - 1, + (288 * 2 / originalViewportSize) - 1, + ), + transform: Matrix4.identity()..rotateZ(_rotation.value), + child: SvgPicture.string( + '', + ), + ), + ), + Positioned.fill( + child: Transform( + alignment: const Alignment( + (224 * 2 / originalViewportSize) - 1, + (352 * 2 / originalViewportSize) - 1, + ), + transform: Matrix4.identity()..rotateZ(-_rotation.value), + child: SvgPicture.string( + '', + ), + ), + ), + ], + ); + }, + ), + ); + } + + String _toHex(Color color) { + return '#${color.toARGB32().toRadixString(16).substring(2)}'; + } +} diff --git a/lib/src/visual/views/onboarding/components/onboarding_wrapper.dart b/lib/src/visual/views/onboarding/components/onboarding_wrapper.dart new file mode 100644 index 00000000..9064ad09 --- /dev/null +++ b/lib/src/visual/views/onboarding/components/onboarding_wrapper.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:twonly/src/visual/themes/light.dart'; + +class OnboardingWrapper extends StatelessWidget { + const OnboardingWrapper({ + required this.children, + super.key, + }); + final List children; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + behavior: HitTestBehavior.opaque, + child: Scaffold( + backgroundColor: primaryColor, + body: Stack( + children: [ + Positioned( + top: -100, + right: -100, + child: Container( + width: 300, + height: 300, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withValues(alpha: 0.1), + ), + ), + ), + Positioned( + bottom: -50, + left: -50, + child: Container( + width: 200, + height: 200, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.black.withValues(alpha: 0.05), + ), + ), + ), + SafeArea( + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: children, + ), + ), + ), + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/visual/views/onboarding/recover.view.dart b/lib/src/visual/views/onboarding/recover.view.dart index 7f13ecb2..4b114767 100644 --- a/lib/src/visual/views/onboarding/recover.view.dart +++ b/lib/src/visual/views/onboarding/recover.view.dart @@ -5,7 +5,9 @@ import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/alert.dialog.dart'; import 'package:twonly/src/visual/components/snackbar.dart'; -import 'package:twonly/src/visual/decorations/input_text.decoration.dart'; +import 'package:twonly/src/visual/themes/light.dart'; +import 'package:twonly/src/visual/views/onboarding/components/link_logo_animation.dart'; +import 'package:twonly/src/visual/views/onboarding/components/onboarding_wrapper.dart'; class BackupRecoveryView extends StatefulWidget { const BackupRecoveryView({super.key}); @@ -64,66 +66,112 @@ class _BackupRecoveryViewState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('twonly Backup ${context.lang.twonlySafeRecoverTitle}'), - actions: [ - IconButton( - onPressed: () async { - await showAlertDialog( - context, - 'twonly Backup', - context.lang.backupTwonlySafeLongDesc, - ); - }, - icon: const FaIcon(FontAwesomeIcons.circleInfo), - iconSize: 18, - ), - ], - ), - body: Padding( - padding: const EdgeInsetsGeometry.symmetric( - vertical: 40, - horizontal: 40, - ), - child: ListView( + return OnboardingWrapper( + children: [ + Row( children: [ - Text( - context.lang.twonlySafeRecoverDesc, - textAlign: TextAlign.center, - ), - const SizedBox(height: 30), - TextField( - controller: usernameCtrl, - onChanged: (value) { - setState(() {}); - }, - style: const TextStyle(fontSize: 17), - decoration: getInputDecoration( - context, - context.lang.registerUsernameDecoration, + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon( + Icons.arrow_back_ios_new_rounded, ), + color: Colors.white, + iconSize: 20, ), - const SizedBox(height: 10), - Stack( - children: [ - TextField( - controller: passwordCtrl, - onChanged: (value) { - setState(() {}); - }, - style: const TextStyle(fontSize: 17), - obscureText: obscureText, - decoration: getInputDecoration( - context, - context.lang.password, + const Spacer(), + IconButton( + onPressed: () async { + await showAlertDialog( + context, + 'twonly Backup', + context.lang.backupTwonlySafeLongDesc, + ); + }, + icon: const FaIcon(FontAwesomeIcons.circleInfo), + color: Colors.white, + iconSize: 20, + ), + ], + ), + const SizedBox(height: 20), + const Center( + child: Padding( + padding: EdgeInsets.all(20), + child: LinkLogoAnimation(), + ), + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + context.lang.twonlySafeRecoverTitle, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.w800, + color: Colors.white, + letterSpacing: -0.5, + ), + ), + ), + const SizedBox(height: 48), + Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(32), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextField( + controller: usernameCtrl, + onChanged: (value) => setState(() {}), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + ), + decoration: InputDecoration( + hintText: context.lang.registerUsernameDecoration, + filled: true, + fillColor: Colors.grey[100], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + prefixIcon: const Icon( + Icons.alternate_email, ), ), - Positioned( - right: 0, - top: 0, - bottom: 0, - child: IconButton( + ), + const SizedBox(height: 16), + TextField( + controller: passwordCtrl, + onChanged: (value) => setState(() {}), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + ), + obscureText: obscureText, + decoration: InputDecoration( + hintText: context.lang.password, + filled: true, + fillColor: Colors.grey[100], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + prefixIcon: const Icon( + Icons.lock_outline_rounded, + ), + suffixIcon: IconButton( onPressed: () { setState(() { obscureText = !obscureText; @@ -137,25 +185,42 @@ class _BackupRecoveryViewState extends State { ), ), ), - ], - ), - const SizedBox(height: 10), - Center( - child: FilledButton.icon( - onPressed: (!isLoading) ? _recoverTwonlySafe : null, - icon: isLoading - ? const SizedBox( - height: 12, - width: 12, - child: CircularProgressIndicator(strokeWidth: 1), - ) - : const Icon(Icons.lock_clock_rounded), - label: Text(context.lang.twonlySafeRecoverBtn), ), - ), - ], + const SizedBox(height: 32), + FilledButton( + onPressed: (!isLoading) ? _recoverTwonlySafe : null, + style: FilledButton.styleFrom( + backgroundColor: primaryColor, + foregroundColor: Colors.white, + minimumSize: const Size.fromHeight(60), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + ), + elevation: 0, + ), + child: isLoading + ? const SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 3, + ), + ) + : Text( + context.lang.twonlySafeRecoverBtn, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), ), - ), + const Spacer(), + const SizedBox(height: 40), + ], ); } } diff --git a/lib/src/visual/views/onboarding/register.view.dart b/lib/src/visual/views/onboarding/register.view.dart index 1a02b505..7a44dcf9 100644 --- a/lib/src/visual/views/onboarding/register.view.dart +++ b/lib/src/visual/views/onboarding/register.view.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_dynamic_calls import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; @@ -16,7 +17,10 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/pow.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/visual/components/alert.dialog.dart'; +import 'package:twonly/src/visual/themes/light.dart'; import 'package:twonly/src/visual/views/groups/group.view.dart'; +import 'package:twonly/src/visual/views/onboarding/components/link_logo_animation.dart'; +import 'package:twonly/src/visual/views/onboarding/components/onboarding_wrapper.dart'; import 'package:twonly/src/visual/views/onboarding/setup.view.dart'; class RegisterView extends StatefulWidget { @@ -134,7 +138,7 @@ class _RegisterViewState extends State { userId: userId, username: username, displayName: username, - subscriptionPlan: 'Preview', + subscriptionPlan: 'Free', currentSetupPage: SetupPages.profile.name, )..appVersion = AppState.latestAppVersionId; @@ -146,174 +150,184 @@ class _RegisterViewState extends State { @override Widget build(BuildContext context) { - if (_registrationDisabled) { - return Scaffold( - body: Padding( - padding: const EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: ListView( - children: [ - const SizedBox(height: 50), - Text( - context.lang.registerTitle, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 30), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - context.lang.registerSlogan, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 12), - ), - ), - const SizedBox(height: 130), + return OnboardingWrapper( + children: [ + const SizedBox(height: 40), + Center( + child: Container( + padding: const EdgeInsets.all(20), + child: const LinkLogoAnimation(), + ), + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + context.lang.registerSlogan, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.white.withValues(alpha: 0.9), + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox(height: 48), + Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(32), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (_registrationDisabled) ...[ + const SizedBox(height: 24), Text( context.lang.registrationClosed, textAlign: TextAlign.center, style: const TextStyle( + fontSize: 16, color: Colors.red, ), ), - ], - ), - ), - ), - ); - } - - InputDecoration getInputDecoration(String hintText) { - return InputDecoration(hintText: hintText, fillColor: Colors.grey[400]); - } - - return Scaffold( - appBar: AppBar( - title: const Text(''), - ), - body: Padding( - padding: const EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: ListView( - children: [ - const SizedBox(height: 50), - Text( - context.lang.registerTitle, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 30), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - context.lang.registerSlogan, + const SizedBox(height: 48), + ] else ...[ + Text( + context.lang.registerUsernameSlogan, textAlign: TextAlign.center, - style: const TextStyle(fontSize: 12), + style: TextStyle( + fontSize: 16, + color: Colors.grey[800], + fontWeight: FontWeight.w600, + ), ), - ), - const SizedBox(height: 60), - Center( - child: Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: Text( - context.lang.registerUsernameSlogan, + const SizedBox(height: 20), + TextField( + controller: usernameController, + onChanged: (value) { + usernameController.text = value.toLowerCase(); + usernameController.selection = TextSelection.fromPosition( + TextPosition( + offset: usernameController.text.length, + ), + ); + setState(() { + _isValidUserName = usernameController.text.length >= 3; + }); + }, + inputFormatters: [ + LengthLimitingTextInputFormatter(12), + FilteringTextInputFormatter.allow( + RegExp('[a-z0-9A-Z._]'), + ), + ], + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + ), + decoration: InputDecoration( + hintText: context.lang.registerUsernameDecoration, + filled: true, + fillColor: Colors.grey[100], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + prefixIcon: const Icon( + Icons.alternate_email, + ), + ), + ), + if (_showUserNameError && + usernameController.text.length < 3) ...[ + const SizedBox(height: 8), + Text( + context.lang.registerUsernameLimits, + style: const TextStyle( + color: Colors.red, + fontSize: 13, + fontWeight: FontWeight.w500, + ), textAlign: TextAlign.center, - style: const TextStyle(fontSize: 15), ), - ), - ), - const SizedBox(height: 15), - TextField( - controller: usernameController, - onChanged: (value) { - usernameController.text = value.toLowerCase(); - usernameController.selection = TextSelection.fromPosition( - TextPosition(offset: usernameController.text.length), - ); - setState(() { - _isValidUserName = usernameController.text.length >= 3; - }); - }, - inputFormatters: [ - LengthLimitingTextInputFormatter(12), - FilteringTextInputFormatter.allow(RegExp('[a-z0-9A-Z._]')), ], - style: const TextStyle(fontSize: 17), - decoration: getInputDecoration( - context.lang.registerUsernameDecoration, - ), - ), - const SizedBox(height: 10), - Text( - context.lang.registerUsernameLimits, - style: TextStyle( - color: _showUserNameError ? Colors.red : Colors.transparent, - fontSize: 12, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - Text( - context.lang.registerProofOfWorkFailed, - style: TextStyle( - color: _showProofOfWorkError - ? Colors.red - : Colors.transparent, - fontSize: 12, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - Column( - children: [ - FilledButton.icon( - icon: _isTryingToRegister - ? const SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - color: Colors.black, - strokeWidth: 2, - ), - ) - : const Icon(Icons.group), - onPressed: createNewUser, - style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - vertical: 10, - horizontal: 30, + if (_showProofOfWorkError) ...[ + const SizedBox(height: 8), + Text( + context.lang.registerProofOfWorkFailed, + style: const TextStyle( + color: Colors.red, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ), + ], + const SizedBox(height: 24), + FilledButton( + onPressed: _isTryingToRegister ? null : createNewUser, + style: FilledButton.styleFrom( + backgroundColor: primaryColor, + foregroundColor: Colors.white, + minimumSize: const Size.fromHeight(60), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + ), + elevation: 0, + ), + child: _isTryingToRegister + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 3, + ), + ) + : Text( + context.lang.registerSubmitButton, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), - ), - backgroundColor: _isTryingToRegister - ? WidgetStateProperty.all( - Colors.grey, - ) - : null, - ), - label: Text( - context.lang.registerSubmitButton, - style: const TextStyle(fontSize: 17), - ), + ), + const SizedBox(height: 16), + ], + TextButton( + onPressed: () => context.push( + Routes.settingsBackupRecovery, + ), + style: TextButton.styleFrom( + minimumSize: const Size.fromHeight(50), + foregroundColor: Colors.grey[600], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - OutlinedButton.icon( - onPressed: () => - context.push(Routes.settingsBackupRecovery), - label: Text(context.lang.twonlySafeRecoverBtn), - ), - ], + ), + child: Text( + context.lang.twonlySafeRecoverBtn, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, ), - ], + ), ), - // ), ], ), ), - ), + const Spacer(), + const SizedBox(height: 40), + ], ); } }