First Commit
This commit is contained in:
42
lib/widgets/app_toast.dart
Normal file
42
lib/widgets/app_toast.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppToast extends StatelessWidget {
|
||||
const AppToast({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.bottomPadding,
|
||||
this.color,
|
||||
this.textStyle,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final double? bottomPadding;
|
||||
final Color? color;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: bottomPadding ?? 100),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: color ?? Theme.of(context).scaffoldBackgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.4),
|
||||
offset: const Offset(2, 2),
|
||||
blurRadius: 5,
|
||||
spreadRadius: 1
|
||||
)
|
||||
]
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
86
lib/widgets/custom_button.dart
Normal file
86
lib/widgets/custom_button.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/constants/theme.dart';
|
||||
import 'package:baligh_dashboard/widgets/small_spinner.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomButton extends StatelessWidget {
|
||||
const CustomButton({
|
||||
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 ?? MyCustomTheme.primaryColor)
|
||||
),
|
||||
child: isLoading
|
||||
? const SmallSpinner()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
label,
|
||||
minFontSize: 8,
|
||||
maxFontSize: 14,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: MyCustomTheme.bgColor,
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: 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 ?? MyCustomTheme.primaryColor,
|
||||
))
|
||||
),
|
||||
child: isLoading
|
||||
? SmallSpinner(color: MyCustomTheme.primaryColor,)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
label,
|
||||
minFontSize: 8,
|
||||
maxFontSize: 14,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: color ?? MyCustomTheme.primaryColor,
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
77
lib/widgets/custom_text_field.dart
Normal file
77
lib/widgets/custom_text_field.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomTextField extends StatefulWidget {
|
||||
const CustomTextField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.labelText,
|
||||
required this.inputType,
|
||||
this.errorText,
|
||||
this.isPassword = false,
|
||||
this.maxLines = 1,
|
||||
this.focusNode,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final String labelText;
|
||||
final TextInputType inputType;
|
||||
final String? errorText;
|
||||
final bool isPassword;
|
||||
final int maxLines;
|
||||
final FocusNode? focusNode;
|
||||
|
||||
@override
|
||||
State<CustomTextField> createState() => _CustomTextFieldState();
|
||||
}
|
||||
|
||||
class _CustomTextFieldState extends State<CustomTextField> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: widget.controller,
|
||||
focusNode: widget.focusNode,
|
||||
keyboardType: widget.inputType,
|
||||
obscureText: widget.isPassword,
|
||||
enableSuggestions: !widget.isPassword,
|
||||
autocorrect: false,
|
||||
maxLines: widget.maxLines,
|
||||
textDirection: TextDirection.rtl,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||
labelText: widget.labelText,
|
||||
alignLabelWithHint: true,
|
||||
labelStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).dividerColor),
|
||||
floatingLabelStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).dividerColor),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Theme.of(context).cardColor, width: 1.5)
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Theme.of(context).dividerColor, width: 1)
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).cardColor,
|
||||
errorText: widget.errorText,
|
||||
errorStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.red
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.red, width: 1.5)
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.red, width: 1.5)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
127
lib/widgets/dashboard_tile.dart
Normal file
127
lib/widgets/dashboard_tile.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/screens/report_details_screen.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DashboardTile extends StatelessWidget {
|
||||
const DashboardTile({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
required this.school,
|
||||
required this.type,
|
||||
required this.status,
|
||||
required this.age,
|
||||
required this.attachments,
|
||||
required this.createdAt,
|
||||
required this.description,
|
||||
required this.nationalIdNumber,
|
||||
required this.phone,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String school;
|
||||
final String type;
|
||||
final String status;
|
||||
final Timestamp createdAt;
|
||||
final int age;
|
||||
final String phone;
|
||||
final int nationalIdNumber;
|
||||
final String description;
|
||||
final List attachments;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ReportDetailsScreen(
|
||||
id: id,
|
||||
name: name,
|
||||
age: age,
|
||||
phone: phone,
|
||||
address: address,
|
||||
school: school,
|
||||
nationalIdNumber: nationalIdNumber,
|
||||
description: description,
|
||||
attachments: attachments,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
status: status,
|
||||
)));
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
name,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
address,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
school,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
type,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
status,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
94
lib/widgets/file_card.dart
Normal file
94
lib/widgets/file_card.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/widgets/app_toast.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class FileCard extends StatelessWidget {
|
||||
const FileCard(
|
||||
{super.key,
|
||||
required this.type,
|
||||
required this.filename,
|
||||
required this.fileUrl});
|
||||
|
||||
final String type;
|
||||
final String filename;
|
||||
final String fileUrl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () async {
|
||||
try {
|
||||
html.AnchorElement(href: fileUrl)
|
||||
..setAttribute('target', '_blank')
|
||||
..setAttribute('download', '$filename.pdf') // Specify the filename
|
||||
..click();
|
||||
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "تم التحميل");
|
||||
});
|
||||
} catch (e) {
|
||||
print(e);
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما في التحميل");
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 180,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
padding: const EdgeInsets.all(8),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: type == "image"
|
||||
? Colors.red.withOpacity(0.2)
|
||||
: type == "video"
|
||||
? Colors.blue.withOpacity(0.2)
|
||||
: Colors.grey.withOpacity(0.2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
type == "image"
|
||||
? FontAwesomeIcons.image
|
||||
: type == "video"
|
||||
? FontAwesomeIcons.video
|
||||
: FontAwesomeIcons.microphone,
|
||||
color: type == "image"
|
||||
? Colors.red
|
||||
: type == "video"
|
||||
? Colors.blue
|
||||
: Colors.grey,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
filename,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
minFontSize: 6,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
19
lib/widgets/small_spinner.dart
Normal file
19
lib/widgets/small_spinner.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SmallSpinner extends StatelessWidget {
|
||||
final Color? color;
|
||||
|
||||
const SmallSpinner({super.key, this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
color: color ?? Colors.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user