First Commit
This commit is contained in:
81
lib/screens/home_screen.dart
Normal file
81
lib/screens/home_screen.dart
Normal file
@ -0,0 +1,81 @@
|
||||
import 'package:baligh/screens/profile_screen.dart';
|
||||
import 'package:baligh/widgets/baligh_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.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 Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
"البلاغات",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(context, (MaterialPageRoute(builder: (context) {
|
||||
return const ProfileScreen();
|
||||
})));
|
||||
},
|
||||
icon: const Icon(FontAwesomeIcons.user),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: GridView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 1,
|
||||
),
|
||||
children: [
|
||||
BlighCard(
|
||||
icon: FontAwesomeIcons.unlock,
|
||||
label: "الابتزاز",
|
||||
iconColor: Colors.grey[700] ?? Colors.grey,
|
||||
),
|
||||
const BlighCard(
|
||||
icon: FontAwesomeIcons.pills,
|
||||
label: "المخدرات",
|
||||
iconColor: Colors.pink,
|
||||
),
|
||||
const BlighCard(
|
||||
icon: FontAwesomeIcons.person,
|
||||
label: "التحرش",
|
||||
iconColor: Colors.purple,
|
||||
),
|
||||
BlighCard(
|
||||
icon: FontAwesomeIcons.moneyBill,
|
||||
label: "الرشوة",
|
||||
iconColor: Colors.green[700] ?? Colors.green,
|
||||
),
|
||||
BlighCard(
|
||||
icon: FontAwesomeIcons.handFist,
|
||||
label: "التعنيف",
|
||||
iconColor: Colors.yellow[700] ?? Colors.yellow,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
112
lib/screens/login_screen.dart
Normal file
112
lib/screens/login_screen.dart
Normal file
@ -0,0 +1,112 @@
|
||||
import 'package:baligh/screens/otp_screen.dart';
|
||||
import 'package:baligh/widgets/app_toast.dart';
|
||||
import 'package:baligh/widgets/custom_button.dart';
|
||||
import 'package:baligh/widgets/custom_text_field.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
TextEditingController phoneNumberController = TextEditingController();
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
phoneNumberController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 150,
|
||||
),
|
||||
Text(
|
||||
"تسجيل الدخول",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: phoneNumberController,
|
||||
labelText: "رقم الهاتف",
|
||||
inputType: TextInputType.phone),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
CustomButton(
|
||||
label: "تسجيل الدخول",
|
||||
isElevated: true,
|
||||
onPressed: handleLogIn,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleLogIn() async {
|
||||
if (phoneNumberController.text.isEmpty) {
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "يجب ملئ جميع الحقول");
|
||||
});
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await FirebaseAuth.instance.verifyPhoneNumber(
|
||||
phoneNumber: "+964${phoneNumberController.text.trim()}",
|
||||
verificationCompleted: (PhoneAuthCredential credential) {},
|
||||
verificationFailed: (FirebaseAuthException e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
},
|
||||
codeSent: (String verificationId, int? resendToken) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => OtpScreen(
|
||||
verificationId: verificationId,
|
||||
)),
|
||||
);
|
||||
},
|
||||
codeAutoRetrievalTimeout: (String verificationId) {},
|
||||
);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
140
lib/screens/otp_screen.dart
Normal file
140
lib/screens/otp_screen.dart
Normal file
@ -0,0 +1,140 @@
|
||||
import 'package:baligh/screens/home_screen.dart';
|
||||
import 'package:baligh/screens/register_screen.dart';
|
||||
import 'package:baligh/widgets/app_toast.dart';
|
||||
import 'package:baligh/widgets/custom_button.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinput/pinput.dart';
|
||||
|
||||
class OtpScreen extends StatefulWidget {
|
||||
const OtpScreen({
|
||||
super.key,
|
||||
required this.verificationId,
|
||||
});
|
||||
|
||||
final String verificationId;
|
||||
|
||||
@override
|
||||
State<OtpScreen> createState() => _OtpScreenState();
|
||||
}
|
||||
|
||||
class _OtpScreenState extends State<OtpScreen> {
|
||||
TextEditingController otpController = TextEditingController();
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
otpController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 80,
|
||||
),
|
||||
Text(
|
||||
"تأكيد رقم الهاتف",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
"قمنا بإرسال رمز التحقق اليك ادخل رمز التحقق لتأكيد رقم هاتفك",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Pinput(
|
||||
controller: otpController,
|
||||
length: 6,
|
||||
autofocus: true,
|
||||
defaultPinTheme: PinTheme(
|
||||
width: 56,
|
||||
height: 56,
|
||||
textStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge
|
||||
?.copyWith(fontSize: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFECECEC),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
separatorBuilder: (index) => const SizedBox(width: 5),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
CustomButton(
|
||||
label: "تأكيد رثم الهاتف",
|
||||
isElevated: true,
|
||||
onPressed: handleSendOtp,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> handleSendOtp() async {
|
||||
if (otpController.text.isEmpty) {
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "يجب ملئ جميع الحقول");
|
||||
});
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Create a PhoneAuthCredential with the code
|
||||
PhoneAuthCredential credential = PhoneAuthProvider.credential(
|
||||
verificationId: widget.verificationId,
|
||||
smsCode: otpController.text.trim());
|
||||
|
||||
// Sign the user in (or link) with the credential
|
||||
UserCredential userCredential =
|
||||
await FirebaseAuth.instance.signInWithCredential(credential);
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
|
||||
if (!mounted) return;
|
||||
if (userCredential.additionalUserInfo?.isNewUser != true) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const RegisterScreen()),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
144
lib/screens/profile_screen.dart
Normal file
144
lib/screens/profile_screen.dart
Normal file
@ -0,0 +1,144 @@
|
||||
import 'package:baligh/screens/login_screen.dart';
|
||||
import 'package:baligh/widgets/app_toast.dart';
|
||||
import 'package:baligh/widgets/custom_button.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ProfileState { loading, error, loaded }
|
||||
|
||||
class ProfileScreen extends StatefulWidget {
|
||||
const ProfileScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ProfileScreen> createState() => _ProfileScreenState();
|
||||
}
|
||||
|
||||
class _ProfileScreenState extends State<ProfileScreen> {
|
||||
ProfileState _profileState = ProfileState.loading;
|
||||
Map<String, dynamic>? _userData;
|
||||
|
||||
bool isLogOutLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadUserData();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
"المعلومات الشخصية",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: _buildContent(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadUserData() async {
|
||||
setState(() {
|
||||
_profileState = ProfileState.loading;
|
||||
});
|
||||
try {
|
||||
final user = FirebaseAuth.instance.currentUser;
|
||||
if (user == null) {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const LoginScreen()),
|
||||
(x) => false);
|
||||
}
|
||||
|
||||
// get user data from firestore in a coolection called 'users'
|
||||
final userData = await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(user!.uid)
|
||||
.get();
|
||||
|
||||
setState(() {
|
||||
_userData = userData.data();
|
||||
_profileState = ProfileState.loaded;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_profileState = ProfileState.error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logOut() async {
|
||||
setState(() {
|
||||
isLogOutLoading = true;
|
||||
});
|
||||
try {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
|
||||
setState(() {
|
||||
isLogOutLoading = false;
|
||||
});
|
||||
|
||||
if (!mounted) return;
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const LoginScreen()),
|
||||
(x) => false);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLogOutLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildContent() {
|
||||
switch (_profileState) {
|
||||
case ProfileState.loading:
|
||||
return const CircularProgressIndicator();
|
||||
case ProfileState.error:
|
||||
return const Text("حدث خطأ ما");
|
||||
case ProfileState.loaded:
|
||||
return ListView(
|
||||
children: [
|
||||
Text('الاسم: ${_userData!['name']}'),
|
||||
const SizedBox(height: 5),
|
||||
Text('رقم الهاتف: 0${_userData!['phone'].toString().split("+964").last}'),
|
||||
const SizedBox(height: 5),
|
||||
Text('العمر: ${_userData!['age']}'),
|
||||
const SizedBox(height: 5),
|
||||
Text('رقم البطاقة الوطنية: ${_userData!['nationalIdNumber']}'),
|
||||
const SizedBox(height: 5),
|
||||
Text('المدرسة: ${_userData!['schoolName']}'),
|
||||
const SizedBox(height: 5),
|
||||
Text('المرحلة الدراسية: ${_userData!['stage']}'),
|
||||
const SizedBox(height: 5),
|
||||
Text('العنوان: ${_userData!['address']}'),
|
||||
const SizedBox(height: 20),
|
||||
CustomButton(
|
||||
label: "تسجيل الخروج",
|
||||
isElevated: true,
|
||||
onPressed: logOut,
|
||||
isLoading: isLogOutLoading,
|
||||
),
|
||||
],
|
||||
);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
}
|
173
lib/screens/register_screen.dart
Normal file
173
lib/screens/register_screen.dart
Normal file
@ -0,0 +1,173 @@
|
||||
import 'package:baligh/screens/home_screen.dart';
|
||||
import 'package:baligh/widgets/app_toast.dart';
|
||||
import 'package:baligh/widgets/custom_button.dart';
|
||||
import 'package:baligh/widgets/custom_text_field.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RegisterScreen extends StatefulWidget {
|
||||
const RegisterScreen({super.key});
|
||||
|
||||
@override
|
||||
State<RegisterScreen> createState() => _RegisterScreenState();
|
||||
}
|
||||
|
||||
class _RegisterScreenState extends State<RegisterScreen> {
|
||||
TextEditingController fullNameController = TextEditingController();
|
||||
TextEditingController ageController = TextEditingController();
|
||||
TextEditingController schoolNameController = TextEditingController();
|
||||
TextEditingController stageController = TextEditingController();
|
||||
TextEditingController addressController = TextEditingController();
|
||||
TextEditingController nationalIdNumberController = TextEditingController();
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
fullNameController.dispose();
|
||||
ageController.dispose();
|
||||
schoolNameController.dispose();
|
||||
stageController.dispose();
|
||||
addressController.dispose();
|
||||
nationalIdNumberController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Text(
|
||||
"انشاء حساب",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: fullNameController,
|
||||
labelText: "الاسم الثلاثي",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: ageController,
|
||||
labelText: "العمر",
|
||||
inputType: TextInputType.number),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: schoolNameController,
|
||||
labelText: "اسم المدرسة",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: stageController,
|
||||
labelText: "المرحلة الدراسية",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: addressController,
|
||||
labelText: "عنوان السكن",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: nationalIdNumberController,
|
||||
labelText: "رقم البطاقة الوطنية الموحدة",
|
||||
inputType: TextInputType.number),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomButton(
|
||||
label: "انشاء حساب",
|
||||
isElevated: true,
|
||||
onPressed: handleRegister,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleRegister() async {
|
||||
if (fullNameController.text.trim().isEmpty ||
|
||||
ageController.text.trim().isEmpty ||
|
||||
schoolNameController.text.trim().isEmpty ||
|
||||
stageController.text.trim().isEmpty ||
|
||||
addressController.text.trim().isEmpty ||
|
||||
nationalIdNumberController.text.trim().isEmpty) {
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "يجب ملئ جميع الحقول");
|
||||
});
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final auth = FirebaseAuth.instance;
|
||||
|
||||
await auth.currentUser?.updateDisplayName(fullNameController.text.trim());
|
||||
|
||||
if (FirebaseAuth.instance.currentUser?.uid == null) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
|
||||
await FirebaseFirestore.instance
|
||||
.collection('users').doc(FirebaseAuth.instance.currentUser?.uid ?? "").set({
|
||||
'uid': FirebaseAuth.instance.currentUser?.uid ?? "",
|
||||
'name': fullNameController.text.trim(),
|
||||
'phone': FirebaseAuth.instance.currentUser?.phoneNumber ?? "",
|
||||
'age': int.parse(ageController.text.trim()),
|
||||
'schoolName': schoolNameController.text.trim(),
|
||||
'stage': stageController.text.trim(),
|
||||
'address': addressController.text.trim(),
|
||||
'nationalIdNumber': int.parse(nationalIdNumberController.text.trim()),
|
||||
'createdAt': FieldValue.serverTimestamp(),
|
||||
});
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
if (!mounted) return;
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
||||
(x) => false);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
384
lib/screens/report_screen.dart
Normal file
384
lib/screens/report_screen.dart
Normal file
@ -0,0 +1,384 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
import 'package:baligh/screens/success_screen.dart';
|
||||
import 'package:baligh/widgets/app_toast.dart';
|
||||
import 'package:baligh/widgets/custom_button.dart';
|
||||
import 'package:baligh/widgets/custom_text_field.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class ReportScreen extends StatefulWidget {
|
||||
const ReportScreen({
|
||||
super.key,
|
||||
required this.headerText,
|
||||
});
|
||||
|
||||
final String headerText;
|
||||
|
||||
@override
|
||||
State<ReportScreen> createState() => _ReportScreenState();
|
||||
}
|
||||
|
||||
class _ReportScreenState extends State<ReportScreen> {
|
||||
TextEditingController descriptionController = TextEditingController();
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
bool isExpanded = false;
|
||||
|
||||
List<File> images = [];
|
||||
List<File> videos = [];
|
||||
List<File> audios = [];
|
||||
|
||||
String explnationText() {
|
||||
return widget.headerText == "التحرش"
|
||||
? """
|
||||
التحرش هو أي تصرف أو قول غير مرغوب فيه يتعلق بالجنس أو الجنسية، ويهدف إلى إزعاج الشخص الآخر أو تهديده. يمكن أن يحدث التحرش في أماكن العمل، المدارس، الأماكن العامة، أو عبر وسائل التواصل الاجتماعي. يمكن أن يكون التحرش لفظيًا (مثل التعليقات الجنسية)، جسديًا (مثل اللمس غير المناسب)، أو بصريًا (مثل عرض صور غير لائقة).
|
||||
|
||||
حقوق المبلغ عن حالة تحرش:
|
||||
الحماية: على المبلغ أن يشعر بالأمان خلال وبعد تقديم البلاغ. لا يتعرض لأي تهديدات أو انتقادات بسبب تقديمه للبلاغ.
|
||||
|
||||
السرية: يتم التعامل مع المعلومات بسرية، إلا إذا كانت هناك حاجة للكشف عنها في إطار التحقيق أو الإجراءات القانونية.
|
||||
|
||||
التحقق العادل: يتم التحقيق في البلاغ بموضوعية وبدون تحيز. للشخص المبلغ الحق في معرفة نتائج التحقيق.
|
||||
عدم الانتقام: الشخص الذي يبلغ عن التحرش محميًا من أي شكل من أشكال الانتقام أو التمييز بسبب البلاغ.
|
||||
|
||||
التوجيه والمساعدة: يحق للمبلغ الحصول على استشارات قانونية، نفسية، أو أي نوع من المساعدة يحتاجها خلال هذه العملية.
|
||||
"""
|
||||
: widget.headerText == "التعنيف"
|
||||
? """
|
||||
التعنيف هو مصطلح يشير إلى استخدام القوة أو العنف ضد شخص آخر بشكل يتسبب له في أذى جسدي أو نفسي أو عاطفي. يمكن أن يحدث التعنيف في عدة سياقات، مثل العلاقات الشخصية، الأسرة، مكان العمل، أو المجتمع بشكل عام يتمتع الشخص المبلغ ضد التعنيف بما يلي 1. الحماية: للشخص المعنف الحق في الحصول على حماية فورية من الجاني، سواء عبر الأوامر القانونية مثل أوامر الحماية، أو من خلال الدعم من الشرطة والجهات المعنية.
|
||||
|
||||
2. الأمان: يحق للشخص المعنف العيش في بيئة آمنة خالية من التهديدات والعنف. يمكن أن يتضمن ذلك توفير أماكن إيواء أو دعم لتغيير مكان الإقامة إذا لزم الأمر.
|
||||
|
||||
3. الرعاية الصحية: يجب أن يحصل الشخص المعنف على الرعاية الطبية اللازمة لعلاج الإصابات الجسدية والنفسية الناتجة عن العنف.
|
||||
|
||||
4. الدعم النفسي: يحق للأشخاص المعنفين الحصول على مشورة ودعم نفسي للتعامل مع الصدمات النفسية والعاطفية التي قد يواجهونها.
|
||||
|
||||
5. المشورة القانونية: يجب أن يكون الشخص المعنف قادرًا على الحصول على مشورة قانونية حول حقوقه، الخيارات القانونية المتاحة، وكيفية تقديم الشكاوى.
|
||||
|
||||
6. عدم التمييز: لا يتعرض الشخص المعنف للتمييز أو الانتقاص من حقوقه بسبب تعرضه للعنف، ويجب أن يتم التعامل معه بكرامة واحترام.
|
||||
|
||||
7.الخصوصية: يجب الحفاظ على سرية المعلومات الشخصية للشخص المعنف، وعدم الكشف عنها دون إذنه.
|
||||
"""
|
||||
: widget.headerText == "المخدرات"
|
||||
? """
|
||||
المخدرات هي مواد كيميائية تؤثر على الجهاز العصبي المركزي، وقد تؤدي إلى تغييرات في السلوك، المزاج، والإدراك. الكوكايين، الهيروين، والماريجوانا.
|
||||
|
||||
الأضرار المحتملة للمخدرات:
|
||||
1. الصحة الجسدية: يمكن أن تؤدي إلى مشكلات صحية خطيرة مثل أمراض القلب، الكبد، والمشاكل التنفسية.
|
||||
2. الصحة النفسية: قد تسبب اضطرابات مثل الاكتئاب، القلق، أو الاضطرابات النفسية الأخرى.
|
||||
3. الاعتبارات الاجتماعية: يمكن أن تؤدي إلى مشكلات في العلاقات الشخصية، العمل، والمجتمع بشكل عام.
|
||||
حقوق الشخص المبلغ عن المخدرات
|
||||
إذا كنتَ أو كنتِ تبلغون عن مشكلة تتعلق بالمخدرات، لديكَ حقوق يجب أن تُحترم:
|
||||
|
||||
1. الحماية: تكون محميًا من أي شكل من أشكال الانتقام أو الضرر نتيجة تقديم البلاغ.
|
||||
يمكن أن يشمل ذلك حماية من الجاني أو من أي طرف آخر قد يسعى للإضرار بك.
|
||||
2. السرية: الحفاظ على سرية هويتك ومعلوماتك الشخصية خلال عملية التحقيق.
|
||||
لا ينبغي الكشف عن تفاصيل البلاغ دون موافقتك، إلا في حال كانت هناك ضرورة قانونية لذلك.
|
||||
3. التحقق العادل: يتم التحقيق في البلاغ بموضوعية ونزاهة، مع ضمان أن يتم التعامل مع كل الأطراف بشكل عادل.
|
||||
4. التوجيه والمساعدة:
|
||||
الحصول على المشورة القانونية والمساعدة في حال كنت تحتاجين إليها خلال عملية البلاغ.
|
||||
يمكن أن يتضمن ذلك مشورة حول كيفية حماية نفسك وضمان حقوقك القانونية.
|
||||
5. عدم التمييز:
|
||||
لا تتعرض للتمييز أو العقوبات بسبب تقديمك للبلاغ.
|
||||
يُعامل كل من يقدم البلاغ بكرامة واحترام.
|
||||
"""
|
||||
: widget.headerText == "الرشوة"
|
||||
? """
|
||||
الرشوة هي جريمة تحدث عندما يقدم شخص ما أو يستلم مكافأة أو مالًا بشكل غير قانوني مقابل تأثيره على سلوك أو قرار معين في إطار عمله أو منصبه. تُعتبر الرشوة جريمة جنائية في معظم القوانين حول العالم.
|
||||
|
||||
إذا قام شخص بتقديم بلاغ عن جريمة رشوة، فإن هناك حقوقاً معينة يتمتع بها،
|
||||
حماية هوية المبلغ: الشخص الذي يقدم البلاغ قد يتمتع بحماية هويته للحفاظ على أمانه وسلامته، خاصة إذا كان هناك خطر من الانتقام.
|
||||
|
||||
1. الحماية من الانتقام: توجد قوانين تحمي المبلغين عن الفساد والرشوة من أي انتقام قد يتعرضون له في مكان العمل أو من قبل المتورطين في الرشوة.
|
||||
|
||||
2. السرية: البلاغات عن الرشوة يتم التعامل معها بسرية تامة، ولا يتم الكشف عن التفاصيل إلا للجهات المختصة.
|
||||
|
||||
3. التعويض: في بعض الحالات، يكون للمبلغ الحق في الحصول على تعويض مالي أو مكافأة إذا ساهم بلاغه في كشف جريمة رشوة أو فساد كبير.
|
||||
|
||||
4. الحماية القانونية: إذا تم تقديم البلاغ بحسن نية وثبتت صحة الاتهامات، فإن المبلغ يتمتع بحماية قانونية من الملاحقة القضائية أو الاتهام الزائف.
|
||||
"""
|
||||
: widget.headerText == "الابتزاز"
|
||||
? """
|
||||
الابتزاز : جريمة تنطوي على تهديد شخص ما بهدف إجباره على تقديم مال أو خدمات أو القيام بشيء ما ضد إرادته. يعتبر الابتزاز من الجرائم الخطيرة التي يعاقب عليها القانون، سواء كان الابتزاز مادياً أو معنوياً.
|
||||
|
||||
الشخص الذي يقدم بلاغًا عن حالة ابتزاز يتمتع بحقوق معينة لحمايته وضمان عدم تعرضه للضرر. :
|
||||
|
||||
1. حماية الهوية:
|
||||
يتمتع الشخص المبلغ بحقه في الحفاظ على سرية هويته، خاصة في الحالات التي قد يتعرض فيها للخطر أو الانتقام من الجاني.
|
||||
2. الحماية من الانتقام:
|
||||
القوانين تحمي المبلغين عن الابتزاز من أي نوع من الانتقام أو الأذى الذي قد يتعرضون له بسبب تقديمهم للبلاغ. .
|
||||
3. السرية:
|
||||
، يتم التعامل مع بلاغات الابتزاز بسرية تامة. السلطات تحافظ على سرية المعلومات لضمان حماية المبلغ وسلامة التحقيق.
|
||||
4. التعويض:
|
||||
قد يكون للمبلغ الحق في الحصول على تعويض إذا تسبب الابتزاز في خسائر مادية أو معنوية. كما يمكن أن تفرض المحكمة على الجاني دفع تعويضات للضحايا.
|
||||
5. الحماية القانونية:
|
||||
الشخص الذي يبلغ عن الابتزاز يكون محمياً قانونيًا إذا تم البلاغ بحسن نية. لا يمكن ملاحقته قانونياً أو توجيه تهم ضده بسبب تقديم البلاغ.
|
||||
6. المساعدة النفسية والاجتماعية:
|
||||
تقدم خدمات دعم نفسي واجتماعي للمبلغين عن الابتزاز، خاصة إذا كانت الجريمة قد تسببت في صدمة نفسية أو توتر عاطفي.
|
||||
8. الإبلاغ عن الابتزاز الرقمي:
|
||||
في حالة الابتزاز الإلكتروني (مثل التهديد بنشر صور أو معلومات شخصية)، يتمتع المبلغ بحقوق مماثلة ويجب أن تكون السلطات قادرة على تعقب الجناة واتخاذ الإجراءات القانونية ضدهم.
|
||||
"""
|
||||
: "";
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
descriptionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
widget.headerText,
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
ExpansionPanelList(
|
||||
elevation: 0,
|
||||
expandedHeaderPadding: EdgeInsets.zero,
|
||||
expansionCallback: (index, isOpen) {
|
||||
setState(() {
|
||||
isExpanded = isOpen;
|
||||
});
|
||||
},
|
||||
children: [
|
||||
ExpansionPanel(
|
||||
backgroundColor: Colors.transparent,
|
||||
isExpanded: isExpanded,
|
||||
canTapOnHeader: true,
|
||||
headerBuilder: (context, isOpen) => Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"شرح توضيحي البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Text(
|
||||
explnationText(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: descriptionController,
|
||||
labelText: "قم بوصف الحالة",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: handleAddImage,
|
||||
icon: images.isEmpty
|
||||
? Icon(FontAwesomeIcons.image, color: Colors.blue[800])
|
||||
: badges.Badge(
|
||||
badgeContent: Text(
|
||||
images.length.toString(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: Colors.white),
|
||||
),
|
||||
child: Icon(FontAwesomeIcons.image,
|
||||
color: Colors.blue[800]),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: handleAddVideo,
|
||||
icon: videos.isEmpty
|
||||
? const Icon(
|
||||
FontAwesomeIcons.video,
|
||||
color: Colors.red,
|
||||
)
|
||||
: badges.Badge(
|
||||
badgeContent: Text(
|
||||
videos.length.toString(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: Colors.white),
|
||||
),
|
||||
child: const Icon(
|
||||
FontAwesomeIcons.video,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: handleAddAudio,
|
||||
icon: audios.isEmpty
|
||||
? Icon(
|
||||
FontAwesomeIcons.microphone,
|
||||
color: Colors.purple[300],
|
||||
)
|
||||
: badges.Badge(
|
||||
badgeContent: Text(
|
||||
audios.length.toString(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: Colors.white),
|
||||
),
|
||||
child: Icon(
|
||||
FontAwesomeIcons.microphone,
|
||||
color: Colors.purple[300],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
CustomButton(
|
||||
label: "ارسال",
|
||||
isElevated: true,
|
||||
onPressed: handleSendReport,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleAddImage() async {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
allowMultiple: true,
|
||||
type: FileType.image,
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
for (var el in result.files) {
|
||||
setState(() {
|
||||
images.add(File(el.path ?? ""));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleAddVideo() async {
|
||||
FilePickerResult? result = await FilePicker.platform
|
||||
.pickFiles(allowMultiple: true, type: FileType.video);
|
||||
|
||||
if (result != null) {
|
||||
for (var el in result.files) {
|
||||
setState(() {
|
||||
videos.add(File(el.path ?? ""));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleAddAudio() async {
|
||||
FilePickerResult? result = await FilePicker.platform
|
||||
.pickFiles(allowMultiple: true, type: FileType.video);
|
||||
|
||||
if (result != null) {
|
||||
for (var el in result.files) {
|
||||
setState(() {
|
||||
audios.add(File(el.path ?? ""));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleSendReport() async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
final storageRef = FirebaseStorage.instance.ref();
|
||||
List<String> attachments = [];
|
||||
|
||||
Future<void> uploadImage(List<File> files, String folder) async {
|
||||
for (var el in files) {
|
||||
final filePath =
|
||||
'${el.path.split('.').first}_${Random().nextInt(99999)}.${el.path.split('.').last}';
|
||||
|
||||
final ref = storageRef.child("$folder/$filePath");
|
||||
final uploadTask = ref.putFile(el); // Upload the file
|
||||
|
||||
final snapshot = await uploadTask
|
||||
.whenComplete(() {}); // Wait for upload completion
|
||||
|
||||
final downloadUrl =
|
||||
await snapshot.ref.getDownloadURL(); // Get download URL
|
||||
|
||||
attachments.add(downloadUrl);
|
||||
}
|
||||
}
|
||||
|
||||
await uploadImage(images, "images");
|
||||
await uploadImage(videos, "videos");
|
||||
await uploadImage(audios, "audios");
|
||||
|
||||
DocumentReference userRef = FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(FirebaseAuth.instance.currentUser?.uid);
|
||||
|
||||
await FirebaseFirestore.instance.collection('reports').add({
|
||||
'userRef': userRef,
|
||||
'userId': FirebaseAuth.instance.currentUser?.uid,
|
||||
'type': widget.headerText,
|
||||
'description': descriptionController.text.trim(),
|
||||
'attachments': attachments,
|
||||
'status': "قيد الانتظار",
|
||||
'createdAt': FieldValue.serverTimestamp(),
|
||||
});
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
|
||||
if (!mounted) return;
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => const SuccessScreen()));
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
46
lib/screens/success_screen.dart
Normal file
46
lib/screens/success_screen.dart
Normal file
@ -0,0 +1,46 @@
|
||||
import 'package:baligh/screens/home_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SuccessScreen extends StatelessWidget {
|
||||
const SuccessScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
const SizedBox(height: 100,),
|
||||
Icon(Icons.done_outline_rounded, size: 85, color: Colors.blue[600],),
|
||||
const SizedBox(height: 50,),
|
||||
Text(
|
||||
"تم ارسال البلاغ",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20,),
|
||||
Text(
|
||||
"تم ارسال بلاغك بنجاح و ستتم مراجعته من قبل الجهات المعنية باقرب وقت",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 40,),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) => const HomeScreen()), (x) => false);
|
||||
},
|
||||
child: Text(
|
||||
"العودة الى صفحة البلاغات",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge
|
||||
?.copyWith(color: Colors.white),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user