1111
This commit is contained in:
84
lib/presentation/screens/about_screen.dart
Normal file
84
lib/presentation/screens/about_screen.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
|
||||
class AboutScreen extends StatelessWidget {
|
||||
const AboutScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
/// -------------------- SETTINGS BAR --------------------
|
||||
SettingsBar(
|
||||
selectedIndex: 0,
|
||||
onTap: (_) {},
|
||||
showBackButton: true,
|
||||
onBackTap: () => Navigator.pop(context),
|
||||
iconPaths: const [],
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
/// -------------------- CENTER CONTENT --------------------
|
||||
///
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24.0,
|
||||
vertical: 10,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"عن الشركة",
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
Container(
|
||||
width: 200,
|
||||
height: 1,
|
||||
color: const Color(0x8732C599),
|
||||
),
|
||||
|
||||
const SizedBox(height: 14),
|
||||
|
||||
const Text(
|
||||
"شركة كودا ذ.م.م هي شركة عراقية رائدة تقدم حلولاً متميزة في البرمجيات المخصصة والتطبيقات وإدارة الخوادم والمواقع، ومنظومات الاتصال وخدمات الأمن السيبراني. تأسست عام 2008 ولديها عقود واتفاقيات عديدة مع مؤسسات حكومية عراقية والقطاع الخاص.\n\nوسعت الشركة شراكاتها مع وكالات عالمية مثل CODACS الأمريكية و Laipac الكندية و Teltonika اللتوانية وWolf Team الصينية وLibelium الإسبانية وOdoo العالمية وغيرها. مما أتاح لها تقديم أحدث التقنيات والمنتجات المبتكرة لعملائها.\n\nتوفر كودا حلولاً برمجية مخصصة للمؤسسات الكبيرة والدوائر الحكومية، مما يسهم في تبسيط الإجراءات وتقديم الخدمات للمواطنين بشكل إلكتروني وفعال.\n\nكما تقدم كودا مجموعة واسعة من المنتجات والخدمات الحديثة، بما في ذلك الشاشات الإعلانية، والبوابات الذكية، والمواقف الذكية، والأقفال، والمباني الذكية، وأجهزة الكشك (الخدمة الإلكترونية الذاتية)، وأجهزة محمولة تخصصية، وحساسات، وأجهزة نداء واستغاثة.\n\nبفضل هذه المنتجات والخدمات المبتكرة، تعزز كودا مكانتها الرائدة في السوق المحلي والإقليمي، وتساهم في تطوير التكنولوجيا وتعزيز جودة الحياة في العراق .",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: Colors.white70,
|
||||
height:
|
||||
1.5, // Added line height for better readability
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20), // Bottom padding
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
269
lib/presentation/screens/attendence_screen.dart
Normal file
269
lib/presentation/screens/attendence_screen.dart
Normal file
@@ -0,0 +1,269 @@
|
||||
import 'package:coda_project/presentation/screens/face_screen.dart';
|
||||
import 'package:coda_project/presentation/screens/notifications_screen.dart';
|
||||
import 'package:coda_project/presentation/screens/user_settings_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
|
||||
class AttendanceScreen extends StatelessWidget {
|
||||
const AttendanceScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.sizeOf(context).width;
|
||||
final screenHeight = MediaQuery.sizeOf(context).height;
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Stack(
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height),
|
||||
|
||||
/// ------------------------------
|
||||
/// SETTINGS BAR (STATIC)
|
||||
/// ------------------------------
|
||||
SafeArea(
|
||||
child: SettingsBar(
|
||||
selectedIndex: 0,
|
||||
showBackButton: false,
|
||||
iconPaths: ['assets/images/user.svg', 'assets/images/ball.svg'],
|
||||
onTap: (index) {
|
||||
if (index == 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserSettingsScreen(),
|
||||
),
|
||||
);
|
||||
} else if (index == 1) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NotificationsScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
/// ------------------------------
|
||||
/// GREETING TEXT
|
||||
/// ------------------------------
|
||||
Positioned(
|
||||
top:
|
||||
screenHeight *
|
||||
0.14, // moved down because settings bar now exists
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"صباح الخير, محمد",
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
shadows: [Shadow(color: Color(0x42000000), blurRadius: 6)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// ------------------------------
|
||||
/// MAIN CARD AREA
|
||||
/// ------------------------------
|
||||
Positioned(
|
||||
top:
|
||||
screenHeight *
|
||||
0.2, // pushed down because of settings bar + greeting
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: screenHeight * 0.05),
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: screenHeight * 0.5,
|
||||
width: screenWidth * 0.7,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color(0x1F2B2B2B),
|
||||
blurRadius: 5,
|
||||
offset: Offset(10, -10),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0xABCECECE),
|
||||
blurRadius: 5,
|
||||
offset: Offset(-2, 5),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(148, 2, 70, 35),
|
||||
blurRadius: 80,
|
||||
offset: Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: screenHeight * 0.5,
|
||||
width: screenWidth * 0.7,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0x92757575),
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// ------------------------------
|
||||
/// LOGIN BUTTON
|
||||
/// ------------------------------
|
||||
Positioned(
|
||||
top: screenHeight * 0.21,
|
||||
left: screenWidth * 0.05,
|
||||
child: _ShadowedCard(
|
||||
shadow: [
|
||||
BoxShadow(
|
||||
color: Color(0x62000000),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 5,
|
||||
offset: Offset(5, 5),
|
||||
),
|
||||
],
|
||||
child: _FingerButton(
|
||||
icon: "assets/images/faceLogin.svg",
|
||||
label: "تسجيل الدخول",
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => OvalCameraCapturePage(isLogin: true),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// ------------------------------
|
||||
/// LOGOUT BUTTON
|
||||
/// ------------------------------
|
||||
Positioned(
|
||||
bottom: screenHeight * 0.2,
|
||||
right: screenWidth * 0.1,
|
||||
child: _ShadowedCard(
|
||||
shadow: [
|
||||
BoxShadow(
|
||||
color: Color(0xABCECECE),
|
||||
blurRadius: 5,
|
||||
spreadRadius: 3,
|
||||
offset: Offset(-6, -6),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x92014221),
|
||||
blurRadius: 10,
|
||||
offset: Offset(-5, -5),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x7D1A1A1A),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 3,
|
||||
offset: Offset(5, 5),
|
||||
),
|
||||
],
|
||||
child: _FingerButton(
|
||||
icon: "assets/images/faceLogout.svg",
|
||||
label: "تسجيل خروج",
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => OvalCameraCapturePage(isLogin: false),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------------------------------------------
|
||||
/// SHADOW WRAPPER
|
||||
/// ---------------------------------------------
|
||||
|
||||
class _ShadowedCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
final List<BoxShadow> shadow;
|
||||
|
||||
const _ShadowedCard({required this.child, required this.shadow});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 160,
|
||||
width: 160,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
boxShadow: shadow,
|
||||
),
|
||||
),
|
||||
child,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------------------------------------------
|
||||
/// BUTTON WIDGET
|
||||
/// ---------------------------------------------
|
||||
|
||||
class _FingerButton extends StatelessWidget {
|
||||
final String icon;
|
||||
final String label;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _FingerButton({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 160,
|
||||
width: 160,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEAFBF3),
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(icon, width: 75, height: 75),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
34
lib/presentation/screens/auth_screen.dart
Normal file
34
lib/presentation/screens/auth_screen.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/auth_form.dart';
|
||||
import '../../core/di/injection_container.dart';
|
||||
import '../blocs/login/login_bloc.dart';
|
||||
|
||||
class AuthScreen extends StatelessWidget {
|
||||
const AuthScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => sl<LoginBloc>(),
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 60),
|
||||
// Logo
|
||||
Center(child: Image.asset("assets/images/logo2.png", width: 200)),
|
||||
// const SizedBox(height: 15),
|
||||
// Form - taking remaining space and centered
|
||||
Expanded(child: Center(child: const AuthForm())),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
351
lib/presentation/screens/face_screen.dart
Normal file
351
lib/presentation/screens/face_screen.dart
Normal file
@@ -0,0 +1,351 @@
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'dart:async';
|
||||
|
||||
class OvalCameraCapturePage extends StatefulWidget {
|
||||
final bool isLogin;
|
||||
const OvalCameraCapturePage({super.key, this.isLogin = true});
|
||||
|
||||
@override
|
||||
State<OvalCameraCapturePage> createState() => _OvalCameraCapturePageState();
|
||||
}
|
||||
|
||||
class _OvalCameraCapturePageState extends State<OvalCameraCapturePage> {
|
||||
CameraController? _cameraController;
|
||||
bool _isCameraInitialized = false;
|
||||
String? _errorMessage;
|
||||
bool _isSuccess = false;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeCamera();
|
||||
}
|
||||
|
||||
Future<void> _initializeCamera() async {
|
||||
try {
|
||||
// Dispose existing controller if any
|
||||
await _cameraController?.dispose();
|
||||
_cameraController = null;
|
||||
|
||||
// Get available cameras
|
||||
final cameras = await availableCameras();
|
||||
|
||||
// Check if cameras list is available
|
||||
if (cameras.isEmpty) {
|
||||
setState(() {
|
||||
_errorMessage = "لا توجد كاميرات متاحة";
|
||||
_isCameraInitialized = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to find front camera, fallback to first available camera
|
||||
CameraDescription? selectedCamera;
|
||||
try {
|
||||
selectedCamera = cameras.firstWhere(
|
||||
(cam) => cam.lensDirection == CameraLensDirection.front,
|
||||
);
|
||||
} catch (e) {
|
||||
// If no front camera found, use the first available camera
|
||||
if (cameras.isNotEmpty) {
|
||||
selectedCamera = cameras.first;
|
||||
} else {
|
||||
setState(() {
|
||||
_errorMessage = "لا توجد كاميرات متاحة";
|
||||
_isCameraInitialized = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_cameraController = CameraController(
|
||||
selectedCamera,
|
||||
ResolutionPreset.medium,
|
||||
enableAudio: false,
|
||||
imageFormatGroup: ImageFormatGroup.jpeg,
|
||||
);
|
||||
|
||||
await _cameraController!.initialize();
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isCameraInitialized = true;
|
||||
_errorMessage = null;
|
||||
});
|
||||
|
||||
_timer = Timer(const Duration(seconds: 3), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isSuccess = true;
|
||||
});
|
||||
|
||||
// Auto-close after 2 seconds
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_errorMessage = "خطأ في تهيئة الكاميرا: $e";
|
||||
_isCameraInitialized = false;
|
||||
});
|
||||
print("Error initializing camera: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cameraController?.dispose();
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Color(0xff000000),
|
||||
|
||||
body:
|
||||
_errorMessage != null
|
||||
? Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.camera_alt_outlined,
|
||||
size: 64,
|
||||
color: Colors.white70,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
_errorMessage!,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: _initializeCamera,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xffE8001A),
|
||||
foregroundColor: Colors.white,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
child: Text("إعادة المحاولة"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: _isCameraInitialized && _cameraController != null
|
||||
? Stack(
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height),
|
||||
|
||||
// Camera Preview
|
||||
Positioned(
|
||||
child: Center(child: CameraPreview(_cameraController!)),
|
||||
),
|
||||
// Oval overlay with dimmed background
|
||||
Positioned.fill(
|
||||
child: CustomPaint(painter: _OvalOverlayPainter()),
|
||||
),
|
||||
|
||||
// Top Text
|
||||
Positioned(
|
||||
top: 100,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
widget.isLogin ? "تسجيل الدخول" : "تسجيل خروج",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily:
|
||||
'Cairo', // Assuming Cairo font based on Arabic text
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Bottom Text and Logo
|
||||
Positioned(
|
||||
bottom: 80,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
_isSuccess
|
||||
? (widget.isLogin
|
||||
? "تم تسجيل دخولك بنجاح"
|
||||
: "تم تسجيل خروجك بنجاح")
|
||||
: (widget.isLogin
|
||||
? "يتم تسجيل الدخول ..."
|
||||
: "يتم تسجيل الخروج ..."),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Logo
|
||||
SizedBox(
|
||||
height: _isSuccess ? 80 : 60,
|
||||
width: _isSuccess ? 80 : 60,
|
||||
child: SvgPicture.asset(
|
||||
_isSuccess
|
||||
? 'assets/images/logSuccess.svg'
|
||||
: 'assets/images/logLoading.svg',
|
||||
// ignore: deprecated_member_use
|
||||
color: Colors.white,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// // Capture button
|
||||
// Positioned(
|
||||
// bottom: 60,
|
||||
// left: 0,
|
||||
// right: 0,
|
||||
// child: Center(
|
||||
// child: GestureDetector(
|
||||
// onTap: (){},
|
||||
// child: Container(
|
||||
// width: 72,
|
||||
// height: 72,
|
||||
// decoration: BoxDecoration(
|
||||
// shape: BoxShape.circle,
|
||||
// color: Colors.white,
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: Colors.black26,
|
||||
// blurRadius: 8,
|
||||
// offset: Offset(0, 4),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// child: Icon(Icons.camera_alt, color: Color(0xffE8001A), size: 36),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
)
|
||||
: Center(
|
||||
child: CircularProgressIndicator(color: Color(0xffE8001A)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OvalOverlayPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final width = size.width * 0.75;
|
||||
final height = size.height * 0.4;
|
||||
final center = Offset(size.width / 2, size.height / 2);
|
||||
final ovalRect = Rect.fromCenter(
|
||||
center: center,
|
||||
width: width,
|
||||
height: height,
|
||||
);
|
||||
|
||||
// Create a path for the whole screen
|
||||
final screenPath =
|
||||
Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
|
||||
// Create a path for the oval
|
||||
final ovalPath = Path()..addOval(ovalRect);
|
||||
// Subtract the oval from the screen path
|
||||
final overlayPath = Path.combine(
|
||||
PathOperation.difference,
|
||||
screenPath,
|
||||
ovalPath,
|
||||
);
|
||||
|
||||
// Draw the dimmed area outside the oval with gradient
|
||||
final gradient = LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color.fromARGB(255, 41, 41, 41), // top dark gray
|
||||
Color.fromARGB(255, 0, 20, 15), // bottom deep green
|
||||
],
|
||||
);
|
||||
final paint =
|
||||
Paint()
|
||||
..shader = gradient.createShader(
|
||||
Rect.fromLTWH(0, 0, size.width, size.height),
|
||||
)
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawPath(overlayPath, paint);
|
||||
|
||||
// Draw glowing circles effect (like AppBackground) - drawn after overlay
|
||||
// Top circle - positioned similar to AppBackground (top: -250, left: 100, right: -200)
|
||||
final topCircleCenter = Offset(size.width * 0.3, -250);
|
||||
final topCircleRadius = 150.0;
|
||||
// Draw multiple circles with different opacities for spread effect (spreadRadius: 160)
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final spreadPaint =
|
||||
Paint()
|
||||
..color = Color.fromARGB(69 ~/ (i + 1), 62, 254, 190)
|
||||
..maskFilter = MaskFilter.blur(BlurStyle.normal, 140 - (i * 20));
|
||||
canvas.drawCircle(
|
||||
topCircleCenter,
|
||||
topCircleRadius + (i * 30),
|
||||
spreadPaint,
|
||||
);
|
||||
}
|
||||
|
||||
// Bottom circle - positioned similar to AppBackground (bottom: 100, left: -140, right: -120)
|
||||
final bottomCircleCenter = Offset(size.width * 0.2, size.height + 100);
|
||||
final bottomCircleRadius = 160.0;
|
||||
// Draw multiple circles with different opacities for spread effect (spreadRadius: 60)
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final spreadPaint =
|
||||
Paint()
|
||||
..color = Color.fromARGB(83 ~/ (i + 1), 62, 254, 190)
|
||||
..maskFilter = MaskFilter.blur(BlurStyle.normal, 180 - (i * 25));
|
||||
canvas.drawCircle(
|
||||
bottomCircleCenter,
|
||||
bottomCircleRadius + (i * 40),
|
||||
spreadPaint,
|
||||
);
|
||||
}
|
||||
|
||||
// Draw oval border
|
||||
final borderPaint =
|
||||
Paint()
|
||||
..color = Colors.greenAccent
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 4;
|
||||
canvas.drawOval(ovalRect, borderPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
105
lib/presentation/screens/finance_screen.dart
Normal file
105
lib/presentation/screens/finance_screen.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:coda_project/presentation/screens/notifications_screen.dart';
|
||||
import 'package:coda_project/presentation/screens/user_settings_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../widgets/finance_summary_card.dart';
|
||||
import '../widgets/work_day_card.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
|
||||
class FinanceScreen extends StatefulWidget {
|
||||
final void Function(bool isScrollingDown)? onScrollEvent;
|
||||
|
||||
const FinanceScreen({super.key, this.onScrollEvent});
|
||||
|
||||
@override
|
||||
State<FinanceScreen> createState() => _FinanceScreenState();
|
||||
}
|
||||
|
||||
class _FinanceScreenState extends State<FinanceScreen> {
|
||||
String dropdownValue = "الكل";
|
||||
late ScrollController scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
scrollController = ScrollController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: SafeArea(
|
||||
child: CustomScrollView(
|
||||
|
||||
controller: scrollController,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsBar(
|
||||
selectedIndex: 0,
|
||||
showBackButton: false,
|
||||
iconPaths: [
|
||||
'assets/images/user.svg',
|
||||
'assets/images/ball.svg',
|
||||
],
|
||||
onTap: (index) {
|
||||
if (index == 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserSettingsScreen(),
|
||||
),
|
||||
);
|
||||
} else if (index == 1) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NotificationsScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
||||
|
||||
/// SUMMARY CARD
|
||||
SliverToBoxAdapter(
|
||||
child: FinanceSummaryCard(
|
||||
totalAmount: "333,000",
|
||||
dropdownValue: dropdownValue,
|
||||
onCalendarTap: () => showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
),
|
||||
onDropdownChanged: (value) {
|
||||
setState(() => dropdownValue = value!);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
/// WORK DAY CARDS
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return const WorkDayCard();
|
||||
},
|
||||
childCount: 3,
|
||||
),
|
||||
),
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
536
lib/presentation/screens/holiday_screen.dart
Normal file
536
lib/presentation/screens/holiday_screen.dart
Normal file
@@ -0,0 +1,536 @@
|
||||
import 'package:coda_project/presentation/screens/notifications_screen.dart';
|
||||
import 'package:coda_project/presentation/screens/user_settings_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../widgets/settings_bar.dart';
|
||||
import 'request_leave_screen.dart';
|
||||
import 'request_advance_scrren.dart';
|
||||
import '../../models/leave_request.dart';
|
||||
import '../../models/advance_request.dart';
|
||||
import '../../core/services/request_service.dart';
|
||||
|
||||
class HolidayScreen extends StatefulWidget {
|
||||
final void Function(bool isScrollingDown)? onScrollEvent;
|
||||
|
||||
const HolidayScreen({super.key, this.onScrollEvent});
|
||||
|
||||
@override
|
||||
State<HolidayScreen> createState() => _HolidayScreenState();
|
||||
}
|
||||
|
||||
class _HolidayScreenState extends State<HolidayScreen> {
|
||||
int activeTab = 0;
|
||||
|
||||
final RequestService _requestService = RequestService();
|
||||
List<LeaveRequest> _leaveRequests = [];
|
||||
List<AdvanceRequest> _advanceRequests = [];
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
bool _showActions = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeData();
|
||||
|
||||
_scrollController.addListener(() {
|
||||
final direction = _scrollController.position.userScrollDirection;
|
||||
|
||||
if (direction == ScrollDirection.reverse) {
|
||||
if (_showActions) setState(() => _showActions = false);
|
||||
widget.onScrollEvent?.call(true);
|
||||
} else if (direction == ScrollDirection.forward) {
|
||||
if (!_showActions) setState(() => _showActions = true);
|
||||
widget.onScrollEvent?.call(false);
|
||||
}
|
||||
|
||||
if (_scrollController.position.pixels <= 5) {
|
||||
setState(() {
|
||||
_showActions = true;
|
||||
});
|
||||
widget.onScrollEvent?.call(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _initializeData() async {
|
||||
_leaveRequests = await _requestService.getLeaveRequests();
|
||||
_advanceRequests = await _requestService.getAdvanceRequests();
|
||||
|
||||
_requestService.leaveRequestsStream.listen((requests) {
|
||||
if (mounted) setState(() => _leaveRequests = requests);
|
||||
});
|
||||
|
||||
_requestService.advanceRequestsStream.listen((requests) {
|
||||
if (mounted) setState(() => _advanceRequests = requests);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
// ---------------------------------------------------------
|
||||
// ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW
|
||||
// ---------------------------------------------------------
|
||||
SafeArea(
|
||||
child: CustomScrollView(
|
||||
controller: _scrollController,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
// SETTINGS BAR
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
// First, make sure you have your screens defined
|
||||
|
||||
// Then in your main widget:
|
||||
child: SettingsBar(
|
||||
selectedIndex: 0,
|
||||
showBackButton: false,
|
||||
iconPaths: [
|
||||
'assets/images/user.svg',
|
||||
'assets/images/ball.svg',
|
||||
],
|
||||
onTap: (index) {
|
||||
if (index == 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserSettingsScreen(),
|
||||
),
|
||||
);
|
||||
} else if (index == 1) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NotificationsScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
||||
|
||||
// TABS SECTION
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 55,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// الأجازات
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => activeTab = 1),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"الأجازات",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
color:
|
||||
activeTab == 1
|
||||
? const Color(0xFF8EFDC2)
|
||||
: const Color(0x9EFFFFFF),
|
||||
),
|
||||
),
|
||||
if (activeTab == 1)
|
||||
Container(
|
||||
width: 60,
|
||||
height: 2,
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
color: const Color(0xFF8EFDC2),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 70),
|
||||
|
||||
// السلف
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => activeTab = 0),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"السلف",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
color:
|
||||
activeTab == 0
|
||||
? const Color(0xFF8EFDC2)
|
||||
: const Color(0x9EFFFFFF),
|
||||
),
|
||||
),
|
||||
if (activeTab == 0)
|
||||
Container(
|
||||
width: 60,
|
||||
height: 2,
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
color: const Color(0xFF8EFDC2),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 20)),
|
||||
|
||||
// CONTENT LISTS
|
||||
activeTab == 1
|
||||
? _buildLeaveRequestsSliver()
|
||||
: _buildAdvanceRequestsSliver(),
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// ⭐ FLOATING ACTION BUTTONS
|
||||
// ---------------------------------------------------------
|
||||
Positioned(
|
||||
bottom: 150,
|
||||
right: 20,
|
||||
child: AnimatedSlide(
|
||||
offset: _showActions ? Offset.zero : const Offset(0, 1.3),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: AnimatedOpacity(
|
||||
opacity: _showActions ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: Column(
|
||||
children: [
|
||||
_HolidayActionButton(
|
||||
label: "طلب سلفة",
|
||||
svgPath: "assets/images/money2.svg",
|
||||
iconWidth: 28,
|
||||
iconHeight: 20,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const RequestAdvanceScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
_HolidayActionButton(
|
||||
label: "طلب إجازة",
|
||||
svgPath: "assets/images/plus.svg",
|
||||
iconWidth: 28,
|
||||
iconHeight: 20,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const RequestLeaveScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// SLIVERS FOR LISTS
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
Widget _buildLeaveRequestsSliver() {
|
||||
if (_leaveRequests.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"لا توجد طلبات أجازة",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: _buildLeaveRequestCard(_leaveRequests[index]),
|
||||
);
|
||||
}, childCount: _leaveRequests.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAdvanceRequestsSliver() {
|
||||
if (_advanceRequests.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"لا توجد طلبات سلف",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: _buildAdvanceRequestCard(_advanceRequests[index]),
|
||||
);
|
||||
}, childCount: _advanceRequests.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// CARD BUILDERS (unchanged)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
Widget _buildLeaveRequestCard(LeaveRequest request) {
|
||||
const bgColor = Color(0xFFEAF8F3);
|
||||
const lineColor = Color(0xFF22A685);
|
||||
|
||||
final bool isWaiting = request.status == "waiting";
|
||||
final bool isApproved = request.status == "approved";
|
||||
final bool isDenied = request.status == "denied";
|
||||
|
||||
final String dateText =
|
||||
"${request.fromDate.year}.${request.fromDate.month}.${request.fromDate.day}";
|
||||
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
// STATUS ROW
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_statusCircle(
|
||||
isWaiting,
|
||||
"قيد الانتظار",
|
||||
isWaiting ? "assets/images/waiting.svg" : null,
|
||||
const Color(0xFFA58A1B),
|
||||
const Color(0xFFFFDC69),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_statusCircle(
|
||||
isApproved,
|
||||
"موافقة",
|
||||
isApproved ? "assets/images/yes.svg" : null,
|
||||
const Color(0xFF0A8A60),
|
||||
const Color(0xFF00D7A3),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_statusCircle(
|
||||
isDenied,
|
||||
"تم الرفض",
|
||||
isDenied ? "assets/images/no.svg" : null,
|
||||
const Color(0xFFE63946),
|
||||
const Color(0xFFE63946),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// TITLE + DATE
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/images/holiday.svg",
|
||||
width: 32,
|
||||
height: 32,
|
||||
color: const Color(0xFF11663C),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
request.leaveType,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(dateText),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
Container(height: 1.4, width: double.infinity, color: lineColor),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// REASON ROW
|
||||
Row(
|
||||
textDirection: TextDirection.ltr, // Add this
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(request.reason, textAlign: TextAlign.right),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
const Text(
|
||||
"السبب:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _statusCircle(
|
||||
bool active,
|
||||
String label,
|
||||
String? svg,
|
||||
Color color,
|
||||
Color glow,
|
||||
) {
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: active ? color : const Color(0xFFD6D6D6),
|
||||
boxShadow:
|
||||
active
|
||||
? [BoxShadow(color: glow.withOpacity(0.6), blurRadius: 12)]
|
||||
: [],
|
||||
),
|
||||
child:
|
||||
svg != null
|
||||
? Center(child: SvgPicture.asset(svg, width: 18, height: 18))
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(label, style: const TextStyle(fontSize: 10)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAdvanceRequestCard(AdvanceRequest request) {
|
||||
// Same card logic as before (unchanged)
|
||||
return _buildLeaveRequestCard(
|
||||
LeaveRequest(
|
||||
id: request.id,
|
||||
leaveType: "سلفة ${request.amount}",
|
||||
isTimedLeave: false,
|
||||
fromDate: DateTime.now(),
|
||||
toDate: DateTime.now(),
|
||||
fromTime: TimeOfDay.now(),
|
||||
toTime: TimeOfDay.now(),
|
||||
reason: request.reason,
|
||||
requestDate: DateTime.now(),
|
||||
status: request.status,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// FLOATING BUTTON WIDGET (unchanged)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
class _HolidayActionButton extends StatelessWidget {
|
||||
final String label;
|
||||
final String svgPath;
|
||||
final VoidCallback onTap;
|
||||
final double iconWidth;
|
||||
final double iconHeight;
|
||||
|
||||
const _HolidayActionButton({
|
||||
required this.label,
|
||||
required this.svgPath,
|
||||
required this.onTap,
|
||||
required this.iconWidth,
|
||||
required this.iconHeight,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 80,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x4B00C68B),
|
||||
blurRadius: 20,
|
||||
offset: Offset(0, 6),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
child: SvgPicture.asset(svgPath, fit: BoxFit.contain),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
75
lib/presentation/screens/main_screen.dart
Normal file
75
lib/presentation/screens/main_screen.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../../widgets/floatingnavbar.dart';
|
||||
import 'attendence_screen.dart';
|
||||
import 'finance_screen.dart';
|
||||
import 'holiday_screen.dart';
|
||||
|
||||
class MainPage extends StatefulWidget {
|
||||
const MainPage({super.key});
|
||||
|
||||
@override
|
||||
State<MainPage> createState() => _MainPageState();
|
||||
}
|
||||
|
||||
class _MainPageState extends State<MainPage> {
|
||||
int _currentIndex = 0;
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenHeight = MediaQuery.sizeOf(context).height;
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
|
||||
/// BACKGROUND
|
||||
const AppBackground(child: SizedBox()),
|
||||
|
||||
/// ACTIVE SCREEN (fills entire screen - content will extend behind navbar)
|
||||
///
|
||||
Positioned.fill(
|
||||
child: IndexedStack(
|
||||
index: _currentIndex,
|
||||
children: [
|
||||
const AttendanceScreen(),
|
||||
const FinanceScreen(),
|
||||
const HolidayScreen(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
/// FLOATING NAVBAR (positioned above content)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Floatingnavbar(
|
||||
items: [
|
||||
NavBarItem(
|
||||
iconPath: 'assets/images/attendance.svg',
|
||||
label: 'الحضور',
|
||||
),
|
||||
NavBarItem(
|
||||
iconPath: 'assets/images/finance.svg',
|
||||
label: 'المالية',
|
||||
),
|
||||
NavBarItem(
|
||||
iconPath: 'assets/images/holiday.svg',
|
||||
label: 'الإجازة',
|
||||
),
|
||||
],
|
||||
selectedIndex: _currentIndex,
|
||||
onTap: (index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
139
lib/presentation/screens/notifications_screen.dart
Normal file
139
lib/presentation/screens/notifications_screen.dart
Normal file
@@ -0,0 +1,139 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
|
||||
class NotificationsScreen extends StatelessWidget {
|
||||
const NotificationsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: AppBackground(
|
||||
child: Column(
|
||||
children: [
|
||||
/// -------------------- SETTINGS BAR --------------------
|
||||
SettingsBar(
|
||||
selectedIndex: 0,
|
||||
onTap: (_) {},
|
||||
showBackButton: true,
|
||||
onBackTap: () => Navigator.pop(context),
|
||||
iconPaths: const [],
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
/// -------------------- CONTENT --------------------
|
||||
Expanded(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Column(
|
||||
children: [
|
||||
/// Title
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 40),
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: const Text(
|
||||
"الأشعارات",
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
/// -------------------- NOTIFICATION CARD --------------------
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 22),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(18),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEFFFFA),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 12,
|
||||
offset: Offset(0, 6),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
/// ✅ DATE — TOP LEFT
|
||||
const Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
child: Text(
|
||||
"2025.12.1",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0x9E000000),
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// ✅ MAIN CONTENT — RTL
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"عنوان الاشعار",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
|
||||
// const SizedBox(height: 6),
|
||||
|
||||
/// ✅ DETAILS WITH GREEN LINE
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Green line
|
||||
Container(
|
||||
width: 20,
|
||||
height: 2,
|
||||
color: Color(0xFF025A42),
|
||||
),
|
||||
|
||||
SizedBox(width: 8),
|
||||
|
||||
Text(
|
||||
"تفاصيل الاشعار",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
154
lib/presentation/screens/onboarding_screen.dart
Normal file
154
lib/presentation/screens/onboarding_screen.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'dart:async';
|
||||
import 'package:coda_project/presentation/screens/auth_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../widgets/onboarding_page.dart';
|
||||
import '../widgets/onboarding_button.dart';
|
||||
|
||||
class OnboardingScreen extends StatefulWidget {
|
||||
const OnboardingScreen({super.key});
|
||||
|
||||
@override
|
||||
State<OnboardingScreen> createState() => _OnboardingScreenState();
|
||||
}
|
||||
|
||||
class _OnboardingScreenState extends State<OnboardingScreen> {
|
||||
final PageController controller = PageController();
|
||||
int index = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// auto slide
|
||||
Timer.periodic(const Duration(seconds: 4), (timer) {
|
||||
if (!mounted) return;
|
||||
int next = index == 1 ? 0 : index + 1;
|
||||
|
||||
controller.animateToPage(
|
||||
next,
|
||||
duration: const Duration(milliseconds: 600),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
/// BACKGROUND GRADIENT (base layer)
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: [0.0, 0.45, 0.75, 1.0],
|
||||
colors: [
|
||||
Color(0xFF2E2E2E),
|
||||
Color(0xFF00271D),
|
||||
Color(0xFF005841),
|
||||
Color.fromARGB(176, 62, 254, 203),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// BLURRED CIRCLE (subtle rounded glow)
|
||||
Positioned(
|
||||
bottom: -120,
|
||||
left: -60,
|
||||
right: -60,
|
||||
child: Container(
|
||||
height: 300,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Color.fromARGB(
|
||||
0,
|
||||
62,
|
||||
254,
|
||||
203,
|
||||
), // same green with opacity
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(
|
||||
69,
|
||||
62,
|
||||
254,
|
||||
142,
|
||||
), // stronger outer glow
|
||||
blurRadius: 60,
|
||||
spreadRadius: 100,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 70),
|
||||
Image.asset("assets/images/logo2.png", width: 200),
|
||||
|
||||
/// PAGEVIEW (SVG + TEXT ONLY)
|
||||
Expanded(
|
||||
child: PageView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
controller: controller,
|
||||
onPageChanged: (i) => setState(() => index = i),
|
||||
children: const [
|
||||
OnboardingPage(
|
||||
imagePath: "assets/images/Onboarding1.svg",
|
||||
text:
|
||||
"سجل دخولك وخروجك بسهولة وتابع دوامك\nيومياً بدون تعقيد",
|
||||
),
|
||||
OnboardingPage(
|
||||
imagePath: "assets/images/Onboarding2.svg",
|
||||
text:
|
||||
"اعرف تفاصيل راتبك وقدّم طلب الإجازة\nوتابع حالته بكل شفافية",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 40),
|
||||
child: OnboardingButton(
|
||||
text: "تسجيل دخول",
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AuthScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
/// DOTS INDICATOR
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(2, (i) {
|
||||
bool active = i == index;
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: active ? Colors.white : Colors.white60,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
const SizedBox(height: 100),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
258
lib/presentation/screens/request_advance_scrren.dart
Normal file
258
lib/presentation/screens/request_advance_scrren.dart
Normal file
@@ -0,0 +1,258 @@
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
import '../widgets/onboarding_button.dart';
|
||||
import '../../models/advance_request.dart';
|
||||
import '../../core/services/request_service.dart';
|
||||
|
||||
class RequestAdvanceScreen extends StatefulWidget {
|
||||
const RequestAdvanceScreen({super.key});
|
||||
|
||||
@override
|
||||
State<RequestAdvanceScreen> createState() => _RequestAdvanceScreenState();
|
||||
}
|
||||
|
||||
class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
|
||||
|
||||
// Text controller for amount
|
||||
final TextEditingController amountController = TextEditingController();
|
||||
// Text controller for reason
|
||||
final TextEditingController reasonController = TextEditingController();
|
||||
|
||||
// Use the singleton instance
|
||||
final RequestService _requestService = RequestService();
|
||||
|
||||
// Method to save the advance request
|
||||
Future<void> _saveAdvanceRequest() async {
|
||||
if (amountController.text.isEmpty || reasonController.text.isEmpty) {
|
||||
// Show an error message if fields are empty
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('الرجاء إدخال جميع الحقول'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the amount to double
|
||||
final amount = double.tryParse(amountController.text);
|
||||
if (amount == null || amount <= 0) {
|
||||
// Show an error message if amount is invalid
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('الرجاء إدخال مبلغ صحيح'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new advance request with default status "waiting"
|
||||
final advanceRequest = AdvanceRequest(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
amount: amount,
|
||||
reason: reasonController.text,
|
||||
status: "waiting", // Default status
|
||||
);
|
||||
|
||||
try {
|
||||
// Save the advance request
|
||||
await _requestService.addAdvanceRequest(advanceRequest);
|
||||
|
||||
// Show a success message
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('تم إرسال طلب السلفة بنجاح'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
|
||||
// Navigate back to the previous screen
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
// Show an error message if something went wrong
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('حدث خطأ: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// Settings bar
|
||||
SettingsBar(
|
||||
selectedIndex: -1,
|
||||
onTap: (_) {},
|
||||
showBackButton: true,
|
||||
onBackTap: () => Navigator.pop(context),
|
||||
iconPaths: const [
|
||||
"assets/images/user.svg",
|
||||
"assets/images/bell.svg",
|
||||
],
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Title for advance request
|
||||
const Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Text(
|
||||
"طلب سلفة",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
//=============================
|
||||
// AMOUNT INPUT
|
||||
//=============================
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: const Text(
|
||||
"المبلغ المطلوب",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 6),
|
||||
|
||||
Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
height: 58,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x22000000),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: TextField(
|
||||
controller: amountController,
|
||||
textAlign: TextAlign.right,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: "دع .000 ",
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 25),
|
||||
|
||||
// =============================
|
||||
// REASON TEXTFIELD
|
||||
// =============================
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: const Text(
|
||||
"السبب",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// OUTER BORDER CONTAINER
|
||||
Container(
|
||||
padding: const EdgeInsets.all(
|
||||
15,
|
||||
), // border thickness space
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Color(0xFF00FFAA), // green border
|
||||
width: 0.5,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
|
||||
// INNER TEXTFIELD CONTAINER
|
||||
child: Container(
|
||||
height: 100,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEAEAEA),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
// Added Directionality to fix text direction
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: TextField(
|
||||
controller: reasonController,
|
||||
maxLines: 5,
|
||||
textAlign:
|
||||
TextAlign
|
||||
.right, // Added this to align text to the right
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText:
|
||||
"اكتب السبب", // Added placeholder text
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 70),
|
||||
|
||||
// CONFIRM BUTTON
|
||||
Center(
|
||||
child: OnboardingButton(
|
||||
text: "تأكيد الطلب",
|
||||
backgroundColor: const Color(0xFFD1FEF0),
|
||||
textColor: Colors.black,
|
||||
onPressed: _saveAdvanceRequest, // Call the save method
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
642
lib/presentation/screens/request_leave_screen.dart
Normal file
642
lib/presentation/screens/request_leave_screen.dart
Normal file
@@ -0,0 +1,642 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
import '../widgets/onboarding_button.dart';
|
||||
import '../../models/leave_request.dart';
|
||||
import '../../core/services/request_service.dart';
|
||||
|
||||
class RequestLeaveScreen extends StatefulWidget {
|
||||
const RequestLeaveScreen({super.key});
|
||||
|
||||
@override
|
||||
State<RequestLeaveScreen> createState() => _RequestLeaveScreenState();
|
||||
}
|
||||
|
||||
class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
||||
// Dropdown value - initialize with default
|
||||
String leaveType = "إجازة مرضية ";
|
||||
|
||||
// Toggle switch
|
||||
bool isTimedLeave = false;
|
||||
|
||||
// Date & time selectors
|
||||
DateTime? fromDate = DateTime.now();
|
||||
DateTime? toDate = DateTime.now();
|
||||
|
||||
TimeOfDay? fromTime = const TimeOfDay(hour: 12, minute: 00);
|
||||
TimeOfDay? toTime = const TimeOfDay(hour: 12, minute: 00);
|
||||
|
||||
// Text controller for reason
|
||||
final TextEditingController reasonController = TextEditingController();
|
||||
|
||||
// Use the singleton instance
|
||||
final RequestService _requestService = RequestService();
|
||||
|
||||
/// PICK DATE
|
||||
Future<void> pickDate(bool isFrom) async {
|
||||
DateTime initial = isFrom ? fromDate! : toDate!;
|
||||
DateTime? newDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: initial,
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2035),
|
||||
builder: (context, child) {
|
||||
return Theme(data: ThemeData.dark(), child: child!);
|
||||
},
|
||||
);
|
||||
|
||||
if (newDate != null) {
|
||||
setState(() {
|
||||
if (isFrom) {
|
||||
fromDate = newDate;
|
||||
} else {
|
||||
toDate = newDate;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// PICK TIME
|
||||
Future<void> pickTime(bool isFrom) async {
|
||||
TimeOfDay initial = isFrom ? fromTime! : toTime!;
|
||||
TimeOfDay? newTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: initial,
|
||||
builder: (context, child) {
|
||||
return Theme(data: ThemeData.dark(), child: child!);
|
||||
},
|
||||
);
|
||||
|
||||
if (newTime != null) {
|
||||
setState(() {
|
||||
if (isFrom) {
|
||||
fromTime = newTime;
|
||||
} else {
|
||||
toTime = newTime;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Method to save the leave request
|
||||
Future<void> _saveLeaveRequest() async {
|
||||
if (reasonController.text.isEmpty) {
|
||||
// Show an error message if reason is empty
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('الرجاء إدخال السبب'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new leave request with default status "waiting"
|
||||
final leaveRequest = LeaveRequest(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
leaveType: leaveType, // Use the current leaveType value
|
||||
isTimedLeave: isTimedLeave,
|
||||
fromDate: fromDate!,
|
||||
toDate: toDate!,
|
||||
fromTime: fromTime!,
|
||||
toTime: toTime!,
|
||||
reason: reasonController.text,
|
||||
requestDate: DateTime.now(),
|
||||
status: "waiting", // Default status
|
||||
);
|
||||
|
||||
try {
|
||||
// Save the leave request
|
||||
await _requestService.addLeaveRequest(leaveRequest);
|
||||
|
||||
// Show a success message
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('تم إرسال طلب الأجازة بنجاح'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
|
||||
// Navigate back to the previous screen
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
// Show an error message if something went wrong
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('حدث خطأ: $e'), backgroundColor: Colors.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true, // ✅ IMPORTANT
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// In your RequestLeaveScreen's build method, replace the SettingsBar with this:
|
||||
SettingsBar(
|
||||
selectedIndex: -1,
|
||||
onTap: (_) {},
|
||||
showBackButton: true,
|
||||
onBackTap: () => Navigator.pop(context),
|
||||
iconPaths: const [
|
||||
"assets/images/user.svg",
|
||||
"assets/images/bell.svg",
|
||||
],
|
||||
),
|
||||
|
||||
// Title
|
||||
// const SizedBox(height: 30),
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Fixed alignment for "طلب أجازة"
|
||||
const Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Text(
|
||||
"طلب أجازة ",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
//=============================
|
||||
// DROPDOWN: نوع الإجازة
|
||||
//=============================
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: const Text(
|
||||
"نوع الإجازة",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 5),
|
||||
|
||||
// Modified dropdown with disabled state
|
||||
Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Opacity(
|
||||
opacity: isTimedLeave ? 0.45 : 1.0,
|
||||
child: IgnorePointer(
|
||||
ignoring: isTimedLeave,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
),
|
||||
height: 58,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isTimedLeave
|
||||
? Colors.grey[300]
|
||||
: Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x22000000),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: leaveType,
|
||||
icon: const Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
color: Colors.black,
|
||||
size: 28,
|
||||
),
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 17,
|
||||
),
|
||||
isExpanded: true,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
leaveType = value!;
|
||||
// Set toggle based on selected value
|
||||
isTimedLeave = value == "أجازة زمنية";
|
||||
});
|
||||
},
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: "إجازة مرضية ",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text("إجازة مرضية "),
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: "إجازة مدفوعة",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text("إجازة مدفوعة"),
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: "إجازة غير مدفوعة",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text("إجازة غير مدفوعة"),
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: "أجازة زمنية",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text("أجازة زمنية"),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 25),
|
||||
|
||||
//=============================
|
||||
// PERFECT CUSTOM TOGGLE (NEW)
|
||||
//=============================
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isTimedLeave = !isTimedLeave;
|
||||
// Set leave type to "أجازة زمنية" when toggle is ON
|
||||
if (isTimedLeave) {
|
||||
leaveType = "أجازة زمنية";
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
// ---------- TOGGLE ----------
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
width: 75,
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isTimedLeave
|
||||
? const Color(
|
||||
0xFF0A6B4A,
|
||||
) // ON green track
|
||||
: const Color(
|
||||
0xFF9E9E9E,
|
||||
), // OFF grey track
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
),
|
||||
child: Align(
|
||||
alignment:
|
||||
isTimedLeave
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isTimedLeave
|
||||
? const Color(0xFF12BE85) // ON knob
|
||||
: Colors.white, // OFF knob
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0x2D000000),
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 14),
|
||||
|
||||
// ---------- Dot (ALWAYS visible) ----------
|
||||
Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF32C59A),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 6),
|
||||
|
||||
// ---------- Line (ALWAYS visible) ----------
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 2,
|
||||
color: const Color(0xFF32C59A),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// ---------- Label ----------
|
||||
const Text(
|
||||
"إجازة زمنية",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// =============================
|
||||
// DATE PICKER BOX
|
||||
// =============================
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF4F4F4),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x33000000),
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.end, // ALIGN RIGHT
|
||||
children: [
|
||||
// TEXT aligned right
|
||||
const Text(
|
||||
"فترة الإجازة",
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 8),
|
||||
|
||||
// 🟢 YOUR CUSTOM SVG ICON
|
||||
SvgPicture.asset(
|
||||
"assets/images/calendar.svg", // <-- replace with your icon filename
|
||||
width: 24,
|
||||
height: 24,
|
||||
color: Color(
|
||||
0xFF007C46,
|
||||
), // Optional: match your green
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// From date row
|
||||
_dateRow(
|
||||
label: "من",
|
||||
date: fromDate!,
|
||||
time: fromTime!,
|
||||
onDateTap:
|
||||
isTimedLeave ? null : () => pickDate(true),
|
||||
onTimeTap: () => pickTime(true),
|
||||
),
|
||||
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// To date row
|
||||
_dateRow(
|
||||
label: "الى",
|
||||
date: toDate!,
|
||||
time: toTime!,
|
||||
onDateTap:
|
||||
isTimedLeave ? null : () => pickDate(false),
|
||||
onTimeTap: () => pickTime(false),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// =============================
|
||||
// REASON TEXTFIELD (Two Containers)
|
||||
// =============================
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: const Text(
|
||||
"السبب",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// OUTER BORDER CONTAINER
|
||||
Container(
|
||||
padding: const EdgeInsets.all(
|
||||
12,
|
||||
), // border thickness space
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Color(0xFF00FFAA), // green border
|
||||
width: 0.5,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
|
||||
// INNER TEXTFIELD CONTAINER
|
||||
child: Container(
|
||||
height: 70,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEAEAEA),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
// Added Directionality to fix text direction
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: TextField(
|
||||
controller: reasonController,
|
||||
maxLines: 5,
|
||||
textAlign:
|
||||
TextAlign
|
||||
.right, // Added this to align text to the right
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText:
|
||||
"اكتب السبب", // Added placeholder text
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// CONFIRM BUTTON
|
||||
Center(
|
||||
child: OnboardingButton(
|
||||
text: "تأكيد الطلب",
|
||||
backgroundColor: const Color(0xFFD1FEF0),
|
||||
textColor: Colors.black, // Changed to black
|
||||
onPressed: _saveLeaveRequest, // Call the save method
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// CUSTOM DATE ROW WIDGET
|
||||
// ===============================================================
|
||||
Widget _dateRow({
|
||||
required String label,
|
||||
required DateTime date,
|
||||
required TimeOfDay time,
|
||||
required VoidCallback? onDateTap,
|
||||
required VoidCallback onTimeTap,
|
||||
}) {
|
||||
bool dateDisabled = onDateTap == null;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
height: 55,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// -----------------------
|
||||
// DATE PART (can be disabled)
|
||||
// -----------------------
|
||||
Opacity(
|
||||
opacity: dateDisabled ? 0.45 : 1,
|
||||
child: IgnorePointer(
|
||||
ignoring: dateDisabled,
|
||||
child: GestureDetector(
|
||||
onTap: onDateTap,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
"${date.year}-${date.month}-${date.day}",
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
_weekday(date.weekday),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const Spacer(),
|
||||
|
||||
// -----------------------
|
||||
// TIME PART (always active)
|
||||
// -----------------------
|
||||
GestureDetector(
|
||||
onTap: onTimeTap,
|
||||
child: Text(
|
||||
"${time.hour}:${time.minute.toString().padLeft(2, '0')}",
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 10),
|
||||
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(color: Colors.black, fontSize: 15),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _weekday(int day) {
|
||||
switch (day) {
|
||||
case 1:
|
||||
return "الإثنين";
|
||||
case 2:
|
||||
return "الثلاثاء";
|
||||
case 3:
|
||||
return "الأربعاء";
|
||||
case 4:
|
||||
return "الخميس";
|
||||
case 5:
|
||||
return "الجمعة";
|
||||
case 6:
|
||||
return "السبت";
|
||||
case 7:
|
||||
return "الأحد";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
40
lib/presentation/screens/splash_screen.dart
Normal file
40
lib/presentation/screens/splash_screen.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'onboarding_screen.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SplashScreen> createState() => _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
FlutterNativeSplash.remove();
|
||||
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => OnboardingScreen()),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage("assets/images/splash.png"),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
child: Center(child: Image.asset("assets/images/logo.png", width: 200)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
317
lib/presentation/screens/user_settings_screen.dart
Normal file
317
lib/presentation/screens/user_settings_screen.dart
Normal file
@@ -0,0 +1,317 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
import 'about_screen.dart';
|
||||
import 'auth_screen.dart';
|
||||
import '../widgets/change_password_modal.dart';
|
||||
|
||||
|
||||
class UserSettingsScreen extends StatefulWidget {
|
||||
const UserSettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
State<UserSettingsScreen> createState() => _UserSettingsScreenState();
|
||||
}
|
||||
|
||||
class _UserSettingsScreenState extends State<UserSettingsScreen> {
|
||||
bool _notificationsOn = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
/// -------------------- SETTINGS BAR --------------------
|
||||
SettingsBar(
|
||||
selectedIndex: 0,
|
||||
onTap: (_) {},
|
||||
showBackButton: true,
|
||||
onBackTap: () => Navigator.pop(context),
|
||||
iconPaths: const [],
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
/// -------------------- PAGE TITLE --------------------
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 40),
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: const Text(
|
||||
"الإعدادات",
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// -------------------- FORM CONTAINER --------------------
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 40),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.85,
|
||||
height: MediaQuery.of(context).size.height * 0.62,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xF3EEFFFA),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 30,
|
||||
offset: Offset(0, 20),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 22,
|
||||
vertical: 55,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
|
||||
/// -------------------- USER NAME --------------------
|
||||
const Text(
|
||||
"اسم الموظف",
|
||||
style: TextStyle(
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 6),
|
||||
|
||||
/// ✅ CLICKABLE + UNDERLINED
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder:
|
||||
(_) => const ChangePasswordModal(),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
"تغيير كلمة المرور",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: Colors.black54,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationThickness: 1.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
SvgPicture.asset(
|
||||
"assets/images/edit.svg",
|
||||
width: 22,
|
||||
height: 22,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
/// -------------------- NOTIFICATION SWITCH --------------------
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_notificationsOn = !_notificationsOn;
|
||||
});
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(
|
||||
milliseconds: 250,
|
||||
),
|
||||
width: 75,
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
_notificationsOn
|
||||
? const Color(0xFF0A6B4A)
|
||||
: const Color(0xFFB5B5B5),
|
||||
borderRadius: BorderRadius.circular(
|
||||
40,
|
||||
),
|
||||
),
|
||||
child: Align(
|
||||
alignment:
|
||||
_notificationsOn
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(
|
||||
milliseconds: 250,
|
||||
),
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
_notificationsOn
|
||||
? const Color(0xFF12BE85)
|
||||
: const Color(0xFF0F0F0F),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x2D000000),
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
"الإشعارات",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SvgPicture.asset(
|
||||
"assets/images/ball2.svg",
|
||||
width: 22,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
_divider(),
|
||||
|
||||
_settingsRow(
|
||||
label: "عن الشركة",
|
||||
icon: "assets/images/info.svg",
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const AboutScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
_divider(),
|
||||
|
||||
_settingsRow(
|
||||
label: "تسجيل خروج",
|
||||
icon: "assets/images/logout2.svg",
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const AuthScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// -------------------- AVATAR --------------------
|
||||
Positioned(
|
||||
top: -55,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: const Color(0xFFE1E1E1),
|
||||
border: Border.all(
|
||||
color: const Color(0xFF2D5E50),
|
||||
width: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _divider() {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: const Color(0xFF008864),
|
||||
margin: const EdgeInsets.symmetric(vertical: 12),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _settingsRow({
|
||||
required String label,
|
||||
required String icon,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(width: 24),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
SvgPicture.asset(icon, width: 23),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user