first commit
This commit is contained in:
87
lib/constants/app_theme.dart
Normal file
87
lib/constants/app_theme.dart
Normal file
@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppTheme {
|
||||
|
||||
static const primaryColor = Color(0xFF668D7B);
|
||||
static const secondaryColor = Color(0xFFBEDDCC);
|
||||
static const cardColor = Color(0xFF93B5A4);
|
||||
static const yellowColor = Color(0xFFF1CC83);
|
||||
static const redColor = Colors.red;
|
||||
static const brownColor = Color(0xFF6B6148);
|
||||
static const blackColor = Color(0xFF444444);
|
||||
static const textColor = Color(0xFFFFFFFF);
|
||||
|
||||
static get theme => ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: primaryColor,
|
||||
surface: primaryColor,
|
||||
),
|
||||
scaffoldBackgroundColor: primaryColor,
|
||||
textTheme: const TextTheme(
|
||||
bodySmall: bodySmall,
|
||||
bodyMedium: bodyMedium,
|
||||
bodyLarge: bodyLarge,
|
||||
titleLarge: titleLarge,
|
||||
headlineLarge: headlineLarge,
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
elevation: WidgetStateProperty.all(0),
|
||||
backgroundColor: WidgetStateProperty.all(textColor),
|
||||
padding: WidgetStateProperty.all(const EdgeInsets.symmetric(vertical: 0, horizontal: 4)),
|
||||
foregroundColor: WidgetStateProperty.all(primaryColor),
|
||||
textStyle: WidgetStateProperty.all(
|
||||
bodyMedium
|
||||
)
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
elevation: WidgetStateProperty.all(0),
|
||||
backgroundColor: WidgetStateProperty.all(primaryColor),
|
||||
padding: WidgetStateProperty.all(const EdgeInsets.symmetric(vertical: 0, horizontal: 4)),
|
||||
foregroundColor: WidgetStateProperty.all(textColor),
|
||||
textStyle: WidgetStateProperty.all(
|
||||
bodyMedium
|
||||
)
|
||||
),
|
||||
),
|
||||
cardColor: cardColor,
|
||||
);
|
||||
|
||||
static const bodySmall = TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w300,
|
||||
fontFamily: 'NotoSansArabic',
|
||||
);
|
||||
|
||||
static const bodyMedium = TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontFamily: 'NotoSansArabic',
|
||||
);
|
||||
|
||||
static const bodyLarge = TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontFamily: 'NotoSansArabic',
|
||||
);
|
||||
|
||||
static const titleLarge = TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'NotoSansArabic',
|
||||
);
|
||||
|
||||
static const headlineLarge = TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontFamily: 'NotoSansArabic',
|
||||
);
|
||||
}
|
47
lib/extensions/face_detection_extension.dart
Normal file
47
lib/extensions/face_detection_extension.dart
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||
import 'package:google_mlkit_commons/google_mlkit_commons.dart';
|
||||
|
||||
extension MLKitUtils on AnalysisImage {
|
||||
InputImage toInputImage() {
|
||||
|
||||
return when(
|
||||
nv21: (image) {
|
||||
return InputImage.fromBytes(
|
||||
bytes: image.bytes,
|
||||
metadata: InputImageMetadata(
|
||||
rotation: inputImageRotation,
|
||||
format: InputImageFormat.nv21,
|
||||
size: image.size,
|
||||
bytesPerRow: image.planes.first.bytesPerRow,
|
||||
),
|
||||
);
|
||||
},
|
||||
bgra8888: (image) {
|
||||
return InputImage.fromBytes(
|
||||
bytes: image.bytes,
|
||||
metadata: InputImageMetadata(
|
||||
size: size,
|
||||
rotation: inputImageRotation,
|
||||
format: InputImageFormat.nv21,
|
||||
bytesPerRow: image.planes.first.bytesPerRow,
|
||||
),
|
||||
);
|
||||
}
|
||||
)!;
|
||||
}
|
||||
|
||||
InputImageRotation get inputImageRotation =>
|
||||
InputImageRotation.values.byName(rotation.name);
|
||||
|
||||
InputImageFormat get inputImageFormat {
|
||||
switch (format) {
|
||||
case InputAnalysisImageFormat.bgra8888:
|
||||
return InputImageFormat.bgra8888;
|
||||
case InputAnalysisImageFormat.nv21:
|
||||
return InputImageFormat.nv21;
|
||||
default:
|
||||
return InputImageFormat.yuv420;
|
||||
}
|
||||
}
|
||||
}
|
37
lib/main.dart
Normal file
37
lib/main.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/log_in_screen.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await EasyLocalization.ensureInitialized();
|
||||
const List<Locale> supportedLocales = [Locale("ar")];
|
||||
|
||||
runApp(EasyLocalization(
|
||||
supportedLocales: supportedLocales,
|
||||
path: 'assets/languages',
|
||||
startLocale: const Locale("ar"),
|
||||
fallbackLocale: const Locale("ar"),
|
||||
saveLocale: true,
|
||||
child: const MyApp(),
|
||||
));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Gascom',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: AppTheme.theme,
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
supportedLocales: context.supportedLocales,
|
||||
locale: context.locale,
|
||||
home: const LogInScreen(),
|
||||
);
|
||||
}
|
||||
}
|
118
lib/screens/change_engine_screen.dart
Normal file
118
lib/screens/change_engine_screen.dart
Normal file
@ -0,0 +1,118 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/screens/success_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/app_text_field.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class ChangeEngineScreen extends StatefulWidget {
|
||||
const ChangeEngineScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ChangeEngineScreen> createState() => _ChangeEngineScreenState();
|
||||
}
|
||||
|
||||
class _ChangeEngineScreenState extends State<ChangeEngineScreen> {
|
||||
TextEditingController reasonController = TextEditingController();
|
||||
TextEditingController countryController = TextEditingController();
|
||||
TextEditingController engineNumberController = TextEditingController();
|
||||
TextEditingController enginePowerController = TextEditingController();
|
||||
TextEditingController engineTypeController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(
|
||||
title: "تبديل المحرك",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"لتبديل المحرك يرجى تزويدنا بمعلومات المحرك الجديد الجديد.",
|
||||
maxLines: 3,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
AutoSizeText(
|
||||
"سبب التبديل",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: reasonController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
AutoSizeText(
|
||||
"منشأ المحرك الجديد",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: countryController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
AutoSizeText(
|
||||
"رقم المحرك",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: engineNumberController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
AutoSizeText(
|
||||
"القدرة التوليدية (KV.A)",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: enginePowerController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
AutoSizeText(
|
||||
"نوع المحرك",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: engineTypeController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 25.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: handleSendRequest,
|
||||
label: "تاكيد",
|
||||
isElevated: true),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void handleSendRequest() {
|
||||
// handle request
|
||||
|
||||
pushScreenWithoutNavBar(
|
||||
context,
|
||||
const SuccessScreen(
|
||||
title: "تم تقديم الطلب بنجاح",
|
||||
subtitle: "سيتم مراجعة طلبك، واعلامك بالموافقة قريباً"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
127
lib/screens/change_location.dart
Normal file
127
lib/screens/change_location.dart
Normal file
@ -0,0 +1,127 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/screens/document_camera_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/app_text_field.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class ChangeLocationScreen extends StatefulWidget {
|
||||
const ChangeLocationScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ChangeLocationScreen> createState() => _ChangeLocationScreenState();
|
||||
}
|
||||
|
||||
class _ChangeLocationScreenState extends State<ChangeLocationScreen> {
|
||||
|
||||
TextEditingController reasonController = TextEditingController();
|
||||
TextEditingController districtController = TextEditingController();
|
||||
TextEditingController mahalaController = TextEditingController();
|
||||
TextEditingController streetController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(
|
||||
title: "تحويل موقع المولدة",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"لتحويل الموقع يرجى تزويدنا بمعلومات دقيقة للموقع الجديد بالاضافة الى سند ملكية للارض الجديدة.",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 2,
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
|
||||
AutoSizeText(
|
||||
"سبب التحويل",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: reasonController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
|
||||
AutoSizeText(
|
||||
"المنطقة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: districtController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
|
||||
AutoSizeText(
|
||||
"المحلة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: mahalaController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
|
||||
AutoSizeText(
|
||||
"الزقاق",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
AppTextField(
|
||||
controller: streetController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: handleSetLocationOnMap,
|
||||
label: "تحديد على الخريطة",
|
||||
isElevated: false
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25.0),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: handleSendRequest,
|
||||
label: "تاكيد",
|
||||
isElevated: true
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void handleSendRequest() {
|
||||
// handle request
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => const DocumentCameraScreen(title: "سند ملكية", description: "ضع سند ملكية الارض الجديدة في المربع، وتاكد من وجود اضاءة مناسبة",),
|
||||
));
|
||||
}
|
||||
|
||||
void handleSetLocationOnMap() {
|
||||
}
|
||||
}
|
126
lib/screens/document_camera_screen.dart
Normal file
126
lib/screens/document_camera_screen.dart
Normal file
@ -0,0 +1,126 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/success_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class DocumentCameraScreen extends StatefulWidget {
|
||||
const DocumentCameraScreen({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String description;
|
||||
|
||||
@override
|
||||
State<DocumentCameraScreen> createState() => _DocumentCameraScreenState();
|
||||
}
|
||||
|
||||
class _DocumentCameraScreenState extends State<DocumentCameraScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: widget.title,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: CameraAwesomeBuilder.custom(
|
||||
sensorConfig: SensorConfig.single(
|
||||
aspectRatio: CameraAspectRatios.ratio_16_9,
|
||||
flashMode: FlashMode.none,
|
||||
sensor: Sensor.position(SensorPosition.back),
|
||||
zoom: 0.0,
|
||||
),
|
||||
saveConfig: SaveConfig.photo(
|
||||
|
||||
),
|
||||
builder: (state, previewSize) {
|
||||
return Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: ClipPath(
|
||||
clipper: HoleClipper(),
|
||||
child: Container(
|
||||
color: AppTheme.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 130,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: AutoSizeText(
|
||||
widget.description,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
)
|
||||
),
|
||||
Positioned(
|
||||
bottom: 70,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
// save image as bytes in a variable using the state object
|
||||
// state.when(
|
||||
// onPhotoMode:(s) => s.takePhoto(
|
||||
// onPhoto: (request) => request.when(
|
||||
// single: (r) => r.path,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SuccessScreen(title: 'تم تقديم الطلب بنجاح', subtitle: 'سيتم مراجعة طلبك، واعلامك بموعد الكشف الموقعي',),
|
||||
),
|
||||
);
|
||||
},
|
||||
label: "التقاط",
|
||||
isElevated: true
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HoleClipper extends CustomClipper<Path> {
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
final path = Path()
|
||||
..addRect(Rect.fromLTWH(0, 0, size.width, size.height))
|
||||
..addRRect(RRect.fromRectAndRadius(
|
||||
Rect.fromCenter(
|
||||
center: Offset(size.width / 2, size.height * 0.38),
|
||||
width: size.width - (size.width * 0.2), // Adjust the width of the square
|
||||
height: size.height - (size.height * 0.3), // Adjust the height of the square to match the width for a square shape
|
||||
),
|
||||
const Radius.circular(20), // Adjust the border radius as needed
|
||||
))
|
||||
..fillType = PathFillType.evenOdd;
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
}
|
276
lib/screens/face_detection_screen.dart
Normal file
276
lib/screens/face_detection_screen.dart
Normal file
@ -0,0 +1,276 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/extensions/face_detection_extension.dart';
|
||||
import 'package:gascom/screens/order_details_screen.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class FaceDetectionScreen extends StatefulWidget {
|
||||
const FaceDetectionScreen({super.key});
|
||||
|
||||
@override
|
||||
State<FaceDetectionScreen> createState() => _FaceDetectionScreenState();
|
||||
}
|
||||
|
||||
class _FaceDetectionScreenState extends State<FaceDetectionScreen> {
|
||||
|
||||
String message = "ضع وجهك في الدائرة المخصصة وتاكد من وجود اضاءة مناسبة";
|
||||
|
||||
final options = FaceDetectorOptions(
|
||||
enableContours: true,
|
||||
enableClassification: true,
|
||||
enableLandmarks: true,
|
||||
performanceMode: FaceDetectorMode.accurate,
|
||||
minFaceSize: 0.5,
|
||||
);
|
||||
|
||||
late final faceDetector = FaceDetector(options: options);
|
||||
|
||||
// Face stability tracking
|
||||
DateTime? _stableStartTime;
|
||||
Rect? _lastFacePosition;
|
||||
|
||||
// Constants for stability detection
|
||||
final double _movementThreshold = 40;
|
||||
final Duration _requiredStableTime = const Duration(seconds: 2);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
faceDetector.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(
|
||||
title: "تحقق الوجه",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
CameraAwesomeBuilder.previewOnly(
|
||||
builder: (state, preview) => const SizedBox(),
|
||||
imageAnalysisConfig: AnalysisConfig(
|
||||
maxFramesPerSecond: 10,
|
||||
),
|
||||
onImageForAnalysis: handleImageAnalysis,
|
||||
sensorConfig: SensorConfig.single(
|
||||
aspectRatio: CameraAspectRatios.ratio_4_3,
|
||||
flashMode: FlashMode.none,
|
||||
sensor: Sensor.position(SensorPosition.front),
|
||||
zoom: 0.0,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: ClipPath(
|
||||
clipper: HoleClipper(),
|
||||
child: Container(
|
||||
color: AppTheme.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 50,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: Text(
|
||||
message,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isFaceStable(Rect currentFace) {
|
||||
if (_lastFacePosition == null) {
|
||||
_lastFacePosition = currentFace;
|
||||
return false;
|
||||
}
|
||||
|
||||
final double movement =
|
||||
(currentFace.center.dx - _lastFacePosition!.center.dx).abs()
|
||||
+ (currentFace.center.dy - _lastFacePosition!.center.dy).abs();
|
||||
|
||||
_lastFacePosition = currentFace;
|
||||
return movement < _movementThreshold;
|
||||
}
|
||||
|
||||
bool _isFaceCovered(Face face) {
|
||||
// Check if essential face contours are present and visible
|
||||
final requiredContours = [
|
||||
FaceContourType.face,
|
||||
FaceContourType.leftEye,
|
||||
FaceContourType.rightEye,
|
||||
FaceContourType.noseBridge,
|
||||
FaceContourType.noseBottom,
|
||||
FaceContourType.leftCheek,
|
||||
FaceContourType.rightCheek,
|
||||
FaceContourType.upperLipTop,
|
||||
FaceContourType.upperLipBottom,
|
||||
FaceContourType.lowerLipTop,
|
||||
FaceContourType.lowerLipBottom,
|
||||
];
|
||||
|
||||
// Count visible contours
|
||||
int visibleContours = 0;
|
||||
for (var contourType in requiredContours) {
|
||||
if (face.contours[contourType]?.points.isNotEmpty ?? false) {
|
||||
visibleContours++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check landmarks visibility
|
||||
final hasEssentialLandmarks =
|
||||
face.landmarks[FaceLandmarkType.leftEye] != null &&
|
||||
face.landmarks[FaceLandmarkType.rightEye] != null &&
|
||||
face.landmarks[FaceLandmarkType.noseBase] != null &&
|
||||
face.landmarks[FaceLandmarkType.bottomMouth] != null &&
|
||||
face.landmarks[FaceLandmarkType.leftMouth] != null &&
|
||||
face.landmarks[FaceLandmarkType.rightMouth] != null &&
|
||||
face.landmarks[FaceLandmarkType.leftEar] != null &&
|
||||
face.landmarks[FaceLandmarkType.rightEar] != null;
|
||||
|
||||
// Check if eyes are open (existing check enhanced)
|
||||
final eyesOpen = (face.rightEyeOpenProbability ?? 0) > 0.7 && (face.leftEyeOpenProbability ?? 0) > 0.7;
|
||||
|
||||
print("@@@@@@@@@@@@@@@@@@");
|
||||
print(visibleContours);
|
||||
print(hasEssentialLandmarks);
|
||||
print(eyesOpen);
|
||||
return !hasEssentialLandmarks ||
|
||||
visibleContours != requiredContours.length ||
|
||||
!eyesOpen;
|
||||
}
|
||||
|
||||
Future<void> handleImageAnalysis(AnalysisImage img) async {
|
||||
final inputImage = img.toInputImage();
|
||||
|
||||
try {
|
||||
final faces = await faceDetector.processImage(inputImage);
|
||||
|
||||
if (!context.mounted || !mounted) return;
|
||||
if (faces.length == 1) {
|
||||
|
||||
var face = faces.first;
|
||||
var rect = faces.first.boundingBox;
|
||||
final bool isStable = _isFaceStable(rect);
|
||||
|
||||
// Check if face is covered
|
||||
if (_isFaceCovered(face)) {
|
||||
setState(() {
|
||||
message = "الرجاء إزالة أي غطاء عن الوجه";
|
||||
});
|
||||
_stableStartTime = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isStable) {
|
||||
setState(() {
|
||||
message = "ثبت وجهك في المكان المخصص وتأكد من أنه وجه حقيقي";
|
||||
});
|
||||
_stableStartTime = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(
|
||||
rect.left > (inputImage.metadata?.size.width ?? 0) * 0.1
|
||||
&& rect.right < (inputImage.metadata?.size.width ?? 0) * 0.9
|
||||
&& rect.top > (inputImage.metadata?.size.height ?? 0) * 0.1
|
||||
&& rect.bottom < (inputImage.metadata?.size.height ?? 0) * 0.9
|
||||
&& (faces.first.rightEyeOpenProbability ?? 0) > 0.3
|
||||
&& (faces.first.leftEyeOpenProbability ?? 0) > 0.3
|
||||
)) {
|
||||
setState(() {
|
||||
message = "ثبت وجهك في المكان المخصص";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
_stableStartTime ??= DateTime.now();
|
||||
|
||||
final stableDuration = DateTime.now().difference(_stableStartTime!);
|
||||
if (stableDuration >= _requiredStableTime) {
|
||||
img.when(
|
||||
nv21: (image) {
|
||||
faceDetector.close();
|
||||
|
||||
Navigator.pop(context);
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const OrderDetailsScreen()),
|
||||
);
|
||||
},
|
||||
bgra8888: (image) {
|
||||
faceDetector.close();
|
||||
Navigator.pop(context);
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const OrderDetailsScreen()),
|
||||
);
|
||||
},
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
setState(() {
|
||||
message = "ثبت وجهك في المكان المخصص";
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
debugPrint("...sending image resulted error $error");
|
||||
}
|
||||
}
|
||||
|
||||
Future<CaptureRequest> handleBuildPath(List<Sensor> sensors) async {
|
||||
final Directory extDir = await getTemporaryDirectory();
|
||||
final Directory testDir =
|
||||
await Directory('${extDir.path}/camerawesome')
|
||||
.create(recursive: true);
|
||||
|
||||
if (sensors.length == 1) {
|
||||
final String filePath =
|
||||
'${testDir.path}/${DateTime.now().millisecondsSinceEpoch}.jpg';
|
||||
return SingleCaptureRequest(filePath, sensors.first);
|
||||
} else {
|
||||
return MultipleCaptureRequest(
|
||||
{
|
||||
for (final sensor in sensors)
|
||||
sensor:
|
||||
'${testDir.path}/${sensor.position == SensorPosition.front ? 'front_' : "back_"}${DateTime.now().millisecondsSinceEpoch}.jpg',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HoleClipper extends CustomClipper<Path> {
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
final path = Path()
|
||||
..addRect(Rect.fromLTWH(0, 0, size.width, size.height))
|
||||
..addRRect(RRect.fromRectAndRadius(
|
||||
Rect.fromCenter(
|
||||
center: Offset(size.width / 2, size.height * 0.45),
|
||||
width: size.width - (size.width * 0.2), // Adjust the width of the square
|
||||
height: size.height - (size.height * 0.3), // Adjust the height of the square to match the width for a square shape
|
||||
),
|
||||
const Radius.circular(20), // Adjust the border radius as needed
|
||||
))
|
||||
..fillType = PathFillType.evenOdd;
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
}
|
69
lib/screens/fines_screen.dart
Normal file
69
lib/screens/fines_screen.dart
Normal file
@ -0,0 +1,69 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
import 'package:gascom/widgets/fine_container.dart';
|
||||
|
||||
class FinesScreen extends StatelessWidget {
|
||||
const FinesScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(
|
||||
title: "غرامات",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
"غرامات غير مدفوعة",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, index) => FineContainer(),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30),
|
||||
child: Row(
|
||||
children: [
|
||||
Divider(
|
||||
color: AppTheme.textColor,
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
"غرامات مدفوعة",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, index) => const FineContainer(),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
56
lib/screens/follow_order_screen.dart
Normal file
56
lib/screens/follow_order_screen.dart
Normal file
@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/widgets/order_container.dart';
|
||||
|
||||
class FollowOrderScreen extends StatefulWidget {
|
||||
const FollowOrderScreen({super.key});
|
||||
|
||||
@override
|
||||
State<FollowOrderScreen> createState() => _FollowOrderScreenState();
|
||||
}
|
||||
|
||||
class _FollowOrderScreenState extends State<FollowOrderScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
"طلبات حالية",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, index) => OrderContainer(),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
"طلبات سابقة",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, index) => OrderContainer(),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
195
lib/screens/generator_info_screen.dart
Normal file
195
lib/screens/generator_info_screen.dart
Normal file
@ -0,0 +1,195 @@
|
||||
import 'package:animated_custom_dropdown/custom_dropdown.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/app_text_field.dart';
|
||||
|
||||
class GeneratorInfoScreen extends StatefulWidget {
|
||||
const GeneratorInfoScreen({super.key});
|
||||
|
||||
@override
|
||||
State<GeneratorInfoScreen> createState() => _GeneratorInfoScreenState();
|
||||
}
|
||||
|
||||
class _GeneratorInfoScreenState extends State<GeneratorInfoScreen> {
|
||||
|
||||
final TextEditingController ownerNameController = TextEditingController();
|
||||
final TextEditingController phoneController = TextEditingController();
|
||||
final TextEditingController areaController = TextEditingController();
|
||||
final TextEditingController districtController = TextEditingController();
|
||||
final TextEditingController laneController = TextEditingController();
|
||||
final TextEditingController generatorBrandController = TextEditingController();
|
||||
final TextEditingController generatorPowerController = TextEditingController();
|
||||
final TextEditingController authorizedNameController = TextEditingController();
|
||||
|
||||
String selectedGeneratorType = 'حكومية';
|
||||
List<String> availableGeneratorType = ['حكومية', 'اهلية'];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
children: [
|
||||
const SizedBox(height: 45,),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"معلومات المولدة",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"#10023432",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 30,),
|
||||
Text(
|
||||
"اسم صاحب المولدة",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: ownerNameController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"رقم الهاتف",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: phoneController,
|
||||
keyboardType: TextInputType.phone,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"فئة المولدة",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
CustomDropdown<String>(
|
||||
hintText: "فئة المولدة",
|
||||
items: availableGeneratorType,
|
||||
closedHeaderPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
expandedHeaderPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: CustomDropdownDecoration(
|
||||
closedFillColor: AppTheme.primaryColor,
|
||||
expandedFillColor: AppTheme.primaryColor,
|
||||
closedBorder: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 1
|
||||
),
|
||||
expandedBorder: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 1
|
||||
),
|
||||
closedBorderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||
expandedBorderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
hintStyle: Theme.of(context).textTheme.bodySmall,
|
||||
listItemStyle: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
headerStyle: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
expandedSuffixIcon: const Icon(CupertinoIcons.chevron_up, color: AppTheme.textColor, size: 18,),
|
||||
closedSuffixIcon: const Icon(CupertinoIcons.chevron_down, color: AppTheme.textColor, size: 18,),
|
||||
),
|
||||
onChanged: (value) {
|
||||
selectedGeneratorType = value ?? "";
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"المنطقة",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: areaController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"المحلة",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: districtController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"الزقاق",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: laneController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"نوع المولدة",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: generatorBrandController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"راس التوليد",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: generatorPowerController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 15,),
|
||||
Text(
|
||||
"مخول استلام الحصة الوقودية",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 6,),
|
||||
AppTextField(
|
||||
controller: authorizedNameController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 40,),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
height: 45,
|
||||
child: AppButton(
|
||||
onPressed: handleUpdateInfo,
|
||||
label: "تحديث المعلومات",
|
||||
isElevated: false
|
||||
)
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 50,),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void handleUpdateInfo() {
|
||||
}
|
||||
}
|
175
lib/screens/home_screen.dart
Normal file
175
lib/screens/home_screen.dart
Normal file
@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/change_engine_screen.dart';
|
||||
import 'package:gascom/screens/change_location.dart';
|
||||
import 'package:gascom/screens/document_camera_screen.dart';
|
||||
import 'package:gascom/screens/fines_screen.dart';
|
||||
import 'package:gascom/screens/notifications_screen.dart';
|
||||
import 'package:gascom/screens/pay_monthly_gas.dart';
|
||||
import 'package:gascom/screens/service_fees_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/home_grid_item.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
}
|
||||
|
||||
class _HomeScreenState extends State<HomeScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 50,),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
width: 160,
|
||||
"assets/svgs/logo.svg",
|
||||
semanticsLabel: 'Logo',
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => pushScreenWithoutNavBar(context, const NotificationsScreen()),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: SvgPicture.asset(
|
||||
"assets/svgs/notification.svg",
|
||||
semanticsLabel: 'Menu',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40,),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.65,
|
||||
child: const Divider(
|
||||
color: AppTheme.yellowColor,
|
||||
thickness: 3,
|
||||
height: 0,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.35,
|
||||
child: const Divider(
|
||||
color: AppTheme.secondaryColor,
|
||||
thickness: 1,
|
||||
height: 0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 30,),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"الخدمات",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
SizedBox(
|
||||
width: 140,
|
||||
height: 32,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, const ServiceFeesScreen());
|
||||
},
|
||||
label: "رسوم الخدمات",
|
||||
isElevated: false
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30,),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: GridView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 20,
|
||||
mainAxisSpacing: 5,
|
||||
childAspectRatio: MediaQuery.sizeOf(context).width / (MediaQuery.sizeOf(context).height / 1.3),
|
||||
),
|
||||
children: [
|
||||
HomeGridItem(
|
||||
title: "طلب حصة",
|
||||
svgPath: "assets/svgs/gas_station.svg",
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, PayMonthlyGas());
|
||||
}
|
||||
),
|
||||
HomeGridItem(
|
||||
title: "غرامات",
|
||||
svgPath: "assets/svgs/dollar_flag.svg",
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, const FinesScreen());
|
||||
}
|
||||
),
|
||||
HomeGridItem(
|
||||
title: "تحويل موقع",
|
||||
svgPath: "assets/svgs/location_pin.svg",
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, const ChangeLocationScreen());
|
||||
}
|
||||
),
|
||||
HomeGridItem(
|
||||
title: "تبديل محرك",
|
||||
svgPath: "assets/svgs/settings.svg",
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, const ChangeEngineScreen());
|
||||
}
|
||||
),
|
||||
HomeGridItem(
|
||||
title: "تجديد بطاقة الحصة الوقودية",
|
||||
svgPath: "assets/svgs/profile_paper.svg",
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, const DocumentCameraScreen(title: "تجديد دفتر", description: "لتجديد الدفتر يرجى ارفاق صورة للدفتر الحالي, ضع الدفتر في المربع، وتاكد من وجود اضاءة جيدة ثم اضغط التقاط.",));
|
||||
}
|
||||
),
|
||||
HomeGridItem(
|
||||
title: "تغيير مخول",
|
||||
svgPath: "assets/svgs/user_sync.svg",
|
||||
onPressed: () {
|
||||
}
|
||||
),
|
||||
HomeGridItem(
|
||||
title: "الدعم الفني",
|
||||
svgPath: "assets/svgs/headphone.svg",
|
||||
onPressed: () {}
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
145
lib/screens/log_in_screen.dart
Normal file
145
lib/screens/log_in_screen.dart
Normal file
@ -0,0 +1,145 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/nfc_screen.dart';
|
||||
import 'package:gascom/screens/otp_provider_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:pinput/pinput.dart';
|
||||
|
||||
class LogInScreen extends StatefulWidget {
|
||||
const LogInScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LogInScreen> createState() => _LogInScreenState();
|
||||
}
|
||||
|
||||
class _LogInScreenState extends State<LogInScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
children: [
|
||||
const SizedBox(height: 150),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.6,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svgs/logo.svg",
|
||||
semanticsLabel: 'Home',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 50),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"مرحبا بكم في كازكوم، يرجى ادخال رقم الترخيص الخاص بصاحب المولدة",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"أو قم بمسح رمز QR الموجود على البطاقة",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 35),
|
||||
Row(
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
onTap: () {
|
||||
// TODO: Implement QR Code Scanner
|
||||
},
|
||||
child: Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
border: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
"assets/svgs/camera.svg",
|
||||
semanticsLabel: 'QR Code',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
border: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Pinput(
|
||||
length: 5,
|
||||
defaultPinTheme: PinTheme(
|
||||
width: 30,
|
||||
height: 35,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
decoration: const BoxDecoration(
|
||||
// color: AppTheme.textColor,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppTheme.textColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
onCompleted: (value) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => OtpProviderScreen(
|
||||
cardNumber: int.parse(value),
|
||||
)));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 50),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
fullscreenDialog: true,
|
||||
// builder: (context) => NewGeneratorScreen(),
|
||||
builder: (context) => NfcScreen(),
|
||||
));
|
||||
},
|
||||
label: "مولـدة جـــديــدة",
|
||||
isElevated: false
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
120
lib/screens/national_id_camera_screen.dart
Normal file
120
lib/screens/national_id_camera_screen.dart
Normal file
@ -0,0 +1,120 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class NationalIdCameraScreen extends StatefulWidget {
|
||||
const NationalIdCameraScreen({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.description,
|
||||
required this.onScanComplete,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String description;
|
||||
final void Function() onScanComplete;
|
||||
|
||||
@override
|
||||
State<NationalIdCameraScreen> createState() => _NationalIdCameraScreenState();
|
||||
}
|
||||
|
||||
class _NationalIdCameraScreenState extends State<NationalIdCameraScreen> {
|
||||
|
||||
String? error;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(),
|
||||
body: SafeArea(
|
||||
child: CameraAwesomeBuilder.custom(
|
||||
sensorConfig: SensorConfig.single(
|
||||
aspectRatio: CameraAspectRatios.ratio_16_9,
|
||||
flashMode: FlashMode.none,
|
||||
sensor: Sensor.position(SensorPosition.back),
|
||||
zoom: 0.0,
|
||||
),
|
||||
saveConfig: SaveConfig.photo(
|
||||
|
||||
),
|
||||
builder: (state, previewSize) {
|
||||
return Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: ClipPath(
|
||||
clipper: HoleClipper(),
|
||||
child: Container(
|
||||
color: AppTheme.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 30,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: AutoSizeText(
|
||||
widget.title,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
)
|
||||
),
|
||||
Positioned(
|
||||
top: 100,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: AutoSizeText(
|
||||
widget.subtitle,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
)
|
||||
),
|
||||
Positioned(
|
||||
bottom: 200,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: AutoSizeText(
|
||||
error ?? widget.description,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HoleClipper extends CustomClipper<Path> {
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
final path = Path()
|
||||
..addRect(Rect.fromLTWH(0, 0, size.width, size.height))
|
||||
..addRRect(RRect.fromRectAndRadius(
|
||||
Rect.fromCenter(
|
||||
center: Offset(size.width / 2, size.height * 0.38),
|
||||
width: size.width - (size.width * 0.1), // Adjust the width of the square
|
||||
height: size.height - (size.height * 0.7), // Adjust the height of the square to match the width for a square shape
|
||||
),
|
||||
const Radius.circular(20), // Adjust the border radius as needed
|
||||
))
|
||||
..fillType = PathFillType.evenOdd;
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
}
|
112
lib/screens/new_generator_screen.dart
Normal file
112
lib/screens/new_generator_screen.dart
Normal file
@ -0,0 +1,112 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/screens/national_id_camera_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class NewGeneratorScreen extends StatefulWidget {
|
||||
const NewGeneratorScreen({super.key});
|
||||
|
||||
@override
|
||||
State<NewGeneratorScreen> createState() => _NewGeneratorScreenState();
|
||||
}
|
||||
|
||||
class _NewGeneratorScreenState extends State<NewGeneratorScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
children: [
|
||||
const SizedBox(height: 150),
|
||||
// SizedBox(
|
||||
// width: 40,
|
||||
// child: SvgPicture.asset(
|
||||
// "assets/svgs/x.svg",
|
||||
// semanticsLabel: 'Cancel',
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 50),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"طلب تخصيص حصة كاز للمولدة",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 50),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"يوفر تطبيق كاز كوم خدمة طلب حصة الكاز للمولدات الاهلية الكترونياً وبسهولة تامة.",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"لتسجيل حساب في كازكوم يرجى ملئ المعلومات المطلوبة والتأكد من صحة ودقة المعلومات.",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 75),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => NationalIdCameraScreen(
|
||||
title: "الهوية الوطنية",
|
||||
subtitle: "الوجه الامامي",
|
||||
description: "ضع البطاقة الموحدة الخاصة بصاحب المولد في المربع وتاكد من وجود اضاءة مناسبة",
|
||||
onScanComplete: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => NationalIdCameraScreen(
|
||||
title: "الهوية الوطنية",
|
||||
subtitle: "الوجه الخلفي",
|
||||
description: "ضع البطاقة الموحدة الخاصة بصاحب المولد في المربع وتاكد من وجود اضاءة مناسبة",
|
||||
onScanComplete: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => NationalIdCameraScreen(
|
||||
title: "بطاقة السكن",
|
||||
subtitle: "",
|
||||
description: "ضع بطاقة السكن الخاصة بصاحب المولد في المربع وتاكد من وجود اضاءة مناسبة",
|
||||
onScanComplete: () {
|
||||
|
||||
}
|
||||
)
|
||||
));
|
||||
}
|
||||
)
|
||||
));
|
||||
},
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
label: "التالي",
|
||||
isElevated: false
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
585
lib/screens/nfc_screen.dart
Normal file
585
lib/screens/nfc_screen.dart
Normal file
@ -0,0 +1,585 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dmrtd/dmrtd.dart';
|
||||
import 'package:dmrtd/src/proto/can_key.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class NfcScreen extends StatefulWidget {
|
||||
const NfcScreen({super.key});
|
||||
|
||||
@override
|
||||
State<NfcScreen> createState() => _NfcScreenState();
|
||||
}
|
||||
|
||||
class _NfcScreenState extends State<NfcScreen> {
|
||||
final NfcProvider _nfc = NfcProvider();
|
||||
MrtdData? _mrtdData = MrtdData();
|
||||
String _alertMessage = "";
|
||||
bool _isReading = false;
|
||||
|
||||
String id = ''; // should change based on user id card (it's the string below the image)
|
||||
DateTime dateOfBirth = DateTime(2000, 1, 1); // should change based on user id card
|
||||
DateTime dateOfExpiry = DateTime(2029, 1, 1); // should change based on user id card
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
try {
|
||||
_buttonPressed();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
appBar: CustomAppBar(),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Text("NFC Screen, Nfc has ti be turned on."),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String formatProgressMsg(String message, int percentProgress) {
|
||||
final p = (percentProgress / 20).round();
|
||||
final full = "🟢 " * p;
|
||||
final empty = "⚪️ " * (5 - p);
|
||||
return "$message\n\n$full$empty";
|
||||
}
|
||||
|
||||
void _readMRTD({required AccessKey accessKey, bool isPace = false}) async {
|
||||
try {
|
||||
setState(() {
|
||||
_mrtdData = null;
|
||||
_alertMessage = "Waiting for Passport tag ...";
|
||||
_isReading = true;
|
||||
});
|
||||
try {
|
||||
bool demo = false;
|
||||
print("-------------- FIRST -------------");
|
||||
if (!demo)
|
||||
await _nfc.connect(
|
||||
iosAlertMessage: "Hold your phone near Biometric Passport");
|
||||
|
||||
print("-------------- SECOND -------------");
|
||||
final passport = Passport(_nfc);
|
||||
|
||||
print("-------------- THIRD -------------");
|
||||
setState(() {
|
||||
_alertMessage = "Reading Passport ...";
|
||||
});
|
||||
|
||||
print("-------------- 4 -------------");
|
||||
_nfc.setIosAlertMessage("Trying to read EF.CardAccess ...");
|
||||
final mrtdData = MrtdData();
|
||||
|
||||
print("-------------- 5 -------------");
|
||||
try {
|
||||
mrtdData.cardAccess = await passport.readEfCardAccess();
|
||||
} on PassportError {
|
||||
print("-------------- 6 -------------");
|
||||
//if (e.code != StatusWord.fileNotFound) rethrow;
|
||||
}
|
||||
|
||||
_nfc.setIosAlertMessage("Trying to read EF.CardSecurity ...");
|
||||
|
||||
try {
|
||||
//mrtdData.cardSecurity = await passport.readEfCardSecurity();
|
||||
} on PassportError {
|
||||
//if (e.code != StatusWord.fileNotFound) rethrow;
|
||||
}
|
||||
|
||||
_nfc.setIosAlertMessage("Initiating session with PACE...");
|
||||
//set MrtdData
|
||||
mrtdData.isPACE = isPace;
|
||||
mrtdData.isDBA = accessKey.PACE_REF_KEY_TAG == 0x01 ? true : false;
|
||||
|
||||
if (isPace) {
|
||||
//PACE session
|
||||
print("-------------- 7 -------------");
|
||||
await passport.startSessionPACE(accessKey, mrtdData.cardAccess!);
|
||||
} else {
|
||||
print("-------------- 8 -------------");
|
||||
//BAC session
|
||||
await passport.startSession(accessKey as DBAKey);
|
||||
}
|
||||
|
||||
print("-------------- 9 -------------");
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.COM ...", 0));
|
||||
mrtdData.com = await passport.readEfCOM();
|
||||
|
||||
_nfc.setIosAlertMessage(
|
||||
formatProgressMsg("Reading Data Groups ...", 20));
|
||||
|
||||
print("-------------- 10 -------------");
|
||||
if (mrtdData.com!.dgTags.contains(EfDG1.TAG)) {
|
||||
mrtdData.dg1 = await passport.readEfDG1();
|
||||
print("DG1 mrz country: ${mrtdData.dg1?.mrz.country}");
|
||||
print("DG1 mrz dateOfBirth: ${mrtdData.dg1?.mrz.dateOfBirth}");
|
||||
print("DG1 mrz dateOfExpiry: ${mrtdData.dg1?.mrz.dateOfExpiry}");
|
||||
print("DG1 mrz documentCode: ${mrtdData.dg1?.mrz.documentCode}");
|
||||
print("DG1 mrz documentNumber: ${mrtdData.dg1?.mrz.documentNumber}");
|
||||
print("DG1 mrz firstName: ${mrtdData.dg1?.mrz.firstName}");
|
||||
print("DG1 mrz gender: ${mrtdData.dg1?.mrz.gender}");
|
||||
print("DG1 mrz lastName: ${mrtdData.dg1?.mrz.lastName}");
|
||||
print("DG1 mrz nationality: ${mrtdData.dg1?.mrz.nationality}");
|
||||
print("DG1 mrz optionalData: ${mrtdData.dg1?.mrz.optionalData}");
|
||||
print("DG1 mrz optionalData2: ${mrtdData.dg1?.mrz.optionalData2}");
|
||||
print("DG1 mrz version: ${mrtdData.dg1?.mrz.version}");
|
||||
}
|
||||
|
||||
print("-------------- 11 -------------");
|
||||
if (mrtdData.com!.dgTags.contains(EfDG2.TAG)) {
|
||||
mrtdData.dg2 = await passport.readEfDG2();
|
||||
print("DG2 deviceType: ${mrtdData.dg2?.deviceType}");
|
||||
print("DG2 expression: ${mrtdData.dg2?.expression}");
|
||||
print("DG2 eyeColor: ${mrtdData.dg2?.eyeColor}");
|
||||
print("DG2 faceImageType: ${mrtdData.dg2?.faceImageType}");
|
||||
print("DG2 facialRecordDataLength: ${mrtdData.dg2?.facialRecordDataLength}");
|
||||
print("DG2 featureMask: ${mrtdData.dg2?.featureMask}");
|
||||
print("DG2 gender: ${mrtdData.dg2?.gender}");
|
||||
print("DG2 hairColor: ${mrtdData.dg2?.hairColor}");
|
||||
print("DG2 imageColorSpace: ${mrtdData.dg2?.imageColorSpace}");
|
||||
print("DG2 imageData: ${mrtdData.dg2?.imageData}");
|
||||
print("DG2 imageHeight: ${mrtdData.dg2?.imageHeight}");
|
||||
print("DG2 imageType: ${mrtdData.dg2?.imageType}");
|
||||
print("DG2 imageWidth: ${mrtdData.dg2?.imageWidth}");
|
||||
print("DG2 lengthOfRecord: ${mrtdData.dg2?.lengthOfRecord}");
|
||||
print("DG2 nrFeaturePoints: ${mrtdData.dg2?.nrFeaturePoints}");
|
||||
print("DG2 numberOfFacialImages: ${mrtdData.dg2?.numberOfFacialImages}");
|
||||
print("DG2 poseAngle: ${mrtdData.dg2?.poseAngle}");
|
||||
print("DG2 poseAngleUncertainty: ${mrtdData.dg2?.poseAngleUncertainty}");
|
||||
print("DG2 quality: ${mrtdData.dg2?.quality}");
|
||||
print("DG2 sourceType: ${mrtdData.dg2?.sourceType}");
|
||||
print("DG2 versionNumber: ${mrtdData.dg2?.versionNumber}");
|
||||
}
|
||||
|
||||
// To read DG3 and DG4 session has to be established with CVCA certificate (not supported).
|
||||
// if(mrtdData.com!.dgTags.contains(EfDG3.TAG)) {
|
||||
// mrtdData.dg3 = await passport.readEfDG3();
|
||||
// }
|
||||
|
||||
// if(mrtdData.com!.dgTags.contains(EfDG4.TAG)) {
|
||||
// mrtdData.dg4 = await passport.readEfDG4();
|
||||
// }
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG5.TAG)) {
|
||||
mrtdData.dg5 = await passport.readEfDG5();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG6.TAG)) {
|
||||
mrtdData.dg6 = await passport.readEfDG6();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG7.TAG)) {
|
||||
mrtdData.dg7 = await passport.readEfDG7();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG8.TAG)) {
|
||||
mrtdData.dg8 = await passport.readEfDG8();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG9.TAG)) {
|
||||
mrtdData.dg9 = await passport.readEfDG9();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG10.TAG)) {
|
||||
mrtdData.dg10 = await passport.readEfDG10();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG11.TAG)) {
|
||||
mrtdData.dg11 = await passport.readEfDG11();
|
||||
print("DG11 custodyInformation: ${mrtdData.dg11?.custodyInformation}");
|
||||
print("DG11 ersonalSummary: ${mrtdData.dg11?.ersonalSummary}");
|
||||
print("DG11 fullDateOfBirth: ${mrtdData.dg11?.fullDateOfBirth}");
|
||||
print("DG11 nameOfHolder: ${mrtdData.dg11?.nameOfHolder}");
|
||||
print("DG11 otherNames: ${mrtdData.dg11?.otherNames}");
|
||||
print("DG11 otherValidTDNumbers: ${mrtdData.dg11?.otherValidTDNumbers}");
|
||||
print("DG11 permanentAddress: ${mrtdData.dg11?.permanentAddress}");
|
||||
print("DG11 personalNumber: ${mrtdData.dg11?.personalNumber}");
|
||||
print("DG11 placeOfBirth: ${mrtdData.dg11?.placeOfBirth}");
|
||||
print("DG11 profession: ${mrtdData.dg11?.profession}");
|
||||
print("DG11 proofOfCitizenship: ${mrtdData.dg11?.proofOfCitizenship}");
|
||||
print("DG11 telephone: ${mrtdData.dg11?.telephone}");
|
||||
print("DG11 title: ${mrtdData.dg11?.title}");
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG12.TAG)) {
|
||||
mrtdData.dg12 = await passport.readEfDG12();
|
||||
print("DG12 dateOfIssue: ${mrtdData.dg12?.dateOfIssue}");
|
||||
print("DG12 dateOfIssue: ${mrtdData.dg12?.issuingAuthority}");
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG13.TAG)) {
|
||||
mrtdData.dg13 = await passport.readEfDG13();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG14.TAG)) {
|
||||
mrtdData.dg14 = await passport.readEfDG14();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG15.TAG)) {
|
||||
mrtdData.dg15 = await passport.readEfDG15();
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Doing AA ...", 60));
|
||||
|
||||
print("DG15 aaPublicKey: ${mrtdData.dg15?.aaPublicKey}");
|
||||
|
||||
mrtdData.aaSig = await passport.activeAuthenticate(Uint8List(8));
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG16.TAG)) {
|
||||
mrtdData.dg16 = await passport.readEfDG16();
|
||||
}
|
||||
|
||||
print("-------------- 12 -------------");
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.SOD ...", 80));
|
||||
mrtdData.sod = await passport.readEfSOD();
|
||||
|
||||
setState(() {
|
||||
_mrtdData = mrtdData;
|
||||
});
|
||||
|
||||
setState(() {
|
||||
_alertMessage = "";
|
||||
});
|
||||
} on Exception catch (e) {
|
||||
final se = e.toString().toLowerCase();
|
||||
String alertMsg = "An error has occurred while reading Passport!";
|
||||
if (e is PassportError) {
|
||||
if (se.contains("security status not satisfied")) {
|
||||
alertMsg =
|
||||
"Failed to initiate session with passport.\nCheck input data!";
|
||||
}
|
||||
print("PassportError: ${e.message}");
|
||||
} else {
|
||||
print(
|
||||
"An exception was encountered while trying to read Passport: $e");
|
||||
}
|
||||
|
||||
if (se.contains('timeout')) {
|
||||
alertMsg = "Timeout while waiting for Passport tag";
|
||||
} else if (se.contains("tag was lost")) {
|
||||
alertMsg = "Tag was lost. Please try again!";
|
||||
} else if (se.contains("invalidated by user")) {
|
||||
alertMsg = "";
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_alertMessage = alertMsg;
|
||||
});
|
||||
} finally {
|
||||
if (_alertMessage.isNotEmpty) {
|
||||
await _nfc.disconnect(iosErrorMessage: _alertMessage);
|
||||
} else {
|
||||
await _nfc.disconnect(
|
||||
iosAlertMessage: formatProgressMsg("Finished", 100));
|
||||
}
|
||||
setState(() {
|
||||
_isReading = false;
|
||||
});
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
print("Read MRTD error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void _readMRTDOld() async {
|
||||
try {
|
||||
setState(() {
|
||||
_mrtdData = null;
|
||||
_alertMessage = "Waiting for Passport tag ...";
|
||||
_isReading = true;
|
||||
});
|
||||
|
||||
await _nfc.connect(
|
||||
iosAlertMessage: "Hold your phone near Biometric Passport");
|
||||
final passport = Passport(_nfc);
|
||||
|
||||
setState(() {
|
||||
_alertMessage = "Reading Passport ...";
|
||||
});
|
||||
|
||||
_nfc.setIosAlertMessage("Trying to read EF.CardAccess ...");
|
||||
final mrtdData = MrtdData();
|
||||
|
||||
try {
|
||||
mrtdData.cardAccess = await passport.readEfCardAccess();
|
||||
} on PassportError {
|
||||
//if (e.code != StatusWord.fileNotFound) rethrow;
|
||||
}
|
||||
|
||||
_nfc.setIosAlertMessage("Trying to read EF.CardSecurity ...");
|
||||
|
||||
try {
|
||||
mrtdData.cardSecurity = await passport.readEfCardSecurity();
|
||||
} on PassportError {
|
||||
//if (e.code != StatusWord.fileNotFound) rethrow;
|
||||
}
|
||||
|
||||
_nfc.setIosAlertMessage("Initiating session ...");
|
||||
final bacKeySeed = DBAKey(id, dateOfBirth, dateOfExpiry);
|
||||
await passport.startSession(bacKeySeed);
|
||||
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.COM ...", 0));
|
||||
mrtdData.com = await passport.readEfCOM();
|
||||
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Reading Data Groups ...", 20));
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG1.TAG)) {
|
||||
mrtdData.dg1 = await passport.readEfDG1();
|
||||
print(mrtdData.dg1?.mrz);
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG2.TAG)) {
|
||||
mrtdData.dg2 = await passport.readEfDG2();
|
||||
print("DG2 deviceType: ${mrtdData.dg2?.deviceType}");
|
||||
print("DG2 expression: ${mrtdData.dg2?.expression}");
|
||||
print("DG2 eyeColor: ${mrtdData.dg2?.eyeColor}");
|
||||
print("DG2 faceImageType: ${mrtdData.dg2?.faceImageType}");
|
||||
print("DG2 facialRecordDataLength: ${mrtdData.dg2?.facialRecordDataLength}");
|
||||
print("DG2 featureMask: ${mrtdData.dg2?.featureMask}");
|
||||
print("DG2 fid: ${mrtdData.dg2?.fid}");
|
||||
print("DG2 gender: ${mrtdData.dg2?.gender}");
|
||||
print("DG2 hairColor: ${mrtdData.dg2?.hairColor}");
|
||||
print("DG2 imageColorSpace: ${mrtdData.dg2?.imageColorSpace}");
|
||||
print("DG2 imageData: ${mrtdData.dg2?.imageData}");
|
||||
print("DG2 imageHeight: ${mrtdData.dg2?.imageHeight}");
|
||||
print("DG2 imageType: ${mrtdData.dg2?.imageType}");
|
||||
print("DG2 imageWidth: ${mrtdData.dg2?.imageWidth}");
|
||||
print("DG2 nrFeaturePoints: ${mrtdData.dg2?.nrFeaturePoints}");
|
||||
print("DG2 numberOfFacialImages: ${mrtdData.dg2?.numberOfFacialImages}");
|
||||
print("DG2 lengthOfRecord: ${mrtdData.dg2?.lengthOfRecord}");
|
||||
print("DG2 poseAngle: ${mrtdData.dg2?.poseAngle}");
|
||||
print("DG2 poseAngleUncertainty: ${mrtdData.dg2?.poseAngleUncertainty}");
|
||||
print("DG2 quality: ${mrtdData.dg2?.quality}");
|
||||
print("DG2 sourceType: ${mrtdData.dg2?.sourceType}");
|
||||
print("DG2 versionNumber: ${mrtdData.dg2?.versionNumber}");
|
||||
print("DG2 tag: ${mrtdData.dg2?.tag}");
|
||||
print("DG2 sfi: ${mrtdData.dg2?.sfi}");
|
||||
}
|
||||
|
||||
// To read DG3 and DG4 session has to be established with CVCA certificate (not supported).
|
||||
// if(mrtdData.com!.dgTags.contains(EfDG3.TAG)) {
|
||||
// mrtdData.dg3 = await passport.readEfDG3();
|
||||
// }
|
||||
|
||||
// if(mrtdData.com!.dgTags.contains(EfDG4.TAG)) {
|
||||
// mrtdData.dg4 = await passport.readEfDG4();
|
||||
// }
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG5.TAG)) {
|
||||
mrtdData.dg5 = await passport.readEfDG5();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG6.TAG)) {
|
||||
mrtdData.dg6 = await passport.readEfDG6();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG7.TAG)) {
|
||||
mrtdData.dg7 = await passport.readEfDG7();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG8.TAG)) {
|
||||
mrtdData.dg8 = await passport.readEfDG8();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG9.TAG)) {
|
||||
mrtdData.dg9 = await passport.readEfDG9();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG10.TAG)) {
|
||||
mrtdData.dg10 = await passport.readEfDG10();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG11.TAG)) {
|
||||
mrtdData.dg11 = await passport.readEfDG11();
|
||||
print("DG11 custodyInformation: ${mrtdData.dg11?.custodyInformation}");
|
||||
print("DG11 ersonalSummary: ${mrtdData.dg11?.ersonalSummary}");
|
||||
print("DG11 fullDateOfBirth: ${mrtdData.dg11?.fullDateOfBirth}");
|
||||
print("DG11 nameOfHolder: ${mrtdData.dg11?.nameOfHolder}");
|
||||
print("DG11 otherNames: ${mrtdData.dg11?.otherNames}");
|
||||
print("DG11 otherValidTDNumbers: ${mrtdData.dg11?.otherValidTDNumbers}");
|
||||
print("DG11 permanentAddress: ${mrtdData.dg11?.permanentAddress}");
|
||||
print("DG11 personalNumber: ${mrtdData.dg11?.personalNumber}");
|
||||
print("DG11 placeOfBirth: ${mrtdData.dg11?.placeOfBirth}");
|
||||
print("DG11 profession: ${mrtdData.dg11?.profession}");
|
||||
print("DG11 proofOfCitizenship: ${mrtdData.dg11?.proofOfCitizenship}");
|
||||
print("DG11 telephone: ${mrtdData.dg11?.telephone}");
|
||||
print("DG11 title: ${mrtdData.dg11?.title}");
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG12.TAG)) {
|
||||
mrtdData.dg12 = await passport.readEfDG12();
|
||||
print("DG12 dateOfIssue: ${mrtdData.dg12?.dateOfIssue}");
|
||||
print("DG12 issuingAuthority: ${mrtdData.dg12?.issuingAuthority}");
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG13.TAG)) {
|
||||
mrtdData.dg13 = await passport.readEfDG13();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG14.TAG)) {
|
||||
mrtdData.dg14 = await passport.readEfDG14();
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG15.TAG)) {
|
||||
mrtdData.dg15 = await passport.readEfDG15();
|
||||
print("DG12 issuingAuthority: ${mrtdData.dg15?.aaPublicKey}");
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Doing AA ...", 60));
|
||||
mrtdData.aaSig = await passport.activeAuthenticate(Uint8List(8));
|
||||
}
|
||||
|
||||
if (mrtdData.com!.dgTags.contains(EfDG16.TAG)) {
|
||||
mrtdData.dg16 = await passport.readEfDG16();
|
||||
}
|
||||
|
||||
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.SOD ...", 80));
|
||||
mrtdData.sod = await passport.readEfSOD();
|
||||
|
||||
setState(() {
|
||||
_mrtdData = mrtdData;
|
||||
});
|
||||
|
||||
setState(() {
|
||||
_alertMessage = "";
|
||||
});
|
||||
} on Exception catch (e) {
|
||||
final se = e.toString().toLowerCase();
|
||||
String alertMsg = "An error has occurred while reading Passport!";
|
||||
if (e is PassportError) {
|
||||
if (se.contains("security status not satisfied")) {
|
||||
alertMsg =
|
||||
"Failed to initiate session with passport.\nCheck input data!";
|
||||
}
|
||||
print("PassportError: ${e.message}");
|
||||
} else {
|
||||
print("An exception was encountered while trying to read Passport: $e");
|
||||
}
|
||||
|
||||
if (se.contains('timeout')) {
|
||||
alertMsg = "Timeout while waiting for Passport tag";
|
||||
} else if (se.contains("tag was lost")) {
|
||||
alertMsg = "Tag was lost. Please try again!";
|
||||
} else if (se.contains("invalidated by user")) {
|
||||
alertMsg = "";
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_alertMessage = alertMsg;
|
||||
});
|
||||
} finally {
|
||||
if (_alertMessage.isNotEmpty) {
|
||||
await _nfc.disconnect(iosErrorMessage: _alertMessage);
|
||||
} else {
|
||||
await _nfc.disconnect(
|
||||
iosAlertMessage: formatProgressMsg("Finished", 100));
|
||||
}
|
||||
setState(() {
|
||||
_isReading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _buttonPressed() async {
|
||||
String _can = "";
|
||||
print("Button pressed");
|
||||
//Check on what tab we are
|
||||
if (true) {
|
||||
//DBA tab
|
||||
// String errorText = "";
|
||||
// if (_doe.text.isEmpty) {
|
||||
// errorText += "Please enter date of expiry!\n";
|
||||
// }
|
||||
// if (_dob.text.isEmpty) {
|
||||
// errorText += "Please enter date of birth!\n";
|
||||
// }
|
||||
// if (_docNumber.text.isEmpty) {
|
||||
// errorText += "Please enter passport number!";
|
||||
// }
|
||||
|
||||
// setState(() {
|
||||
// _alertMessage = errorText;
|
||||
// });
|
||||
// //If there is an error, just jump out of the function
|
||||
// if (errorText.isNotEmpty) return;
|
||||
|
||||
final bacKeySeed = DBAKey(id, dateOfBirth, dateOfExpiry, paceMode: true);
|
||||
_readMRTD(accessKey: bacKeySeed, isPace: true);
|
||||
} else {
|
||||
// PACE tab
|
||||
// String errorText = "";
|
||||
// if (_can.isEmpty) {
|
||||
// errorText = "Please enter CAN number!";
|
||||
// } else if (_can.text.length != 6) {
|
||||
// errorText = "CAN number must be exactly 6 digits long!";
|
||||
// }
|
||||
|
||||
// setState(() {
|
||||
// _alertMessage = errorText;
|
||||
// });
|
||||
// //If there is an error, just jump out of the function
|
||||
// if (errorText.isNotEmpty) return;
|
||||
|
||||
// final canKeySeed = CanKey(_can.text);
|
||||
final canKeySeed = CanKey(id);
|
||||
_readMRTD(accessKey: canKeySeed, isPace: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MrtdData {
|
||||
EfCardAccess? cardAccess;
|
||||
EfCardSecurity? cardSecurity;
|
||||
EfCOM? com;
|
||||
EfSOD? sod;
|
||||
EfDG1? dg1;
|
||||
EfDG2? dg2;
|
||||
EfDG3? dg3;
|
||||
EfDG4? dg4;
|
||||
EfDG5? dg5;
|
||||
EfDG6? dg6;
|
||||
EfDG7? dg7;
|
||||
EfDG8? dg8;
|
||||
EfDG9? dg9;
|
||||
EfDG10? dg10;
|
||||
EfDG11? dg11;
|
||||
EfDG12? dg12;
|
||||
EfDG13? dg13;
|
||||
EfDG14? dg14;
|
||||
EfDG15? dg15;
|
||||
EfDG16? dg16;
|
||||
Uint8List? aaSig;
|
||||
bool? isPACE;
|
||||
bool? isDBA;
|
||||
}
|
||||
|
||||
final Map<DgTag, String> dgTagToString = {
|
||||
EfDG1.TAG: 'EF.DG1',
|
||||
EfDG2.TAG: 'EF.DG2',
|
||||
EfDG3.TAG: 'EF.DG3',
|
||||
EfDG4.TAG: 'EF.DG4',
|
||||
EfDG5.TAG: 'EF.DG5',
|
||||
EfDG6.TAG: 'EF.DG6',
|
||||
EfDG7.TAG: 'EF.DG7',
|
||||
EfDG8.TAG: 'EF.DG8',
|
||||
EfDG9.TAG: 'EF.DG9',
|
||||
EfDG10.TAG: 'EF.DG10',
|
||||
EfDG11.TAG: 'EF.DG11',
|
||||
EfDG12.TAG: 'EF.DG12',
|
||||
EfDG13.TAG: 'EF.DG13',
|
||||
EfDG14.TAG: 'EF.DG14',
|
||||
EfDG15.TAG: 'EF.DG15',
|
||||
EfDG16.TAG: 'EF.DG16'
|
||||
};
|
57
lib/screens/notifications_screen.dart
Normal file
57
lib/screens/notifications_screen.dart
Normal file
@ -0,0 +1,57 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class NotificationsScreen extends StatelessWidget {
|
||||
const NotificationsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(
|
||||
title: "الإشعارات",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: 5,
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||
itemBuilder: (context, index) => InkWell(
|
||||
child: Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: ListTile(
|
||||
title: AutoSizeText(
|
||||
"تم تحديث حالة الطلب",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"تم تحديث حالة الطلب الخاص بك الى قيد المراجعة",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
AutoSizeText(
|
||||
"2024/12/12",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
400
lib/screens/order_details_screen.dart
Normal file
400
lib/screens/order_details_screen.dart
Normal file
@ -0,0 +1,400 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/face_detection_screen.dart';
|
||||
import 'package:gascom/screens/payment_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class OrderDetailsScreen extends StatefulWidget {
|
||||
const OrderDetailsScreen({super.key});
|
||||
|
||||
@override
|
||||
State<OrderDetailsScreen> createState() => _OrderDetailsScreenState();
|
||||
}
|
||||
|
||||
class _OrderDetailsScreenState extends State<OrderDetailsScreen> {
|
||||
|
||||
int activeStep = 3;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "رقم الطلب: 67895435",
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
AutoSizeText(
|
||||
"حالة الطلب",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 15),
|
||||
activeStep > 0 ? const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 26,
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
) : const SizedBox(width: 40,),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"مراجعة الطلب من قبل لجنة الطاقة",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: activeStep == 0
|
||||
? AppTheme.textColor
|
||||
: activeStep > 0
|
||||
? AppTheme.yellowColor
|
||||
: AppTheme.textColor.withOpacity(0.6)
|
||||
),
|
||||
),
|
||||
if (activeStep > 0) ...[
|
||||
const SizedBox(height: 5),
|
||||
AutoSizeText(
|
||||
"2024/02/06",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 40),
|
||||
VerticalDivider(
|
||||
color: activeStep > 0 ? AppTheme.textColor : AppTheme.textColor.withOpacity(0.6),
|
||||
thickness: 1,
|
||||
width: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 15),
|
||||
activeStep > 1 ? const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 26,
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
) : const SizedBox(width: 40,),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"مراجعة الطلب من قبل لجنة الطاقة",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: activeStep == 1
|
||||
? AppTheme.textColor
|
||||
: activeStep > 1
|
||||
? AppTheme.yellowColor
|
||||
: AppTheme.textColor.withOpacity(0.6)
|
||||
),
|
||||
),
|
||||
if (activeStep > 1) ...[
|
||||
const SizedBox(height: 5),
|
||||
AutoSizeText(
|
||||
"2024/02/06",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 40),
|
||||
VerticalDivider(
|
||||
color: activeStep > 1 ? AppTheme.textColor : AppTheme.textColor.withOpacity(0.6),
|
||||
thickness: 1,
|
||||
width: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 15),
|
||||
activeStep > 2 ? const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 26,
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
) : const SizedBox(width: 40,),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"مراجعة الطلب من قبل لجنة الطاقة",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: activeStep == 2
|
||||
? AppTheme.textColor
|
||||
: activeStep > 2
|
||||
? AppTheme.yellowColor
|
||||
: AppTheme.textColor.withOpacity(0.6)
|
||||
),
|
||||
),
|
||||
if (activeStep > 2) ...[
|
||||
const SizedBox(height: 5),
|
||||
AutoSizeText(
|
||||
"2024/02/06",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 40),
|
||||
VerticalDivider(
|
||||
color: activeStep > 2 ? AppTheme.textColor : AppTheme.textColor.withOpacity(0.6),
|
||||
thickness: 1,
|
||||
width: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 15),
|
||||
activeStep > 3 ? const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 26,
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
) : const SizedBox(width: 40,),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"الدفع",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: activeStep == 3
|
||||
? AppTheme.textColor
|
||||
: activeStep > 3
|
||||
? AppTheme.yellowColor
|
||||
: AppTheme.textColor.withOpacity(0.6)
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (activeStep > 3) ...[
|
||||
AutoSizeText(
|
||||
"2024/02/06",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
if (activeStep >= 3) ...[
|
||||
AutoSizeText(
|
||||
"مبلغ الحصة",
|
||||
minFontSize: 10,
|
||||
textAlign: TextAlign.start,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: activeStep == 3
|
||||
? AppTheme.textColor
|
||||
: AppTheme.yellowColor
|
||||
),
|
||||
),
|
||||
AutoSizeText(
|
||||
"5000 لتر / كاز اويل",
|
||||
minFontSize: 10,
|
||||
textAlign: TextAlign.start,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: activeStep == 3
|
||||
? AppTheme.textColor
|
||||
: AppTheme.yellowColor
|
||||
),
|
||||
),
|
||||
AutoSizeText(
|
||||
"5,000,000 د.ع",
|
||||
minFontSize: 10,
|
||||
textAlign: TextAlign.start,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: activeStep == 3
|
||||
? AppTheme.textColor
|
||||
: AppTheme.yellowColor
|
||||
),
|
||||
),
|
||||
],
|
||||
if (activeStep == 3) ...[
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, PaymentScreen(
|
||||
title: "دفع مبلغ الحصة",
|
||||
onPaymentComplete: () {
|
||||
pushScreenWithoutNavBar(context, FaceDetectionScreen());
|
||||
},
|
||||
));
|
||||
},
|
||||
label: "ادفع",
|
||||
isElevated: true
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 40),
|
||||
VerticalDivider(
|
||||
color: activeStep > 3 ? AppTheme.textColor : AppTheme.textColor.withOpacity(0.6),
|
||||
thickness: 1,
|
||||
width: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 15),
|
||||
activeStep > 4 ? const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 26,
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
) : const SizedBox(width: 40,),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"الاستلام",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: activeStep == 4
|
||||
? AppTheme.textColor
|
||||
: activeStep > 4
|
||||
? AppTheme.yellowColor
|
||||
: AppTheme.textColor.withOpacity(0.6)
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
if (activeStep == 4) AutoSizeText(
|
||||
"الطلب قيد التسليم",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: activeStep == 4
|
||||
? AppTheme.textColor
|
||||
: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
if (activeStep > 4) ...[
|
||||
AutoSizeText(
|
||||
"2024/02/13",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: activeStep == 4
|
||||
? AppTheme.textColor
|
||||
: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
AutoSizeText(
|
||||
"تم الاستلام",
|
||||
minFontSize: 10,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: activeStep == 4
|
||||
? AppTheme.textColor
|
||||
: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 5),
|
||||
if (activeStep == 4) ...[
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: AppButton(
|
||||
onPressed: () {},
|
||||
label: "تتبع الطلب",
|
||||
isElevated: true
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
88
lib/screens/otp_provider_screen.dart
Normal file
88
lib/screens/otp_provider_screen.dart
Normal file
@ -0,0 +1,88 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gascom/screens/otp_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
|
||||
class OtpProviderScreen extends StatelessWidget {
|
||||
const OtpProviderScreen({
|
||||
super.key,
|
||||
required this.cardNumber,
|
||||
});
|
||||
|
||||
final int cardNumber;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
children: [
|
||||
const SizedBox(height: 150),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.6,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svgs/logo.svg",
|
||||
semanticsLabel: 'Home',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 50),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"طريقة تاكيد الهوية",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"اختر طريقة ارسال رمز OTP لتسجيل الدخول",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 35),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
// TODO: Navigate to OTP Screen
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const OtpScreen(isSms: true,)));
|
||||
},
|
||||
label: "SMS",
|
||||
isElevated: false
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
// TODO: Navigate to OTP Screen
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const OtpScreen(isSms: false,)));
|
||||
},
|
||||
label: "Whatsapp",
|
||||
isElevated: false
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
77
lib/screens/otp_screen.dart
Normal file
77
lib/screens/otp_screen.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/widgets/bottom_nav.dart';
|
||||
import 'package:pinput/pinput.dart';
|
||||
|
||||
class OtpScreen extends StatelessWidget {
|
||||
const OtpScreen({
|
||||
super.key,
|
||||
required this.isSms,
|
||||
});
|
||||
|
||||
final bool isSms;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
children: [
|
||||
const SizedBox(height: 150),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.6,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svgs/logo.svg",
|
||||
semanticsLabel: 'Home',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
Center(
|
||||
child: AutoSizeText(
|
||||
"يرجى ادخال رمز OTP",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 35),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 6),
|
||||
decoration: BoxDecoration(
|
||||
// add a rounded border radius to the container
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
border: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Pinput(
|
||||
length: 6,
|
||||
defaultPinTheme: PinTheme(
|
||||
width: 30,
|
||||
height: 35,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 0, color: Colors.transparent),
|
||||
),
|
||||
),
|
||||
onCompleted: (value) {
|
||||
Navigator.pushAndRemoveUntil(context,
|
||||
MaterialPageRoute(builder: (context) => const BottomNav()), (_) => false);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
136
lib/screens/pay_monthly_gas.dart
Normal file
136
lib/screens/pay_monthly_gas.dart
Normal file
@ -0,0 +1,136 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/payment_screen.dart';
|
||||
import 'package:gascom/screens/success_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class PayMonthlyGas extends StatelessWidget {
|
||||
const PayMonthlyGas({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: "دفع رسوم تقديم الطلب",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"طلب حصة كاز اويل ",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"حجم الحصة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5,),
|
||||
AutoSizeText(
|
||||
"1000 لتر",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"تاريخ الطلب",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5,),
|
||||
AutoSizeText(
|
||||
"03/08/2024",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"رسوم تقديم الطلب",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5,),
|
||||
AutoSizeText(
|
||||
"25,000 د.ع",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.5,
|
||||
child: const Divider(
|
||||
color: AppTheme.yellowColor,
|
||||
thickness: 1,
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"دفع رسوم تقديم الطلب",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5,),
|
||||
AutoSizeText(
|
||||
"25,000 د.ع",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 50),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context, PaymentScreen(
|
||||
title: "",
|
||||
onPaymentComplete: () {
|
||||
pushScreenWithoutNavBar(context, const SuccessScreen(title: "تم تقديم الطلب بنجاح", subtitle: "يمكنك متابعة اجراءات المراجعة والتدقيق من خلال نافذة تتبع الطلبات"));
|
||||
},
|
||||
));
|
||||
},
|
||||
label: "ادفع",
|
||||
isElevated: true,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
252
lib/screens/payment_screen.dart
Normal file
252
lib/screens/payment_screen.dart
Normal file
@ -0,0 +1,252 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/utils/text_field_4_space_formatter.dart';
|
||||
import 'package:gascom/utils/text_field_date_formatter.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/app_text_field.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class PaymentScreen extends StatefulWidget {
|
||||
const PaymentScreen({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.onPaymentComplete,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final void Function() onPaymentComplete;
|
||||
|
||||
@override
|
||||
State<PaymentScreen> createState() => _PaymentScreenState();
|
||||
}
|
||||
|
||||
class _PaymentScreenState extends State<PaymentScreen> {
|
||||
TextEditingController nameController = TextEditingController();
|
||||
TextEditingController cardNumberController = TextEditingController();
|
||||
TextEditingController expireController = TextEditingController();
|
||||
TextEditingController cvvController = TextEditingController();
|
||||
|
||||
bool checkBoxValue = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
AutoSizeText(
|
||||
widget.title,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
padding: const EdgeInsets.all(15),
|
||||
// width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.textColor,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"5,000,000 د.ع",
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.blackColor),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
AutoSizeText(
|
||||
"مبلغ حصة كاز اويل",
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: AppTheme.blackColor,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 1,
|
||||
height: 30,
|
||||
),
|
||||
AutoSizeText(
|
||||
"الاسم على البطاقة",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: AppTheme.blackColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
AppTextField(
|
||||
fillColor: Color(0xFFCBCBCB),
|
||||
isFilled: true,
|
||||
controller: nameController,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
AutoSizeText(
|
||||
"رقم البطاقة",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: AppTheme.blackColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: AppTextField(
|
||||
fillColor: Color(0xFFCBCBCB),
|
||||
isFilled: true,
|
||||
controller: cardNumberController,
|
||||
keyboardType: TextInputType.number,
|
||||
isCentered: true,
|
||||
maxCharacters: 19,
|
||||
inputFormatters: [
|
||||
TextField4SpaceFormatter(),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
height: 80,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"انتهاء الصلاحية",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: AppTheme.blackColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: AppTextField(
|
||||
fillColor: Color(0xFFCBCBCB),
|
||||
isFilled: true,
|
||||
controller: expireController,
|
||||
keyboardType: TextInputType.number,
|
||||
isCentered: true,
|
||||
maxCharacters: 5,
|
||||
inputFormatters: [
|
||||
TextFieldDateFormatter(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
"رقم CVV",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: AppTheme.blackColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: AppTextField(
|
||||
fillColor: Color(0xFFCBCBCB),
|
||||
isFilled: true,
|
||||
controller: cvvController,
|
||||
keyboardType: TextInputType.number,
|
||||
isCentered: true,
|
||||
maxCharacters: 3,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(6),
|
||||
width: 12,
|
||||
height: 12,
|
||||
child: Checkbox(
|
||||
value: checkBoxValue,
|
||||
onChanged: handleCheckBoxChange,
|
||||
checkColor: Colors.grey,
|
||||
activeColor: Colors.grey,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
side: const BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
AutoSizeText(
|
||||
"اوافق على القوانين والاحكام",
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: AppTheme.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Row(children: [
|
||||
Expanded(
|
||||
child: AppButton(
|
||||
onPressed: widget.onPaymentComplete,
|
||||
label: "تاكيد",
|
||||
isElevated: true,
|
||||
color: AppTheme.yellowColor,
|
||||
),
|
||||
),
|
||||
]),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
void handleCheckBoxChange(bool? value) {
|
||||
setState(() {
|
||||
checkBoxValue = value!;
|
||||
});
|
||||
}
|
||||
}
|
68
lib/screens/service_fees_screen.dart
Normal file
68
lib/screens/service_fees_screen.dart
Normal file
@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/widgets/custom_app_bar.dart';
|
||||
|
||||
class ServiceFeesScreen extends StatelessWidget {
|
||||
const ServiceFeesScreen({super.key});
|
||||
|
||||
final List<Map<String, String>> fees = const [
|
||||
{
|
||||
"service": "رسوم طلب حصة كاز اويل",
|
||||
"price": "25,000 د.ع",
|
||||
},
|
||||
{
|
||||
"service": "رسوم توصيل الحصة",
|
||||
"price": "60,000 د.ع",
|
||||
},
|
||||
{
|
||||
"service": "سعر 1000 لتر كاز اويل",
|
||||
"price": "400,000 د.ع",
|
||||
},
|
||||
{
|
||||
"service": "رسوم تحويل موقع",
|
||||
"price": "مجاناً",
|
||||
},
|
||||
{
|
||||
"service": "رسوم تبديل محرك",
|
||||
"price": "مجاناً",
|
||||
},
|
||||
{
|
||||
"service": "رسوم تجديد دفتر",
|
||||
"price": "مجاناً",
|
||||
},
|
||||
{
|
||||
"service": "رسوم تغيير مخول",
|
||||
"price": "مجاناً",
|
||||
},
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: " رسوم خدمات كازكم",
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView.separated(
|
||||
itemCount: fees.length,
|
||||
padding: const EdgeInsets.all(20),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||
itemBuilder: (context, index) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
fees[index]["service"]!,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
fees[index]["price"]!,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
68
lib/screens/success_screen.dart
Normal file
68
lib/screens/success_screen.dart
Normal file
@ -0,0 +1,68 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:gascom/widgets/bottom_nav.dart';
|
||||
|
||||
class SuccessScreen extends StatelessWidget {
|
||||
const SuccessScreen({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String subtitle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.check_rounded,
|
||||
size: 100.0,
|
||||
color: Theme.of(context).textTheme.bodyLarge?.color,
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
AutoSizeText(
|
||||
title,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
AutoSizeText(
|
||||
subtitle,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 40.0),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const BottomNav()),
|
||||
(_) => false
|
||||
);
|
||||
},
|
||||
label: "تم",
|
||||
isElevated: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
23
lib/utils/text_field_4_space_formatter.dart
Normal file
23
lib/utils/text_field_4_space_formatter.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class TextField4SpaceFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
final text = newValue.text.replaceAll(' ', '');
|
||||
final buffer = StringBuffer();
|
||||
for (int i = 0; i < text.length; i++) {
|
||||
buffer.write(text[i]);
|
||||
if ((i + 1) % 4 == 0 && i + 1 != text.length) {
|
||||
buffer.write(' ');
|
||||
}
|
||||
}
|
||||
final formattedText = buffer.toString();
|
||||
return newValue.copyWith(
|
||||
text: formattedText,
|
||||
selection: TextSelection.collapsed(offset: formattedText.length),
|
||||
);
|
||||
}
|
||||
}
|
23
lib/utils/text_field_date_formatter.dart
Normal file
23
lib/utils/text_field_date_formatter.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class TextFieldDateFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
final text = newValue.text.replaceAll('/', '');
|
||||
final buffer = StringBuffer();
|
||||
for (int i = 0; i < text.length; i++) {
|
||||
buffer.write(text[i]);
|
||||
if (i == 1 && i + 1 != text.length) {
|
||||
buffer.write('/');
|
||||
}
|
||||
}
|
||||
final formattedText = buffer.toString();
|
||||
return newValue.copyWith(
|
||||
text: formattedText,
|
||||
selection: TextSelection.collapsed(offset: formattedText.length),
|
||||
);
|
||||
}
|
||||
}
|
100
lib/widgets/app_button.dart
Normal file
100
lib/widgets/app_button.dart
Normal file
@ -0,0 +1,100 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
|
||||
class AppButton extends StatelessWidget {
|
||||
const AppButton({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.label,
|
||||
required this.isElevated,
|
||||
this.isLoading = false,
|
||||
this.textStyle,
|
||||
this.color,
|
||||
});
|
||||
|
||||
final void Function() onPressed;
|
||||
final String label;
|
||||
final bool isLoading;
|
||||
final bool isElevated;
|
||||
final TextStyle? textStyle;
|
||||
final Color? color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return isElevated
|
||||
? ElevatedButton(
|
||||
onPressed: isLoading ? null : onPressed,
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
backgroundColor: WidgetStatePropertyAll(color ?? AppTheme.textColor),
|
||||
),
|
||||
child: isLoading
|
||||
? const Spinner(color: AppTheme.primaryColor)
|
||||
: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
label,
|
||||
minFontSize: 8,
|
||||
maxFontSize: 14,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: AppTheme.primaryColor,
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: OutlinedButton(
|
||||
onPressed: isLoading ? null : onPressed,
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
backgroundColor: WidgetStatePropertyAll(Theme.of(context).scaffoldBackgroundColor),
|
||||
side: WidgetStatePropertyAll(BorderSide(
|
||||
width: 1.5,
|
||||
color: color ?? AppTheme.textColor,
|
||||
))
|
||||
),
|
||||
child: isLoading
|
||||
? Spinner(color: color ?? AppTheme.textColor,)
|
||||
: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
label,
|
||||
minFontSize: 8,
|
||||
maxFontSize: 14,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: AppTheme.textColor,
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Spinner extends StatelessWidget {
|
||||
const Spinner({
|
||||
super.key,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
final Color? color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
color: color ?? Colors.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
77
lib/widgets/app_text_field.dart
Normal file
77
lib/widgets/app_text_field.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
|
||||
class AppTextField extends StatelessWidget {
|
||||
const AppTextField(
|
||||
{super.key,
|
||||
required this.controller,
|
||||
required this.keyboardType,
|
||||
this.onChanged,
|
||||
this.errorText,
|
||||
this.fillColor,
|
||||
this.textColor,
|
||||
this.isFilled,
|
||||
this.isCentered,
|
||||
this.maxCharacters,
|
||||
this.inputFormatters,});
|
||||
|
||||
final TextEditingController controller;
|
||||
final TextInputType keyboardType;
|
||||
final void Function(String)? onChanged;
|
||||
final String? errorText;
|
||||
final Color? fillColor;
|
||||
final Color? textColor;
|
||||
final bool? isFilled;
|
||||
final bool? isCentered;
|
||||
final int? maxCharacters;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
keyboardType: keyboardType,
|
||||
onChanged: onChanged,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: textColor ?? const Color.fromARGB(255, 20, 20, 20),
|
||||
),
|
||||
textAlign: isCentered ?? false ? TextAlign.center : TextAlign.start,
|
||||
autocorrect: false,
|
||||
inputFormatters: inputFormatters,
|
||||
cursorColor: textColor ?? const Color.fromARGB(255, 20, 20, 20),
|
||||
cursorRadius: const Radius.circular(10),
|
||||
maxLength: maxCharacters,
|
||||
decoration: InputDecoration(
|
||||
counterText: "",
|
||||
fillColor: fillColor ?? const Color.fromARGB(255, 20, 20, 20),
|
||||
filled: isFilled ?? false,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
borderSide:
|
||||
const BorderSide(color: AppTheme.textColor, width: 1)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
borderSide:
|
||||
const BorderSide(color: AppTheme.textColor, width: 1)),
|
||||
errorText: errorText,
|
||||
errorStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: AppTheme.redColor),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderSide: const BorderSide(color: AppTheme.redColor, width: 1)),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderSide: const BorderSide(color: AppTheme.redColor, width: 1)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
104
lib/widgets/bottom_nav.dart
Normal file
104
lib/widgets/bottom_nav.dart
Normal file
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/follow_order_screen.dart';
|
||||
import 'package:gascom/screens/generator_info_screen.dart';
|
||||
import 'package:gascom/screens/home_screen.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class BottomNav extends StatelessWidget {
|
||||
const BottomNav({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PersistentTabView(
|
||||
controller: PersistentTabController(initialIndex: 0),
|
||||
resizeToAvoidBottomInset: false,
|
||||
navBarHeight: 70,
|
||||
tabs: [
|
||||
tabConfig(
|
||||
context,
|
||||
const HomeScreen(),
|
||||
"home",
|
||||
"assets/svgs/home_filled.svg",
|
||||
"assets/svgs/home.svg",
|
||||
"Home Active",
|
||||
"Home Inactive",
|
||||
"الرئيسية",
|
||||
),
|
||||
tabConfig(
|
||||
context,
|
||||
const GeneratorInfoScreen(),
|
||||
"generator_info",
|
||||
"assets/svgs/generator_filled.svg",
|
||||
"assets/svgs/generator.svg",
|
||||
"Generator Info Active",
|
||||
"Generator Info Inactive",
|
||||
"معلومات المولدة",
|
||||
),
|
||||
tabConfig(
|
||||
context,
|
||||
const FollowOrderScreen(),
|
||||
"follow_order",
|
||||
"assets/svgs/van_filled.svg",
|
||||
"assets/svgs/van.svg",
|
||||
"Follow Order Active",
|
||||
"Follow Order Inactive",
|
||||
"تتبع الطلب",
|
||||
),
|
||||
],
|
||||
navBarBuilder: (navBarConfig) => Style6BottomNavBar(
|
||||
navBarConfig: navBarConfig,
|
||||
navBarDecoration: NavBarDecoration(
|
||||
color: AppTheme.secondaryColor,
|
||||
padding: EdgeInsets.all(10),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.15),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 1,
|
||||
offset: const Offset(0, 0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PersistentTabConfig tabConfig(
|
||||
BuildContext context,
|
||||
Widget screen,
|
||||
String initialRoute,
|
||||
String activeIcon,
|
||||
String inactiveIcon,
|
||||
String activeSemanticsLabel,
|
||||
String inactiveSemanticsLabel,
|
||||
String title,
|
||||
) {
|
||||
return PersistentTabConfig(
|
||||
screen: screen,
|
||||
navigatorConfig: NavigatorConfig(
|
||||
initialRoute: initialRoute,
|
||||
),
|
||||
item: ItemConfig(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(top: 4, left: 4, right: 4),
|
||||
child: SvgPicture.asset(
|
||||
activeIcon,
|
||||
semanticsLabel: activeSemanticsLabel,
|
||||
),
|
||||
),
|
||||
inactiveIcon: SvgPicture.asset(
|
||||
inactiveIcon,
|
||||
semanticsLabel: inactiveSemanticsLabel,
|
||||
),
|
||||
activeForegroundColor: AppTheme.primaryColor,
|
||||
inactiveForegroundColor: AppTheme.primaryColor,
|
||||
textStyle: Theme.of(context).textTheme.bodySmall ?? TextStyle(),
|
||||
title: title,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
40
lib/widgets/custom_app_bar.dart
Normal file
40
lib/widgets/custom_app_bar.dart
Normal file
@ -0,0 +1,40 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
|
||||
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const CustomAppBar({super.key, this.title});
|
||||
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(80);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
toolbarHeight: 80,
|
||||
titleSpacing: 0,
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
surfaceTintColor: AppTheme.primaryColor,
|
||||
title: Text(
|
||||
title ?? "",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: const Icon(CupertinoIcons.back, color: AppTheme.textColor,),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
bottom: title == null ? null : PreferredSize(
|
||||
preferredSize: const Size.fromHeight(1),
|
||||
child: Container(
|
||||
color: AppTheme.yellowColor,
|
||||
height: 1,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
97
lib/widgets/fine_container.dart
Normal file
97
lib/widgets/fine_container.dart
Normal file
@ -0,0 +1,97 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/payment_screen.dart';
|
||||
import 'package:gascom/screens/success_screen.dart';
|
||||
import 'package:gascom/widgets/app_button.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class FineContainer extends StatelessWidget {
|
||||
const FineContainer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"غرامة",
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
AutoSizeText(
|
||||
"2024/12/12",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
AutoSizeText(
|
||||
"200,000 د.ع",
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Divider(
|
||||
color: AppTheme.textColor,
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
AutoSizeText(
|
||||
"مخالفة ساعات التشغيل والاطفاء",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
AutoSizeText(
|
||||
"يجب دفع الغرامة قبل تاريخ 01/09/2024 وبخلافه، لن يتم تزويدك باي حصة بعد هذا التاريخ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 2,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.45,
|
||||
child: AppButton(
|
||||
onPressed: () {
|
||||
pushScreenWithoutNavBar(context,
|
||||
PaymentScreen(
|
||||
title: "غرامات",
|
||||
onPaymentComplete: () {
|
||||
pushScreenWithoutNavBar(context, const SuccessScreen(title: "تم دفع الغرامة بنجاح", subtitle: ""));
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
label: "دفع الغرامة",
|
||||
isElevated: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
55
lib/widgets/home_grid_item.dart
Normal file
55
lib/widgets/home_grid_item.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
|
||||
class HomeGridItem extends StatelessWidget {
|
||||
const HomeGridItem({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.svgPath,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String svgPath;
|
||||
final void Function() onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(25),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
border: Border.all(
|
||||
color: AppTheme.secondaryColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
svgPath,
|
||||
semanticsLabel: title,
|
||||
),
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10,),
|
||||
AutoSizeText(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
minFontSize: 12,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
70
lib/widgets/order_container.dart
Normal file
70
lib/widgets/order_container.dart
Normal file
@ -0,0 +1,70 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
import 'package:gascom/screens/order_details_screen.dart';
|
||||
import 'package:gascom/widgets/order_state_badge.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
class OrderContainer extends StatelessWidget {
|
||||
const OrderContainer({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.cardColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"2024/12/12",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
const OrderStateBadge(state: "قيد المراجعة"),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
AutoSizeText(
|
||||
"5000 لتر",
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
AutoSizeText(
|
||||
"رقم الطلب: 67895435",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Divider(
|
||||
color: AppTheme.primaryColor,
|
||||
thickness: 1,
|
||||
height: 25,
|
||||
),
|
||||
InkWell(
|
||||
splashColor: AppTheme.primaryColor,
|
||||
onTap: () {
|
||||
pushScreenWithoutNavBar(context, OrderDetailsScreen());
|
||||
},
|
||||
child: Center(
|
||||
child: AutoSizeText(
|
||||
"اظهر التفاصيل",
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
28
lib/widgets/order_state_badge.dart
Normal file
28
lib/widgets/order_state_badge.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
|
||||
class OrderStateBadge extends StatelessWidget {
|
||||
const OrderStateBadge({
|
||||
super.key,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
final String state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: state == "قيد المراجعة" ? AppTheme.yellowColor : AppTheme.primaryColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
state,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: state == "قيد المراجعة" ? AppTheme.brownColor : AppTheme.textColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
30
lib/widgets/text_container.dart
Normal file
30
lib/widgets/text_container.dart
Normal file
@ -0,0 +1,30 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gascom/constants/app_theme.dart';
|
||||
|
||||
class TextContainer extends StatelessWidget {
|
||||
const TextContainer({super.key, required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: AppTheme.textColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: AutoSizeText(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
minFontSize: 8,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user