From 59658339f75b724918749a9e6f6c5e0546cd7f1c Mon Sep 17 00:00:00 2001 From: ChandruBtechCSE Date: Wed, 15 Jul 2026 09:25:14 +0530 Subject: [PATCH 1/3] update the gitignore to exclude unnecessary builds --- mobile/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobile/.gitignore b/mobile/.gitignore index 205aaff..dcde787 100644 --- a/mobile/.gitignore +++ b/mobile/.gitignore @@ -46,3 +46,7 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# unnecessary builds +/linux +/macos From b9ced20547e85e15a03d6bb4eee8f29b5cb339f7 Mon Sep 17 00:00:00 2001 From: ChandruBtechCSE Date: Wed, 15 Jul 2026 17:41:08 +0530 Subject: [PATCH 2/3] animated login screen and some corrections --- mobile/lib/main.dart | 2 +- mobile/lib/pages/login.dart | 638 --------------- mobile/lib/pages/login_page.dart | 1090 +++++++++++++++++++++++++ mobile/lib/themes/theme_provider.dart | 4 +- mobile/pubspec.lock | 16 + mobile/pubspec.yaml | 1 + 6 files changed, 1110 insertions(+), 641 deletions(-) delete mode 100644 mobile/lib/pages/login.dart create mode 100644 mobile/lib/pages/login_page.dart diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 7d8c399..ab0fb74 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -7,7 +7,7 @@ import 'core/constants.dart'; import 'controllers/auth.dart'; import 'controllers/chat.dart'; import 'services/auth.dart'; -import 'pages/login.dart'; +import 'pages/login_page.dart'; import 'pages/home_page.dart'; void main() async { diff --git a/mobile/lib/pages/login.dart b/mobile/lib/pages/login.dart deleted file mode 100644 index 2679b22..0000000 --- a/mobile/lib/pages/login.dart +++ /dev/null @@ -1,638 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../core/constants.dart'; -import '../controllers/auth.dart'; - -class LoginPage extends StatefulWidget { - const LoginPage({super.key}); - - @override - State createState() => _LoginPageState(); -} - -class _LoginPageState extends State - with SingleTickerProviderStateMixin { - late TabController _tabController; - final _formKey = GlobalKey(); - - final _usernameController = TextEditingController(); - final _displayNameController = TextEditingController(); - final _passwordController = TextEditingController(); - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 2, vsync: this); - } - - @override - void dispose() { - _tabController.dispose(); - _usernameController.dispose(); - _displayNameController.dispose(); - _passwordController.dispose(); - super.dispose(); - } - - void _openServerConfigDialog() { - final hostController = TextEditingController(text: Env.host); - final portController = TextEditingController(text: Env.port); - - showDialog( - context: context, - builder: (dialogContext) => BackdropFilter( - filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), - child: AlertDialog( - backgroundColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0.8), - title: Row( - children: [ - Icon(Icons.dns, color: Theme.of(context).colorScheme.primary), - SizedBox(width: 8), - Text( - "Server Settings", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Enter your decentralized peer node coordinates:", - style: TextStyle(fontSize: 13, color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 16), - Text( - "Host", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 6), - TextField( - controller: hostController, - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 14), - decoration: InputDecoration( - hintText: Env.defaultHost, - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 10, - ), - ), - ), - const SizedBox(height: 14), - Text( - "Port", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 6), - TextField( - controller: portController, - keyboardType: TextInputType.number, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 14), - decoration: InputDecoration( - hintText: Env.defaultPort, - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 10, - ), - ), - ), - ], - ), - actions: [ - TextButton( - onPressed: () async { - final navigator = Navigator.of(dialogContext); - final scaffoldMessenger = ScaffoldMessenger.of(context); - - await Env.updateConfig(Env.defaultHost, Env.defaultPort); - - navigator.pop(); - setState(() {}); - scaffoldMessenger.showSnackBar( - const SnackBar( - content: Text( - "Reset connection to elephant.commandlinecoding.in", - ), - ), - ); - }, - child: Text( - "Reset Default", - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - ), - onPressed: () async { - final newHost = hostController.text.trim(); - final newPort = portController.text.trim(); - - final navigator = Navigator.of(dialogContext); - final scaffoldMessenger = ScaffoldMessenger.of(context); - - await Env.updateConfig(newHost, newPort); - - navigator.pop(); - setState(() {}); - scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Target base set to: ${Env.httpBaseUrl}"), - ), - ); - }, - child: const Text("Save", style: TextStyle(color: Colors.white)), - ), - ], - ), - ), - ); - } - - void _submit() { - if (_formKey.currentState?.validate() ?? false) { - final auth = context.read(); - if (_tabController.index == 0) { - auth.handleLogin( - _usernameController.text.trim(), - _passwordController.text.trim(), - ); - } else { - auth.handleRegister( - _usernameController.text.trim(), - _displayNameController.text.trim(), - _passwordController.text.trim(), - ); - } - } - } - - Widget _buildSocialButton({required String label, required IconData icon}) { - return Expanded( - child: OutlinedButton.icon( - onPressed: () {}, - icon: Icon(icon, color: Theme.of(context).colorScheme.onSurface, size: 18), - label: Text( - label, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - style: OutlinedButton.styleFrom( - side: BorderSide(color: Theme.of(context).colorScheme.outlineVariant), - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - final authState = context.watch(); - - return Scaffold( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - body: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40), - child: Container( - constraints: const BoxConstraints(maxWidth: 420), - padding: const EdgeInsets.all(32), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: Theme.of(context).colorScheme.outlineVariant, - width: 1, - ), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.03), - blurRadius: 16, - offset: const Offset(0, 8), - ), - ], - ), - child: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Icons.chat_bubble_rounded, - color: Color(0xFF0052CC), - size: 28, - ), - const SizedBox(width: 8), - Text( - 'Elephant', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary, - letterSpacing: 0.5, - ), - ), - ], - ), - const SizedBox(height: 24), - - Text( - _tabController.index == 0 - ? 'Welcome Back' - : 'Create Account', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - const SizedBox(height: 6), - Text( - _tabController.index == 0 - ? 'Access your secure workspace' - : 'Join the peer mesh platform', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 28), - - Container( - height: 46, - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - borderRadius: BorderRadius.circular(8), - ), - child: TabBar( - controller: _tabController, - onTap: (index) => setState(() {}), - indicatorSize: TabBarIndicatorSize.tab, - dividerColor: Colors.transparent, - labelColor: Theme.of(context).colorScheme.primary, - unselectedLabelColor: Theme.of( - context, - ).colorScheme.onSurfaceVariant, - indicator: BoxDecoration(color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(6), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.05), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - tabs: const [ - Tab( - child: Text( - 'Login', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ), - ), - Tab( - child: Text( - 'Register', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ), - ), - ], - ), - ), - const SizedBox(height: 24), - - if (authState.errorMessage != null) ...[ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 10, - ), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.errorContainer, - borderRadius: BorderRadius.circular(6), - border: Border.all( - color: Theme.of(context).colorScheme.error, - ), - ), - child: Text( - authState.errorMessage!, - style: TextStyle( - color: Theme.of(context).colorScheme.onErrorContainer, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 20), - ], - - const Text( - "Username", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Color(0xFF172B4D), - ), - ), - const SizedBox(height: 6), - TextFormField( - controller: _usernameController, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 14), - decoration: InputDecoration( - hintText: "username", - hintStyle: const TextStyle(color: Colors.black38), - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 12, - ), - ), - validator: (val) => (val == null || val.trim().isEmpty) - ? 'Required field' - : null, - ), - const SizedBox(height: 16), - - if (_tabController.index == 1) ...[ - const Text( - "Display Name", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Color(0xFF172B4D), - ), - ), - const SizedBox(height: 6), - TextFormField( - controller: _displayNameController, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - fontSize: 14, - ), - decoration: InputDecoration( - hintText: "John Doe", - hintStyle: const TextStyle(color: Colors.black38), - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 12, - ), - ), - validator: (val) => (val == null || val.trim().isEmpty) - ? 'Required field' - : null, - ), - const SizedBox(height: 16), - ], - - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "Password", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Color(0xFF172B4D), - ), - ), - if (_tabController.index == 0) - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: Size.zero, - ), - child: const Text( - "Forgot Password?", - style: TextStyle( - color: Color(0xFF0052CC), - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ), - ), - ], - ), - const SizedBox(height: 6), - TextFormField( - controller: _passwordController, - obscureText: true, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 14), - decoration: InputDecoration( - hintText: "••••••••", - hintStyle: const TextStyle(color: Colors.black38), - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 12, - ), - ), - validator: (val) => (val == null || val.trim().length < 6) - ? 'Must be at least 6 characters' - : null, - ), - const SizedBox(height: 24), - - ElevatedButton( - onPressed: authState.isLoading ? null : _submit, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: 0, - ), - child: authState.isLoading - ? SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.onPrimary, - strokeWidth: 2.5, - ), - ) - : Text( - _tabController.index == 0 ? 'Sign In' : 'Sign Up', - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(height: 24), - - Row( - children: [ - Expanded(child: Divider( - color: Theme.of(context).colorScheme.outlineVariant, - )), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text( - "or sign in with", - style: TextStyle( - color: Theme.of( - context, - ).colorScheme.onSurfaceVariant, - fontSize: 12, - ), - ), - ), - Expanded(child: Divider( - color: Theme.of(context).colorScheme.outlineVariant, - )), - ], - ), - const SizedBox(height: 20), - - Row( - children: [ - _buildSocialButton( - label: "Google", - icon: Icons.g_mobiledata, - ), - const SizedBox(width: 12), - _buildSocialButton(label: "GitHub", icon: Icons.code), - ], - ), - const SizedBox(height: 28), - - Center( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.tertiaryContainer, - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: Theme.of(context).colorScheme.tertiary, - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.check_circle, - color: Theme.of(context).colorScheme.onTertiaryContainer, - size: 14, - ), - const SizedBox(width: 6), - Text( - "End-to-end encrypted session", - style: TextStyle( - color: Theme.of(context).colorScheme.onTertiaryContainer, - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ), - const SizedBox(height: 20), - - Center( - child: TextButton.icon( - onPressed: _openServerConfigDialog, - icon: Icon( - Icons.dns_outlined, - size: 14, - color: Theme.of(context).colorScheme.primary, - ), - label: Text( - "Connected to: ${Env.host}:${Env.port} ⚙️", - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/mobile/lib/pages/login_page.dart b/mobile/lib/pages/login_page.dart new file mode 100644 index 0000000..c6502fb --- /dev/null +++ b/mobile/lib/pages/login_page.dart @@ -0,0 +1,1090 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:mobile/pages/settings%20pages/appearance.dart'; +import 'package:provider/provider.dart'; +import '../core/constants.dart'; +import '../controllers/auth.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + final _formKey = GlobalKey(); + + final _usernameController = TextEditingController(); + final _displayNameController = TextEditingController(); + final _passwordController = TextEditingController(); + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + _usernameController.dispose(); + _displayNameController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + void _openServerConfigDialog() { + final hostController = TextEditingController(text: Env.host); + final portController = TextEditingController(text: Env.port); + final colorScheme = Theme.of(context).colorScheme; + + showDialog( + context: context, + builder: (dialogContext) => BackdropFilter( + filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), + child: AlertDialog( + scrollable: true, + backgroundColor: Theme.of( + context, + ).colorScheme.surface.withValues(alpha: 0.8), + title: Row( + children: [ + Icon(Icons.dns, color: colorScheme.primary), + SizedBox(width: 8), + Text( + "Server Settings", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Enter your decentralized peer node coordinates:", + style: TextStyle( + fontSize: 13, + color: colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 16), + Text( + "Host", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 6), + TextField( + controller: hostController, + style: TextStyle( + color: colorScheme.onSurfaceVariant, + fontSize: 14, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: Env.defaultHost, + filled: true, + fillColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + ), + const SizedBox(height: 14), + Text( + "Port", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 6), + TextField( + controller: portController, + keyboardType: TextInputType.number, + style: TextStyle( + color: colorScheme.onSurfaceVariant, + fontSize: 14, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: Env.defaultPort, + filled: true, + fillColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () async { + final navigator = Navigator.of(dialogContext); + final scaffoldMessenger = ScaffoldMessenger.of(context); + + await Env.updateConfig(Env.defaultHost, Env.defaultPort); + + navigator.pop(); + setState(() {}); + scaffoldMessenger.showSnackBar( + const SnackBar( + content: Text( + "Reset connection to elephant.commandlinecoding.in", + ), + ), + ); + }, + child: Text( + "Reset Default", + style: TextStyle(color: colorScheme.onSurfaceVariant), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: colorScheme.primary, + ), + onPressed: () async { + final newHost = hostController.text.trim(); + final newPort = portController.text.trim(); + + final navigator = Navigator.of(dialogContext); + final scaffoldMessenger = ScaffoldMessenger.of(context); + + await Env.updateConfig(newHost, newPort); + + navigator.pop(); + setState(() {}); + scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Target base set to: ${Env.httpBaseUrl}"), + ), + ); + }, + child: const Text("Save", style: TextStyle(color: Colors.white)), + ), + ], + ), + ), + ); + } + + void _submit() { + if (_formKey.currentState?.validate() ?? false) { + final auth = context.read(); + if (_tabController.index == 0) { + auth.handleLogin( + _usernameController.text.trim(), + _passwordController.text.trim(), + ); + } else { + auth.handleRegister( + _usernameController.text.trim(), + _displayNameController.text.trim(), + _passwordController.text.trim(), + ); + } + } + } + + Widget _buildSocialButton({required String label, required IconData icon}) { + final colorScheme = Theme.of(context).colorScheme; + return Expanded( + child: OutlinedButton.icon( + onPressed: () {}, + icon: Icon(icon, color: colorScheme.onSurface, size: 18), + label: Text( + label, + style: TextStyle( + color: colorScheme.onSurface, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + style: OutlinedButton.styleFrom( + side: BorderSide(color: colorScheme.outlineVariant), + padding: const EdgeInsets.symmetric(vertical: 12), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + final authState = context.watch(); + final colorScheme = Theme.of(context).colorScheme; + final theme = Theme.of(context); + + return Scaffold( + backgroundColor: theme.scaffoldBackgroundColor, + body: Stack( + children: [ + // ========================================== + // 1. LIVELY LIQUID BACKGROUND + // ========================================== + Positioned.fill( + child: Stack( + children: [ + // Top Left Orb (Primary) + Positioned( + top: -150, + left: -150, + child: + Container( + width: 500, + height: 500, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorScheme.primary.withValues( + alpha: 0.45, + ), + ), + ) + .animate( + onPlay: (controller) => + controller.repeat(reverse: true), + ) + .move( + duration: 6.seconds, + curve: Curves.easeInOutSine, + end: const Offset(300, 250), // Massive movement + ) + .scale( + duration: 4.seconds, + curve: Curves.easeInOutSine, + end: const Offset(1.4, 1.4), + ), + ), + + // Bottom Right Orb (Tertiary) + Positioned( + bottom: -200, + right: -100, + child: + Container( + width: 600, + height: 600, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorScheme.tertiary.withValues( + alpha: 0.35, + ), + ), + ) + .animate( + onPlay: (controller) => + controller.repeat(reverse: true), + ) + .move( + duration: 8.seconds, + curve: Curves.easeInOutSine, + end: const Offset( + -400, + -300, + ), // Swirls deep into the screen + ) + .scale( + duration: 6.seconds, + curve: Curves.easeInOutSine, + end: const Offset(1.2, 0.8), + ), + ), + + // Center Roaming Orb (Secondary) + Positioned( + top: MediaQuery.of(context).size.height * 0.2, + left: MediaQuery.of(context).size.width * 0.1, + child: + Container( + width: 400, + height: 400, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorScheme.secondary.withValues( + alpha: 0.30, + ), + ), + ) + .animate( + onPlay: (controller) => + controller.repeat(reverse: true), + ) + .moveX( + duration: 7.seconds, + curve: Curves.easeInOutSine, + end: + MediaQuery.of(context).size.width * + 0.5, // Roams across the width + ) + .moveY( + duration: 9.seconds, + curve: Curves.easeInOutSine, + end: 300, + ) + .scale( + duration: 5.seconds, + curve: Curves.easeInOutSine, + end: const Offset(1.6, 1.6), + ), + ), + + // The Heavy Blur Layer + Positioned.fill( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 120, + sigmaY: 120, + ), // Increased blur for smooth blending + child: const SizedBox(), + ), + ), + ], + ), + ), + + Positioned.fill( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 40, + ), + child: ClipRRect( + borderRadius: BorderRadiusGeometry.circular(24), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 25, sigmaY: 25), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + theme.colorScheme.surface.withValues(alpha: 0.25), + theme.colorScheme.surface.withValues(alpha: 0.05), + ], + ), + borderRadius: BorderRadius.circular(24), + border: Border.all( + color: theme.colorScheme.onSurface.withValues( + alpha: 0.20, + ), + width: 1.5, + ), + boxShadow: [ + BoxShadow( + color: theme.colorScheme.shadow.withValues( + alpha: 0.15, + ), + blurRadius: 40, + spreadRadius: -5, + ), + ], + ), + constraints: const BoxConstraints(maxWidth: 420), + padding: const EdgeInsets.all(32), + + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.chat_bubble_rounded, + color: Color(0xFF0052CC), + size: 28, + ), + const SizedBox(width: 8), + Text( + 'Elephant', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Theme.of( + context, + ).colorScheme.primary, + letterSpacing: 0.5, + ), + ), + ], + ) + .animate( + onPlay: (controller) => + controller.repeat(period: 4.seconds), + ) + .shimmer( + duration: 1.5.seconds, + color: theme.colorScheme.onSurface.withValues( + alpha: 0.5, + ), + ), + const SizedBox(height: 24), + + AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + switchInCurve: Curves.easeOutCirc, + switchOutCurve: Curves.easeInCirc, + transitionBuilder: + (Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: const Offset(0.0, 0.2), + end: Offset.zero, + ).animate(animation), + child: child, + ), + ); + }, + child: Column( + key: ValueKey(_tabController.index), + children: [ + Text( + _tabController.index == 0 + ? 'Welcome Back' + : 'Create Account', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Theme.of( + context, + ).colorScheme.onSurface, + ), + ), + const SizedBox(height: 6), + Text( + _tabController.index == 0 + ? 'Access your secure workspace' + : 'Join the peer mesh platform', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + const SizedBox(height: 28), + + Container( + height: 52, + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.surface.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(26), + border: Border.all( + color: theme.colorScheme.outlineVariant + .withValues(alpha: 0.3), + ), + ), + child: Stack( + children: [ + AnimatedAlign( + duration: const Duration(milliseconds: 350), + curve: Curves.fastOutSlowIn, + alignment: _tabController.index == 0 + ? Alignment.centerLeft + : Alignment.centerRight, + child: FractionallySizedBox( + widthFactor: 0.5, + child: Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + 22, + ), + color: theme.colorScheme.primary + .withValues(alpha: 0.2), + border: Border.all( + color: theme.colorScheme.primary + .withValues(alpha: 0.5), + ), + boxShadow: [ + BoxShadow( + color: theme.colorScheme.primary + .withValues(alpha: 0.4), + blurRadius: 12, + ), + ], + ), + ), + ), + ), + Row( + children: [ + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => setState( + () => _tabController.index = 0, + ), + child: Center( + child: Text( + 'Sign In', + style: TextStyle( + fontWeight: FontWeight.bold, + color: _tabController.index == 0 + ? Theme.of( + context, + ).colorScheme.primary + : theme + .colorScheme + .onSurfaceVariant, + ), + ), + ), + ), + ), + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => setState( + () => _tabController.index = 1, + ), + child: Center( + child: Text( + 'Register', + style: TextStyle( + fontWeight: FontWeight.bold, + color: _tabController.index == 1 + ? Theme.of( + context, + ).colorScheme.primary + : theme + .colorScheme + .onSurfaceVariant, + ), + ), + ), + ), + ), + ], + ), + ], + ), + ), + const SizedBox(height: 24), + + if (authState.errorMessage != null) ...[ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.errorContainer, + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: Theme.of(context).colorScheme.error, + ), + ), + child: Text( + authState.errorMessage!, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onErrorContainer, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 20), + ], + Text( + "Username", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: colorScheme.primary, + ), + ), + const SizedBox(height: 6), + TextFormField( + controller: _usernameController, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 14, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: "username", + hintStyle: TextStyle(color: theme.hintColor), + filled: true, + fillColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + validator: (val) => + (val == null || val.trim().isEmpty) + ? 'Required field' + : null, + ), + + const SizedBox(height: 16), + + AnimatedSize( + duration: const Duration(milliseconds: 500), + curve: Curves.fastLinearToSlowEaseIn, + child: _tabController.index == 1 + ? Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + Text( + "Display Name", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: Theme.of( + context, + ).colorScheme.primary, + ), + ), + const SizedBox(height: 6), + TextFormField( + controller: + _displayNameController, + style: TextStyle( + color: theme + .colorScheme + .onSurfaceVariant, + fontSize: 14, + ), + decoration: InputDecoration( + focusedBorder: + OutlineInputBorder( + borderRadius: + BorderRadius.circular( + 8, + ), + borderSide: BorderSide( + color: + colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: + const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: "John Doe", + hintStyle: TextStyle( + color: Theme.of( + context, + ).hintColor, + ), + filled: true, + fillColor: theme + .colorScheme + .surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + validator: (val) => + (val == null || + val.trim().isEmpty) + ? 'Required field' + : null, + ), + const SizedBox(height: 16), + ], + ) + .animate() + .fade(duration: 400.ms, delay: 150.ms) + .slideX(begin: -0.05) + : const SizedBox.shrink(), + ), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Password", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: Theme.of( + context, + ).colorScheme.primary, + ), + ), + if (_tabController.index == 0) + TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: Size.zero, + ), + child: const Text( + "Forgot Password?", + style: TextStyle( + color: Color(0xFF0052CC), + fontSize: 13, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + const SizedBox(height: 6), + TextFormField( + controller: _passwordController, + obscureText: true, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 14, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: "••••••••", + hintStyle: TextStyle(color: theme.hintColor), + filled: true, + fillColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + validator: (val) => + (val == null || val.trim().length < 6) + ? 'Must be at least 6 characters' + : null, + ), + const SizedBox(height: 24), + + ElevatedButton( + onPressed: authState.isLoading + ? null + : _submit, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of( + context, + ).colorScheme.primary, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimary, + padding: const EdgeInsets.symmetric( + vertical: 14, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 0, + ), + child: authState.isLoading + ? SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + color: Theme.of( + context, + ).colorScheme.onPrimary, + strokeWidth: 2.5, + ), + ) + : AnimatedSwitcher( + duration: const Duration( + milliseconds: 300, + ), + transitionBuilder: + (child, animation) => + FadeTransition( + opacity: animation, + child: ScaleTransition( + scale: animation, + child: child, + ), + ), + child: Text( + _tabController.index == 0 + ? 'Sign In' + : 'Sign Up', + key: ValueKey( + _tabController.index, + ), + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ) + .animate( + onPlay: (controller) => + controller.repeat(reverse: true), + ) + .scale( + begin: const Offset(1, 1), + end: const Offset(1.02, 1.02), + duration: 2.seconds, + curve: Curves.easeInOutSine, + ), + const SizedBox(height: 24), + + Row( + children: [ + Expanded( + child: Divider( + color: Theme.of( + context, + ).colorScheme.outlineVariant, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + ), + child: Text( + "or sign in with", + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 12, + ), + ), + ), + Expanded( + child: Divider( + color: Theme.of( + context, + ).colorScheme.outlineVariant, + ), + ), + ], + ), + const SizedBox(height: 20), + + Row( + children: [ + _buildSocialButton( + label: "Google", + icon: Icons.g_mobiledata, + ), + const SizedBox(width: 12), + _buildSocialButton( + label: "GitHub", + icon: Icons.code, + ), + ], + ), + const SizedBox(height: 28), + + Center( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.tertiaryContainer, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Theme.of( + context, + ).colorScheme.tertiary, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.check_circle, + color: Theme.of( + context, + ).colorScheme.onTertiaryContainer, + size: 14, + ), + const SizedBox(width: 6), + Text( + "End-to-end encrypted session", + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onTertiaryContainer, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + const SizedBox(height: 20), + + Center( + child: TextButton.icon( + onPressed: _openServerConfigDialog, + icon: Icon( + Icons.dns_outlined, + size: 14, + color: Theme.of(context).colorScheme.primary, + ), + label: Text( + "Connected to: ${Env.host}:${Env.port} ⚙️", + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ].animate(interval: 100.ms).fade(duration: 400.ms).slideY(begin: 0.1, curve: Curves.easeOutQuad), + ), + ), + ), + ), + ).animate().fade(duration: 600.ms).slideY(begin: 0.1, curve: Curves.easeOutExpo), + ), + ), + ), + + Positioned( + top: MediaQuery.of(context).padding.top + 16, + right: 24, + child: + ClipRRect( + borderRadius: BorderRadius.circular(30), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), + child: Container( + decoration: BoxDecoration( + color: theme.colorScheme.surface.withValues( + alpha: 0.15, + ), + shape: BoxShape.circle, + border: Border.all( + color: theme.colorScheme.onSurface.withValues( + alpha: 0.20, + ), + width: 1.5, + ), + boxShadow: [ + BoxShadow( + color: theme.colorScheme.shadow.withValues( + alpha: 0.1, + ), + blurRadius: 20, + ), + ], + ), + child: IconButton( + padding: const EdgeInsets.all(12), + icon: Icon( + Icons.color_lens_outlined, + color: theme.colorScheme.primary, + size: 22, + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AppearanceSettings(), + ), + ); + }, + tooltip: 'Appearance Settings', + ), + ), + ), + ) + // Adds a sleek pop-in animation slightly delayed after the main card + .animate() + .fade(delay: 400.ms, duration: 500.ms) + .scale(curve: Curves.easeOutBack), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/themes/theme_provider.dart b/mobile/lib/themes/theme_provider.dart index a98380d..29b6808 100644 --- a/mobile/lib/themes/theme_provider.dart +++ b/mobile/lib/themes/theme_provider.dart @@ -3,7 +3,7 @@ import 'package:mobile/themes/app_themes.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ThemeProvider extends ChangeNotifier { - ThemeType _currentTheme = ThemeType.onyx; + ThemeType _currentTheme = ThemeType.frost; bool _isInitialized = false; ThemeType get currentTheme => _currentTheme; @@ -23,7 +23,7 @@ class ThemeProvider extends ChangeNotifier { savedThemeIndex < ThemeType.values.length) { _currentTheme = ThemeType.values[savedThemeIndex]; } else { - _currentTheme = ThemeType.onyx; + _currentTheme = ThemeType.frost; } _isInitialized = true; diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 939010b..2937f71 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -214,6 +214,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" flutter_chat_core: dependency: transitive description: @@ -318,6 +326,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.2" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" flutter_test: dependency: "direct dev" description: flutter diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 3590274..e168487 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -52,6 +52,7 @@ dependencies: simple_rich_text: ^2.0.49 qr_flutter: ^4.1.0 scrollable_positioned_list: ^0.3.8 + flutter_animate: ^4.5.2 launcher_name: default: "Elephant" From 8bcbd24024651fcc06c4e2fb0109cd577194b282 Mon Sep 17 00:00:00 2001 From: ChandruBtechCSE Date: Wed, 15 Jul 2026 17:55:11 +0530 Subject: [PATCH 3/3] optimize the login page --- mobile/lib/pages/login_page.dart | 1258 +++++++++++++++--------------- 1 file changed, 631 insertions(+), 627 deletions(-) diff --git a/mobile/lib/pages/login_page.dart b/mobile/lib/pages/login_page.dart index c6502fb..f9620bb 100644 --- a/mobile/lib/pages/login_page.dart +++ b/mobile/lib/pages/login_page.dart @@ -257,13 +257,9 @@ class _LoginPageState extends State backgroundColor: theme.scaffoldBackgroundColor, body: Stack( children: [ - // ========================================== - // 1. LIVELY LIQUID BACKGROUND - // ========================================== Positioned.fill( child: Stack( children: [ - // Top Left Orb (Primary) Positioned( top: -150, left: -150, @@ -273,8 +269,11 @@ class _LoginPageState extends State height: 500, decoration: BoxDecoration( shape: BoxShape.circle, - color: colorScheme.primary.withValues( - alpha: 0.45, + gradient: RadialGradient( + colors: [ + colorScheme.primary.withValues(alpha: 0.45), + colorScheme.primary.withValues(alpha: 0.0), + ], ), ), ) @@ -285,7 +284,7 @@ class _LoginPageState extends State .move( duration: 6.seconds, curve: Curves.easeInOutSine, - end: const Offset(300, 250), // Massive movement + end: const Offset(300, 250), ) .scale( duration: 4.seconds, @@ -294,7 +293,6 @@ class _LoginPageState extends State ), ), - // Bottom Right Orb (Tertiary) Positioned( bottom: -200, right: -100, @@ -304,8 +302,11 @@ class _LoginPageState extends State height: 600, decoration: BoxDecoration( shape: BoxShape.circle, - color: colorScheme.tertiary.withValues( - alpha: 0.35, + gradient: RadialGradient( + colors: [ + colorScheme.tertiary.withValues(alpha: 0.35), + colorScheme.tertiary.withValues(alpha: 0.0), + ], ), ), ) @@ -316,10 +317,7 @@ class _LoginPageState extends State .move( duration: 8.seconds, curve: Curves.easeInOutSine, - end: const Offset( - -400, - -300, - ), // Swirls deep into the screen + end: const Offset(-400, -300), ) .scale( duration: 6.seconds, @@ -328,7 +326,6 @@ class _LoginPageState extends State ), ), - // Center Roaming Orb (Secondary) Positioned( top: MediaQuery.of(context).size.height * 0.2, left: MediaQuery.of(context).size.width * 0.1, @@ -338,8 +335,11 @@ class _LoginPageState extends State height: 400, decoration: BoxDecoration( shape: BoxShape.circle, - color: colorScheme.secondary.withValues( - alpha: 0.30, + gradient: RadialGradient( + colors: [ + colorScheme.secondary.withValues(alpha: 0.30), + colorScheme.secondary.withValues(alpha: 0.0), + ], ), ), ) @@ -350,9 +350,7 @@ class _LoginPageState extends State .moveX( duration: 7.seconds, curve: Curves.easeInOutSine, - end: - MediaQuery.of(context).size.width * - 0.5, // Roams across the width + end: MediaQuery.of(context).size.width * 0.5, ) .moveY( duration: 9.seconds, @@ -366,665 +364,670 @@ class _LoginPageState extends State ), ), - // The Heavy Blur Layer - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 120, - sigmaY: 120, - ), // Increased blur for smooth blending - child: const SizedBox(), - ), - ), ], ), ), Positioned.fill( - child: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric( - horizontal: 24, - vertical: 40, - ), - child: ClipRRect( - borderRadius: BorderRadiusGeometry.circular(24), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 25, sigmaY: 25), - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - theme.colorScheme.surface.withValues(alpha: 0.25), - theme.colorScheme.surface.withValues(alpha: 0.05), - ], - ), - borderRadius: BorderRadius.circular(24), - border: Border.all( - color: theme.colorScheme.onSurface.withValues( - alpha: 0.20, + child: RepaintBoundary( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 40, + ), + child: ClipRRect( + borderRadius: BorderRadiusGeometry.circular(24), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 25, sigmaY: 25), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + theme.colorScheme.surface.withValues(alpha: 0.25), + theme.colorScheme.surface.withValues(alpha: 0.05), + ], ), - width: 1.5, - ), - boxShadow: [ - BoxShadow( - color: theme.colorScheme.shadow.withValues( - alpha: 0.15, + borderRadius: BorderRadius.circular(24), + border: Border.all( + color: theme.colorScheme.onSurface.withValues( + alpha: 0.20, ), - blurRadius: 40, - spreadRadius: -5, + width: 1.5, ), - ], - ), - constraints: const BoxConstraints(maxWidth: 420), - padding: const EdgeInsets.all(32), + boxShadow: [ + BoxShadow( + color: theme.colorScheme.shadow.withValues( + alpha: 0.15, + ), + blurRadius: 40, + spreadRadius: -5, + ), + ], + ), + constraints: const BoxConstraints(maxWidth: 420), + padding: const EdgeInsets.all(32), - child: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.chat_bubble_rounded, + color: Color(0xFF0052CC), + size: 28, + ), + const SizedBox(width: 8), + Text( + 'Elephant', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Theme.of( + context, + ).colorScheme.primary, + letterSpacing: 0.5, + ), + ), + ], + ) + .animate( + onPlay: (controller) => + controller.repeat(period: 4.seconds), + ) + .shimmer( + duration: 1.5.seconds, + color: theme.colorScheme.onSurface + .withValues(alpha: 0.5), + ), + const SizedBox(height: 24), + + AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + switchInCurve: Curves.easeOutCirc, + switchOutCurve: Curves.easeInCirc, + transitionBuilder: + ( + Widget child, + Animation animation, + ) { + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: const Offset(0.0, 0.2), + end: Offset.zero, + ).animate(animation), + child: child, + ), + ); + }, + child: Column( + key: ValueKey(_tabController.index), children: [ - const Icon( - Icons.chat_bubble_rounded, - color: Color(0xFF0052CC), - size: 28, - ), - const SizedBox(width: 8), Text( - 'Elephant', + _tabController.index == 0 + ? 'Welcome Back' + : 'Create Account', + textAlign: TextAlign.center, style: TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, color: Theme.of( context, - ).colorScheme.primary, - letterSpacing: 0.5, + ).colorScheme.onSurface, ), ), - ], - ) - .animate( - onPlay: (controller) => - controller.repeat(period: 4.seconds), - ) - .shimmer( - duration: 1.5.seconds, - color: theme.colorScheme.onSurface.withValues( - alpha: 0.5, - ), - ), - const SizedBox(height: 24), - - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - switchInCurve: Curves.easeOutCirc, - switchOutCurve: Curves.easeInCirc, - transitionBuilder: - (Widget child, Animation animation) { - return FadeTransition( - opacity: animation, - child: SlideTransition( - position: Tween( - begin: const Offset(0.0, 0.2), - end: Offset.zero, - ).animate(animation), - child: child, + const SizedBox(height: 6), + Text( + _tabController.index == 0 + ? 'Access your secure workspace' + : 'Join the peer mesh platform', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, ), - ); - }, - child: Column( - key: ValueKey(_tabController.index), - children: [ - Text( - _tabController.index == 0 - ? 'Welcome Back' - : 'Create Account', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Theme.of( - context, - ).colorScheme.onSurface, - ), - ), - const SizedBox(height: 6), - Text( - _tabController.index == 0 - ? 'Access your secure workspace' - : 'Join the peer mesh platform', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: Theme.of( - context, - ).colorScheme.onSurfaceVariant, ), - ), - ], + ], + ), ), - ), - const SizedBox(height: 28), + const SizedBox(height: 28), - Container( - height: 52, - decoration: BoxDecoration( - color: Theme.of( - context, - ).colorScheme.surface.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(26), - border: Border.all( - color: theme.colorScheme.outlineVariant - .withValues(alpha: 0.3), + Container( + height: 52, + decoration: BoxDecoration( + color: Theme.of( + context, + ).colorScheme.surface.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(26), + border: Border.all( + color: theme.colorScheme.outlineVariant + .withValues(alpha: 0.3), + ), ), - ), - child: Stack( - children: [ - AnimatedAlign( - duration: const Duration(milliseconds: 350), - curve: Curves.fastOutSlowIn, - alignment: _tabController.index == 0 - ? Alignment.centerLeft - : Alignment.centerRight, - child: FractionallySizedBox( - widthFactor: 0.5, - child: Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - 22, - ), - color: theme.colorScheme.primary - .withValues(alpha: 0.2), - border: Border.all( + child: Stack( + children: [ + AnimatedAlign( + duration: const Duration( + milliseconds: 350, + ), + curve: Curves.fastOutSlowIn, + alignment: _tabController.index == 0 + ? Alignment.centerLeft + : Alignment.centerRight, + child: FractionallySizedBox( + widthFactor: 0.5, + child: Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + 22, + ), color: theme.colorScheme.primary - .withValues(alpha: 0.5), - ), - boxShadow: [ - BoxShadow( + .withValues(alpha: 0.2), + border: Border.all( color: theme.colorScheme.primary - .withValues(alpha: 0.4), - blurRadius: 12, + .withValues(alpha: 0.5), ), - ], + boxShadow: [ + BoxShadow( + color: theme.colorScheme.primary + .withValues(alpha: 0.4), + blurRadius: 12, + ), + ], + ), ), ), ), - ), - Row( - children: [ - Expanded( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => setState( - () => _tabController.index = 0, - ), - child: Center( - child: Text( - 'Sign In', - style: TextStyle( - fontWeight: FontWeight.bold, - color: _tabController.index == 0 - ? Theme.of( - context, - ).colorScheme.primary - : theme - .colorScheme - .onSurfaceVariant, + Row( + children: [ + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => setState( + () => _tabController.index = 0, + ), + child: Center( + child: Text( + 'Sign In', + style: TextStyle( + fontWeight: FontWeight.bold, + color: + _tabController.index == 0 + ? Theme.of( + context, + ).colorScheme.primary + : theme + .colorScheme + .onSurfaceVariant, + ), ), ), ), ), - ), - Expanded( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => setState( - () => _tabController.index = 1, - ), - child: Center( - child: Text( - 'Register', - style: TextStyle( - fontWeight: FontWeight.bold, - color: _tabController.index == 1 - ? Theme.of( - context, - ).colorScheme.primary - : theme - .colorScheme - .onSurfaceVariant, + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => setState( + () => _tabController.index = 1, + ), + child: Center( + child: Text( + 'Register', + style: TextStyle( + fontWeight: FontWeight.bold, + color: + _tabController.index == 1 + ? Theme.of( + context, + ).colorScheme.primary + : theme + .colorScheme + .onSurfaceVariant, + ), ), ), ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), - ), - const SizedBox(height: 24), + const SizedBox(height: 24), - if (authState.errorMessage != null) ...[ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 10, - ), - decoration: BoxDecoration( - color: Theme.of( - context, - ).colorScheme.errorContainer, - borderRadius: BorderRadius.circular(6), - border: Border.all( - color: Theme.of(context).colorScheme.error, + if (authState.errorMessage != null) ...[ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, ), - ), - child: Text( - authState.errorMessage!, - style: TextStyle( + decoration: BoxDecoration( color: Theme.of( context, - ).colorScheme.onErrorContainer, - fontSize: 13, - fontWeight: FontWeight.w500, + ).colorScheme.errorContainer, + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: Theme.of( + context, + ).colorScheme.error, + ), ), - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 20), - ], - Text( - "Username", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: colorScheme.primary, - ), - ), - const SizedBox(height: 6), - TextFormField( - controller: _usernameController, - style: TextStyle( - color: Theme.of( - context, - ).colorScheme.onSurfaceVariant, - fontSize: 14, - ), - decoration: InputDecoration( - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide( - color: colorScheme.primary, - width: 1.5, + child: Text( + authState.errorMessage!, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onErrorContainer, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, ), ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 12, + const SizedBox(height: 20), + ], + Text( + "Username", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: colorScheme.primary, ), - hintText: "username", - hintStyle: TextStyle(color: theme.hintColor), - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, + ), + const SizedBox(height: 6), + TextFormField( + controller: _usernameController, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 14, ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: "username", + hintStyle: TextStyle(color: theme.hintColor), + filled: true, + fillColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + validator: (val) => + (val == null || val.trim().isEmpty) + ? 'Required field' + : null, ), - validator: (val) => - (val == null || val.trim().isEmpty) - ? 'Required field' - : null, - ), - const SizedBox(height: 16), + const SizedBox(height: 16), - AnimatedSize( - duration: const Duration(milliseconds: 500), - curve: Curves.fastLinearToSlowEaseIn, - child: _tabController.index == 1 - ? Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - Text( - "Display Name", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Theme.of( - context, - ).colorScheme.primary, - ), - ), - const SizedBox(height: 6), - TextFormField( - controller: - _displayNameController, - style: TextStyle( - color: theme - .colorScheme - .onSurfaceVariant, - fontSize: 14, - ), - decoration: InputDecoration( - focusedBorder: - OutlineInputBorder( - borderRadius: - BorderRadius.circular( - 8, - ), - borderSide: BorderSide( - color: - colorScheme.primary, - width: 1.5, - ), - ), - contentPadding: - const EdgeInsets.symmetric( - horizontal: 14, - vertical: 12, - ), - hintText: "John Doe", - hintStyle: TextStyle( + AnimatedSize( + duration: const Duration(milliseconds: 500), + curve: Curves.fastLinearToSlowEaseIn, + child: _tabController.index == 1 + ? Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + Text( + "Display Name", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, color: Theme.of( context, - ).hintColor, + ).colorScheme.primary, + ), + ), + const SizedBox(height: 6), + TextFormField( + controller: + _displayNameController, + style: TextStyle( + color: theme + .colorScheme + .onSurfaceVariant, + fontSize: 14, ), - filled: true, - fillColor: theme - .colorScheme - .surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: - BorderRadius.circular(8), - borderSide: BorderSide.none, + decoration: InputDecoration( + focusedBorder: + OutlineInputBorder( + borderRadius: + BorderRadius.circular( + 8, + ), + borderSide: BorderSide( + color: colorScheme + .primary, + width: 1.5, + ), + ), + contentPadding: + const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: "John Doe", + hintStyle: TextStyle( + color: Theme.of( + context, + ).hintColor, + ), + filled: true, + fillColor: theme + .colorScheme + .surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular( + 8, + ), + borderSide: BorderSide.none, + ), ), + validator: (val) => + (val == null || + val.trim().isEmpty) + ? 'Required field' + : null, ), - validator: (val) => - (val == null || - val.trim().isEmpty) - ? 'Required field' - : null, - ), - const SizedBox(height: 16), - ], - ) - .animate() - .fade(duration: 400.ms, delay: 150.ms) - .slideX(begin: -0.05) - : const SizedBox.shrink(), - ), + const SizedBox(height: 16), + ], + ) + .animate() + .fade(duration: 400.ms, delay: 150.ms) + .slideX(begin: -0.05) + : const SizedBox.shrink(), + ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Password", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Theme.of( - context, - ).colorScheme.primary, - ), - ), - if (_tabController.index == 0) - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: Size.zero, + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Password", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: Theme.of( + context, + ).colorScheme.primary, ), - child: const Text( - "Forgot Password?", - style: TextStyle( - color: Color(0xFF0052CC), - fontSize: 13, - fontWeight: FontWeight.w500, + ), + if (_tabController.index == 0) + TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: Size.zero, + ), + child: const Text( + "Forgot Password?", + style: TextStyle( + color: Color(0xFF0052CC), + fontSize: 13, + fontWeight: FontWeight.w500, + ), ), ), - ), - ], - ), - const SizedBox(height: 6), - TextFormField( - controller: _passwordController, - obscureText: true, - style: TextStyle( - color: Theme.of( - context, - ).colorScheme.onSurfaceVariant, - fontSize: 14, + ], ), - decoration: InputDecoration( - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide( - color: colorScheme.primary, - width: 1.5, - ), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 12, + const SizedBox(height: 6), + TextFormField( + controller: _passwordController, + obscureText: true, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 14, ), - hintText: "••••••••", - hintStyle: TextStyle(color: theme.hintColor), - filled: true, - fillColor: Theme.of( - context, - ).colorScheme.surfaceContainerHighest, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 1.5, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 12, + ), + hintText: "••••••••", + hintStyle: TextStyle(color: theme.hintColor), + filled: true, + fillColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), ), + validator: (val) => + (val == null || val.trim().length < 6) + ? 'Must be at least 6 characters' + : null, ), - validator: (val) => - (val == null || val.trim().length < 6) - ? 'Must be at least 6 characters' - : null, - ), - const SizedBox(height: 24), + const SizedBox(height: 24), - ElevatedButton( - onPressed: authState.isLoading - ? null - : _submit, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of( - context, - ).colorScheme.primary, - foregroundColor: Theme.of( - context, - ).colorScheme.onPrimary, - padding: const EdgeInsets.symmetric( - vertical: 14, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), + ElevatedButton( + onPressed: authState.isLoading + ? null + : _submit, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of( + context, + ).colorScheme.primary, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimary, + padding: const EdgeInsets.symmetric( + vertical: 14, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 0, ), - elevation: 0, - ), - child: authState.isLoading - ? SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - color: Theme.of( - context, - ).colorScheme.onPrimary, - strokeWidth: 2.5, - ), - ) - : AnimatedSwitcher( - duration: const Duration( - milliseconds: 300, - ), - transitionBuilder: - (child, animation) => - FadeTransition( - opacity: animation, - child: ScaleTransition( - scale: animation, - child: child, - ), - ), - child: Text( - _tabController.index == 0 - ? 'Sign In' - : 'Sign Up', - key: ValueKey( - _tabController.index, + child: authState.isLoading + ? SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + color: Theme.of( + context, + ).colorScheme.onPrimary, + strokeWidth: 2.5, ), - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, + ) + : AnimatedSwitcher( + duration: const Duration( + milliseconds: 300, + ), + transitionBuilder: + (child, animation) => + FadeTransition( + opacity: animation, + child: ScaleTransition( + scale: animation, + child: child, + ), + ), + child: Text( + _tabController.index == 0 + ? 'Sign In' + : 'Sign Up', + key: ValueKey( + _tabController.index, + ), + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), ), ), - ), - ) - .animate( - onPlay: (controller) => - controller.repeat(reverse: true), - ) - .scale( - begin: const Offset(1, 1), - end: const Offset(1.02, 1.02), - duration: 2.seconds, - curve: Curves.easeInOutSine, - ), - const SizedBox(height: 24), + ) + .animate( + onPlay: (controller) => + controller.repeat(reverse: true), + ) + .scale( + begin: const Offset(1, 1), + end: const Offset(1.02, 1.02), + duration: 2.seconds, + curve: Curves.easeInOutSine, + ), + const SizedBox(height: 24), - Row( - children: [ - Expanded( - child: Divider( - color: Theme.of( - context, - ).colorScheme.outlineVariant, + Row( + children: [ + Expanded( + child: Divider( + color: Theme.of( + context, + ).colorScheme.outlineVariant, + ), ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + ), + child: Text( + "or sign in with", + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 12, + ), + ), ), - child: Text( - "or sign in with", - style: TextStyle( + Expanded( + child: Divider( color: Theme.of( context, - ).colorScheme.onSurfaceVariant, - fontSize: 12, + ).colorScheme.outlineVariant, ), ), - ), - Expanded( - child: Divider( - color: Theme.of( - context, - ).colorScheme.outlineVariant, - ), - ), - ], - ), - const SizedBox(height: 20), + ], + ), + const SizedBox(height: 20), - Row( - children: [ - _buildSocialButton( - label: "Google", - icon: Icons.g_mobiledata, - ), - const SizedBox(width: 12), - _buildSocialButton( - label: "GitHub", - icon: Icons.code, - ), - ], - ), - const SizedBox(height: 28), + Row( + children: [ + _buildSocialButton( + label: "Google", + icon: Icons.g_mobiledata, + ), + const SizedBox(width: 12), + _buildSocialButton( + label: "GitHub", + icon: Icons.code, + ), + ], + ), + const SizedBox(height: 28), - Center( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: Theme.of( - context, - ).colorScheme.tertiaryContainer, - borderRadius: BorderRadius.circular(20), - border: Border.all( + Center( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( color: Theme.of( context, - ).colorScheme.tertiary, - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.check_circle, + ).colorScheme.tertiaryContainer, + borderRadius: BorderRadius.circular(20), + border: Border.all( color: Theme.of( context, - ).colorScheme.onTertiaryContainer, - size: 14, + ).colorScheme.tertiary, ), - const SizedBox(width: 6), - Text( - "End-to-end encrypted session", - style: TextStyle( + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.check_circle, color: Theme.of( context, ).colorScheme.onTertiaryContainer, - fontSize: 12, - fontWeight: FontWeight.w600, + size: 14, ), - ), - ], + const SizedBox(width: 6), + Text( + "End-to-end encrypted session", + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onTertiaryContainer, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), ), ), - ), - const SizedBox(height: 20), + const SizedBox(height: 20), - Center( - child: TextButton.icon( - onPressed: _openServerConfigDialog, - icon: Icon( - Icons.dns_outlined, - size: 14, - color: Theme.of(context).colorScheme.primary, - ), - label: Text( - "Connected to: ${Env.host}:${Env.port} ⚙️", - style: TextStyle( + Center( + child: TextButton.icon( + onPressed: _openServerConfigDialog, + icon: Icon( + Icons.dns_outlined, + size: 14, color: Theme.of( context, - ).colorScheme.onSurfaceVariant, - fontSize: 12, - fontWeight: FontWeight.w600, + ).colorScheme.primary, + ), + label: Text( + "Connected to: ${Env.host}:${Env.port} ⚙️", + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ), - ), - ].animate(interval: 100.ms).fade(duration: 400.ms).slideY(begin: 0.1, curve: Curves.easeOutQuad), + ].animate(interval: 100.ms).fade(duration: 400.ms).slideY(begin: 0.1, curve: Curves.easeOutQuad), + ), ), ), ), - ), - ).animate().fade(duration: 600.ms).slideY(begin: 0.1, curve: Curves.easeOutExpo), + ).animate().fade(duration: 600.ms).slideY(begin: 0.1, curve: Curves.easeOutExpo), + ), ), ), ), @@ -1032,56 +1035,57 @@ class _LoginPageState extends State Positioned( top: MediaQuery.of(context).padding.top + 16, right: 24, - child: - ClipRRect( - borderRadius: BorderRadius.circular(30), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), - child: Container( - decoration: BoxDecoration( - color: theme.colorScheme.surface.withValues( - alpha: 0.15, - ), - shape: BoxShape.circle, - border: Border.all( - color: theme.colorScheme.onSurface.withValues( - alpha: 0.20, + child: RepaintBoundary( + child: + ClipRRect( + borderRadius: BorderRadius.circular(30), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), + child: Container( + decoration: BoxDecoration( + color: theme.colorScheme.surface.withValues( + alpha: 0.15, ), - width: 1.5, - ), - boxShadow: [ - BoxShadow( - color: theme.colorScheme.shadow.withValues( - alpha: 0.1, + shape: BoxShape.circle, + border: Border.all( + color: theme.colorScheme.onSurface.withValues( + alpha: 0.20, ), - blurRadius: 20, + width: 1.5, ), - ], - ), - child: IconButton( - padding: const EdgeInsets.all(12), - icon: Icon( - Icons.color_lens_outlined, - color: theme.colorScheme.primary, - size: 22, - ), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AppearanceSettings(), + boxShadow: [ + BoxShadow( + color: theme.colorScheme.shadow.withValues( + alpha: 0.1, + ), + blurRadius: 20, ), - ); - }, - tooltip: 'Appearance Settings', + ], + ), + child: IconButton( + padding: const EdgeInsets.all(12), + icon: Icon( + Icons.color_lens_outlined, + color: theme.colorScheme.primary, + size: 22, + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AppearanceSettings(), + ), + ); + }, + tooltip: 'Appearance Settings', + ), ), ), - ), - ) - // Adds a sleek pop-in animation slightly delayed after the main card - .animate() - .fade(delay: 400.ms, duration: 500.ms) - .scale(curve: Curves.easeOutBack), + ) + .animate() + .fade(delay: 400.ms, duration: 500.ms) + .scale(curve: Curves.easeOutBack), + ), ), ], ),