feat: Implement initial authentication, multi-environment support, and core app infrastructure.

This commit is contained in:
pradeepkumar
2026-02-23 19:23:30 +05:30
parent 0f07a16132
commit 4f8ad7fe49
55 changed files with 3836 additions and 118 deletions

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:real_estate_mobile/core/constants/app_colors.dart';
class AuthTextField extends StatelessWidget {
final String hintText;
final TextEditingController controller;
final bool obscureText;
final TextInputType keyboardType;
final Widget? suffixIcon;
final String? errorText;
final TextInputAction textInputAction;
const AuthTextField({
super.key,
required this.hintText,
required this.controller,
this.obscureText = false,
this.keyboardType = TextInputType.text,
this.suffixIcon,
this.errorText,
this.textInputAction = TextInputAction.next,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: controller,
obscureText: obscureText,
keyboardType: keyboardType,
textInputAction: textInputAction,
style: const TextStyle(
fontFamily: 'Fractul',
fontSize: 14,
fontWeight: FontWeight.w300,
color: AppColors.primaryDark,
),
decoration: InputDecoration(
hintText: hintText,
suffixIcon: suffixIcon,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: errorText != null
? const BorderSide(color: AppColors.error, width: 1.5)
: BorderSide.none,
),
),
),
if (errorText != null)
Padding(
padding: const EdgeInsets.only(top: 4, left: 8),
child: Text(
errorText!,
style: const TextStyle(
fontFamily: 'Fractul',
fontSize: 12,
fontWeight: FontWeight.w300,
color: AppColors.error,
),
),
),
],
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:real_estate_mobile/core/constants/app_colors.dart';
class OrDivider extends StatelessWidget {
const OrDivider({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
const Expanded(
child: Divider(color: AppColors.divider, thickness: 1),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Or',
style: TextStyle(
fontFamily: 'Fractul',
fontSize: 14,
fontWeight: FontWeight.w300,
color: AppColors.primaryDark.withValues(alpha: 0.6),
),
),
),
const Expanded(
child: Divider(color: AppColors.divider, thickness: 1),
),
],
);
}
}

View File

@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:real_estate_mobile/core/constants/app_colors.dart';
class RoleToggle extends StatelessWidget {
final String selectedRole;
final ValueChanged<String> onChanged;
const RoleToggle({
super.key,
required this.selectedRole,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColors.inputFill,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.all(2),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildToggleButton('User', 'USER'),
_buildToggleButton('Admin', 'AGENT'),
],
),
);
}
Widget _buildToggleButton(String label, String role) {
final isSelected = selectedRole == role;
return GestureDetector(
onTap: () => onChanged(role),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 10),
decoration: BoxDecoration(
color: isSelected ? AppColors.primaryDark : Colors.transparent,
borderRadius: BorderRadius.circular(20),
),
child: Text(
label,
style: TextStyle(
fontFamily: 'Fractul',
fontSize: 14,
fontWeight: FontWeight.w300,
color: isSelected ? AppColors.inputFill : AppColors.primaryDark,
),
),
),
);
}
}

View File

@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:real_estate_mobile/core/constants/app_colors.dart';
class SocialLoginButtons extends StatelessWidget {
const SocialLoginButtons({super.key});
void _showComingSoon(BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Coming soon'),
duration: Duration(seconds: 1),
behavior: SnackBarBehavior.floating,
),
);
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Apple
_SocialButton(
onTap: () => _showComingSoon(context),
child: const Icon(
Icons.apple,
size: 28,
color: AppColors.primaryDark,
),
),
const SizedBox(width: 32),
// Google
_SocialButton(
onTap: () => _showComingSoon(context),
child: SvgPicture.asset(
'assets/icons/google.svg',
width: 24,
height: 24,
),
),
const SizedBox(width: 32),
// X (Twitter)
_SocialButton(
onTap: () => _showComingSoon(context),
child: SvgPicture.asset(
'assets/icons/x.svg',
width: 24,
height: 24,
),
),
const SizedBox(width: 32),
// Facebook
_SocialButton(
onTap: () => _showComingSoon(context),
child: SvgPicture.asset(
'assets/icons/facebook.svg',
width: 24,
height: 24,
),
),
],
);
}
}
class _SocialButton extends StatelessWidget {
final VoidCallback onTap;
final Widget child;
const _SocialButton({
required this.onTap,
required this.child,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: SizedBox(
width: 40,
height: 40,
child: Center(child: child),
),
);
}
}