This commit is contained in:
Riccardo Usardi committed 2025-07-18 22:24:01 +02:00
commit e833bcaec4
224 files changed
+11812

No files matched your search

+90
View File
@@ -0,0 +1,90 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'src/models/dashboard_models/dashboard_card.model.dart';
final demoUsersList = List.generate(
20,
(i) => UsersModel.fromMap({
'id': i.toString(),
'avatar':
'https://cdn.dribbble.com/users/1500210/avatars/normal/ebcc769d2f155e72aa024bdafadc28cc.jpg?1629718724&resize=80x80',
'name': 'user $i',
'dateOfBirth': DateTime.now().toLocal(),
'gender': i.isEven ? "male" : "female",
'email': 'user$i@codestein.dev',
'phone': (Random.secure().nextInt(9999999) + 1999999).toString(),
'status': i.isEven,
}),
);
class UsersModel {
final String id;
final String avatar;
final String name;
final DateTime dateOfBirth;
final String gender;
final String email;
final String phone;
bool status;
UsersModel({
required this.id,
required this.avatar,
required this.name,
required this.dateOfBirth,
required this.gender,
required this.email,
required this.phone,
this.status = false,
});
factory UsersModel.fromMap(Map<String, dynamic> map) {
return UsersModel(
id: map['id'],
avatar: map['avatar'],
name: map['name'],
dateOfBirth: map['dateOfBirth'],
gender: map['gender'],
email: map['email'],
phone: map['phone'],
status: map['status'] ?? false,
);
}
}
// final dashboardCardModelValues = [
// DashboardCardModel.fromJson({
// 'name': 'Projects',
// 'icon': Icons.wine_bar,
// 'value': 327,
// 'persentage': 32.1,
// 'persentageValue': '2.1',
// 'notes': '8k social visitors',
// }),
// DashboardCardModel.fromJson({
// 'name': 'Stock Qty',
// 'icon': Icons.add_chart_rounded,
// 'value': 275,
// 'persentage': 64.1,
// 'persentageValue': '3.1',
// 'notes': 'Total 424,567 deliveries',
// }),
// DashboardCardModel.fromJson({
// 'name': 'C APEX',
// 'icon': Icons.apple_rounded,
// 'value': 648,
// 'persentage': 72.1,
// 'persentageValue': '12.1',
// 'notes': 'Counted in Millions',
// }),
// DashboardCardModel.fromJson({
// 'name': 'Saving',
// 'icon': Icons.data_saver_off_rounded,
// 'value': 18478,
// 'persentage': 24.1,
// 'persentageValue': '0.1',
// 'notes': 'Reports by states and ganders',
// }),
// ];
+50
View File
@@ -0,0 +1,50 @@
import 'src/classes/constents.class.dart';
import 'src/platform/platform_functions.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'src/classes/functions.class.dart';
import 'src/classes/routes.class.dart';
// void main() async {
// WidgetsFlutterBinding.ensureInitialized();
// PlatformFunctions.init();
// await initializeProviders();
// runApp(const MyApp());
// }
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Dashboard',
debugShowCheckedModeBanner: false,
getPages: appRoutes,
initialRoute: appRoutes.first.name,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown
},
),
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: primaryColor).copyWith(
surfaceTint: Colors.transparent,
),
textSelectionTheme: TextSelectionThemeData(
cursorColor: primaryColor,
selectionColor: primaryColor.withOpacity(.3),
),
useMaterial3: true,
textTheme: GoogleFonts.poppinsTextTheme(),
),
);
}
}
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
const Color primaryColor = Colors.blueAccent;
const Color navbarColor = Colors.white;
const Color drawerColor = Colors.white;
const bool toastAutoDissmiss = true;
const Duration toastDuration = Duration(milliseconds: 200);
const Duration toastDissmissDuration = Duration(seconds: 4);
// this is the maximum width the main container can take as width => in web called (fixed, fluid)
// it can be double.infinity or fixed double like 1600.0
const double maxContainerWidth = 1600;
// if true a BreadCampWidget will be showen at the top of the main container
// you can add or edit this widget as you like, you will find it in the /src/widgets/app_widgets.
const bool showBreadCamp = false;
const List<BoxShadow> cardShadow = [
BoxShadow(
color: Color(0x1F6E9AE6),
offset: Offset(0, 12),
blurRadius: 20,
spreadRadius: .05,
),
];
@@ -0,0 +1,9 @@
enum AlertType { primary, success, danger, warning, info }
enum ToastType { primary, success, danger, warning, info }
enum ScreenSizeType { desktop, tablet, mobile }
enum DissmissType { auto, click, swipe }
enum DrawerItemType { link, menu }
@@ -0,0 +1,165 @@
import 'dart:math';
import 'constents.class.dart';
import 'enums.class.dart';
import 'routes.class.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import '../providers/alert.provider.dart';
import '../providers/auth.provider.dart';
import '../providers/routes.provider.dart';
import '../providers/search.provider.dart';
import '../providers/toast.provider.dart';
String imgUrl(String url) {
if (kIsWeb) return 'images/$url';
return 'images/$url';
}
Widget svg(String name, {double size = 20, Color? color}) {
try {
final String assetName = 'images/svgs/$name.svg';
return SvgPicture.asset(
assetName,
width: size,
height: size,
colorFilter: ColorFilter.mode(color ?? primaryColor, BlendMode.srcIn),
);
} catch (e) {
//
}
return const SizedBox();
}
alert(
String message, {
AlertType type = AlertType.primary,
Duration autoDismissAfter = Duration.zero,
}) {
Get.find<AlertProvider>().setAlert(
message,
type: type,
autoDismissAfter: autoDismissAfter,
);
}
toast(
String? message, {
ToastType type = ToastType.primary,
VoidCallback? callback,
}) {
Get.find<ToastProvider>().addToast(message, type: type, callback: callback);
}
void showModel(String type) {
final r = Random.secure().nextInt(5);
switch (r) {
case 0:
alert(AlertType.primary.toString(), type: AlertType.primary);
break;
case 1:
alert(AlertType.success.toString(), type: AlertType.success);
break;
case 2:
alert(AlertType.warning.toString(), type: AlertType.warning);
break;
case 3:
alert(AlertType.danger.toString(), type: AlertType.danger);
break;
case 4:
alert(AlertType.info.toString(), type: AlertType.info);
break;
default:
alert(AlertType.primary.toString(), type: AlertType.primary);
}
// Get.defaultDialog(content: SelectableText(type));
}
Future<void> initializeProviders() async {
Get.put(AuthProvider(), permanent: true);
Get.put(AlertProvider(), permanent: true);
Get.put(RoutesProvider());
Get.put(SearchProvider());
Get.put(ToastProvider());
}
Future<void> removeProviders() async {
await Get.delete<RoutesProvider>(force: true);
await Get.delete<SearchProvider>(force: true);
await Get.delete<ToastProvider>(force: true);
}
closeDrawerMenu(String item) {
for (var child in drawerMenu) {
if (child.type == DrawerItemType.menu) {
child.isOpen = false;
for (var subChild in child.children) {
if (subChild.route == item) {
child.isOpen = true;
} else if (subChild.type == DrawerItemType.menu) {
subChild.isOpen = false;
for (var subsubChild in subChild.children) {
if (subsubChild.route == item) {
child.isOpen = true;
subChild.isOpen = true;
}
}
}
}
}
}
}
int menuIndex(String item) {
for (var child in drawerMenu) {
if (child.type == DrawerItemType.menu) {
for (var subChild in child.children) {
if (subChild.type == DrawerItemType.menu) {
for (var subsubChild in subChild.children) {
if (subsubChild.route == item) {
return drawerMenu.indexOf(child);
}
}
} else {
if (subChild.route == item) {
return drawerMenu.indexOf(child);
}
}
}
} else {
if (child.route == item) {
return drawerMenu.indexOf(child);
}
}
}
return 0;
}
void focusSearch() {
Get.find<SearchProvider>().focus();
}
String formatDateTime(DateTime value) {
int hours = value.hour;
String letters = 'AM';
if (hours > 12) {
hours -= 12;
letters = "PM";
}
String convertedDateTime = value.year.toString();
convertedDateTime += "-${value.month.toString().padLeft(2, '0')}";
convertedDateTime += "-${value.day.toString().padLeft(2, '0')}";
convertedDateTime += " ${hours.toString().padLeft(2, '0')}";
convertedDateTime += "-${value.minute.toString().padLeft(2, '0')} $letters";
return convertedDateTime;
}
String formatDate(DateTime value) {
String convertedDateTime = value.year.toString();
convertedDateTime += "-${value.month.toString().padLeft(2, '0')}";
convertedDateTime += "-${value.day.toString().padLeft(2, '0')}";
return convertedDateTime;
}
+157
View File
@@ -0,0 +1,157 @@
import 'package:u_vineyard/pages/login.page.dart';
import 'package:u_vineyard/pages/new.vineyard.page.dart';
import '../models/app_models/drawer_item.model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../middlewares/auth.middleware.dart';
import '../screens/dashboard/dashboard.screen.dart';
import '../screens/main/main.screen.dart';
List<GetPage<dynamic>> appRoutes = [
GetPage(
title: 'Dashboard',
name: MainScreen.route,
page: () => const MainScreen(),
middlewares: [AuthMiddleware()],
),
GetPage(
title: 'Login',
name: LoginPage.route,
page: () => const LoginPage(),
middlewares: [GuestMiddleware()],
)
];
Map<String, Widget Function()> internalRoutes = {
'/dashboard': () => const DashboadScreen(),
'/vineyards/new-vineyard': () => const NewVineyardScreen(), //const SizedBox(child: Text('1')),
'/vineyards/list': () => const SizedBox(child: Text('2')),
'/vineyards/edit': () => const SizedBox(child: Text('3')),
'/settings/account': () => const SizedBox(child: Text('5')),
'/menu2/1': () => const SizedBox(child: Text('menu 2-1')),
'/menu2/2': () => const SizedBox(child: Text('menu 2-2')),
'/menu2/3': () => const SizedBox(child: Text('menu 2-3')),
'/menu2/4': () => const SizedBox(child: Text('menu 2-4')),
'/menu2/menu3/1': () => const SizedBox(child: Text('menu 2-3-1')),
'/menu2/menu3/2': () => const SizedBox(child: Text('menu 2-3-2')),
'/menu2/menu3/3': () => const SizedBox(child: Text('menu 2-3-3')),
'/menu2/menu3/4': () => const SizedBox(child: Text('menu 2-3-4')),
};
final List<Map<String, dynamic>> drawerMenuMap = [
{
'type': 'link',
'name': 'Dashboard',
'icon': 'dashboard',
'route': '/dashboard',
},
{
'type': 'menu',
'name': 'Vigne',
'icon': 'menu',
'route': '/vineyards',
'children': [
{
'type': 'link',
'name': 'Aggiungi Vigna',
'icon': 'dot',
'route': '/new-vineyard',
},
{
'type': 'link',
'name': 'Lista Vigne',
'icon': 'dot',
'route': '/list',
},
{
'type': 'link',
'name': 'Modifica Vigna',
'icon': 'dot',
'route': '/edit',
},
],
},
{
'type': 'menu',
'name': 'Menu2',
'icon': 'menu',
'route': '/menu2',
'children': [
{
'type': 'link',
'name': 'Menu2 Item 1',
'icon': 'dot',
'route': '/1',
},
{
'type': 'link',
'name': 'Menu2 Item 2',
'icon': 'dot',
'route': '/2',
},
{
'type': 'link',
'name': 'Menu2 Item 3',
'icon': 'dot',
'route': '/3',
},
{
'type': 'link',
'name': 'Menu2 Item 4',
'icon': 'dot',
'route': '/4',
},
{
'type': 'menu',
'name': 'Menu2 menu',
'icon': 'menu',
'route': '/menu3',
'children': [
{
'type': 'link',
'name': 'Menu3 Item 1',
'icon': 'dot',
'route': '/1',
},
{
'type': 'link',
'name': 'Menu3 Item 2',
'icon': 'dot',
'route': '/2',
},
{
'type': 'link',
'name': 'Menu3 Item 3',
'icon': 'dot',
'route': '/3',
},
{
'type': 'link',
'name': 'Menu3 Item 4',
'icon': 'dot',
'route': '/4',
},
],
},
],
},
{
'type': 'menu',
'name': 'Settings',
'icon': 'settings',
'route': '/settings',
'children': [
{
'type': 'link',
'name': 'Account',
'icon': 'account',
'route': '/account',
},
],
},
];
final List<DrawerItem> drawerMenu =
drawerMenuMap.map(DrawerItem.fromJson).toList();
@@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const escape = SingleActivator(LogicalKeyboardKey.escape);
const enter = SingleActivator(LogicalKeyboardKey.enter);
const controlF = SingleActivator(LogicalKeyboardKey.keyF, control: true);
@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../providers/auth.provider.dart';
class AuthMiddleware extends GetMiddleware {
final authProvider = Get.find<AuthProvider>();
@override
RouteSettings? redirect(String? route) {
return authProvider.isAuth || route == '/login'
? null
: const RouteSettings(name: '/login');
}
}
class GuestMiddleware extends GetMiddleware {
final authProvider = Get.find<AuthProvider>();
@override
RouteSettings? redirect(String? route) {
return authProvider.isAuth && route != '/'
? const RouteSettings(name: '/')
: null;
}
}
@@ -0,0 +1,42 @@
// ignore_for_file: hash_and_equals
import '../../classes/enums.class.dart';
class DrawerItem {
bool isOpen;
final DrawerItemType type;
final String name;
final String icon;
final String route;
final List<DrawerItem> children;
DrawerItem({
this.isOpen = false,
this.type = DrawerItemType.link,
required this.name,
required this.icon,
required this.route,
this.children = const [],
});
factory DrawerItem.fromJson(Map<String, dynamic> json, {String? parent}) {
if (parent is String) json['route'] = parent + json['route'];
final List<Map<String, dynamic>> childrenMap = json['children'] ?? [];
final List<DrawerItem> children = [];
for (var child in childrenMap) {
children.add(DrawerItem.fromJson(child, parent: json['route']));
}
return DrawerItem(
type: children.isEmpty ? DrawerItemType.link : DrawerItemType.menu,
name: json['name'],
icon: json['icon'] ?? '',
route: json['route'],
children: children,
);
}
@override
bool operator ==(Object other) {
return other is DrawerItem && other.name == name && other.route == route;
}
}
@@ -0,0 +1,7 @@
class UserModel {
final String name;
final String email;
final String image;
UserModel(this.name, this.email, this.image);
}
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
class DashboardCardModel {
final String name;
final IconData icon;
final int value;
double? persentage;
String? persentageValue;
String? notes;
DashboardCardModel({
required this.name,
required this.icon,
required this.value,
this.persentage,
this.persentageValue,
this.notes,
});
factory DashboardCardModel.fromJson(json) {
return DashboardCardModel(
name: json['name'],
icon: json['icon'],
value: json['value'],
persentage: json['persentage'],
persentageValue: json['persentageValue'],
notes: json['notes'],
);
}
}
@@ -0,0 +1,17 @@
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
class GridColumnModel {
final String name;
final double width;
final bool allowFiltering;
final bool allowSorting;
final ColumnWidthMode columnWidthMode;
GridColumnModel({
required this.name,
this.width = double.nan,
this.allowFiltering = true,
this.allowSorting = true,
this.columnWidthMode = ColumnWidthMode.none,
});
}
@@ -0,0 +1,3 @@
class GeneralSearchModel {
GeneralSearchModel();
}
@@ -0,0 +1,24 @@
import 'package:flutter/services.dart';
import '../classes/functions.class.dart';
class PlatformFunctions {
static void init() {
try {
keyListener();
} catch (e) {
//
}
}
static keyListener() {
RawKeyboard.instance.addListener((event) {
final isKeyDown = event is RawKeyDownEvent;
if (isKeyDown &&
event.logicalKey.keyId == 102 &&
event.isControlPressed) {
focusSearch();
}
});
}
}
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../classes/enums.class.dart';
import '../widgets/notifications_widgets/alert.widget.dart';
class AlertProvider extends GetxController {
final Rx<Alert?> _alert = Rx<Alert?>(null);
Widget? get alert => _alert.value;
setAlert(
String message, {
AlertType type = AlertType.info,
Duration autoDismissAfter = Duration.zero,
bool dismissable = true,
}) {
if (message.isEmpty) {
_alert.value = null;
} else {
_alert.value = Alert(
message,
type: type,
dismissible: dismissable,
autoDismissAfter: autoDismissAfter,
onDismiss: () {
_alert.value = null;
update();
},
);
}
update();
}
}
@@ -0,0 +1,50 @@
import 'package:u_vineyard/entities/user.dart';
import 'package:u_vineyard/pages/login.page.dart';
import 'package:u_vineyard/services/api.user.service.dart';
import 'package:u_vineyard/services/auth.service.dart';
import 'package:get/get.dart';
import '../classes/functions.class.dart';
class AuthProvider extends GetxController {
final Rx<User?> _profile = Rx<User?>(null);
final RxBool _isAuth = RxBool(AuthService.instance.token != null);
bool get isAuth => _isAuth.value;
User? get profile => _profile.value;
@override
void onInit() {
super.onInit();
// Chiama la funzione per recuperare il profilo all'inizializzazione del controller
fetchProfileData();
}
Future<void> toggleAuth(bool value) async {
_isAuth.value = value;
if(!value) {
await removeProviders();
} else {
// Se l'utente si autentica, recupera i dati del profilo
await fetchProfileData();
}
update();
}
Future<void> fetchProfileData() async {
final userService = UserService();
// Assumiamo che l'ID utente sia 1
final fetchedUser = await userService.userProfile(1);
// Aggiorna la variabile reattiva
_profile.value = fetchedUser;
}
void logout() async {
_isAuth.value = false;
AuthService.instance.clear();
update();
await removeProviders();
Get.offAllNamed(LoginPage.route);
}
}
@@ -0,0 +1,103 @@
import '../providers/auth.provider.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../classes/functions.class.dart';
import '../classes/routes.class.dart';
import '../models/app_models/drawer_item.model.dart';
import '../screens/errors/404.screen.dart';
class Routes {
static void to(Widget Function() screen, {dynamic arguments}) {
Get.find<RoutesProvider>().to(screen, arguments: arguments);
}
static void toNamed(
String newRoute, {
duplicate = false,
dynamic arguments,
}) {
Get.find<RoutesProvider>().toNamed(
newRoute,
arguments: arguments,
duplicate: duplicate,
);
closeDrawerMenu(newRoute);
}
}
class RoutesProvider extends GetxController {
GlobalKey<ScaffoldState>? mainKey = GlobalKey();
final RxInt _selectedIndex = RxInt(0);
final RxString _current = RxString('/dashboard');
final Rx<Widget Function()?> _currentWidget = Rx<Widget Function()?>(null);
final Rx<dynamic> _arguments = Rx<dynamic>(null);
int get currentIndex => _selectedIndex.value;
String get current => _current.value;
dynamic get arguments => _arguments.value;
bool get isDrawerOpen => mainKey?.currentState?.isDrawerOpen ?? false;
Widget Function() get currentWidget => getWidget();
DrawerItem get currentMenu => drawerMenu[currentIndex];
set currentWidget(Widget Function()? v) => _currentWidget.value = v;
set currentIndex(int v) => _selectedIndex.value = v;
Widget Function() getWidget() {
final auth = Get.find<AuthProvider>();
if (!auth.isAuth) {
auth.logout();
return () => const Error404Screen();
}
return _currentWidget.value != null
? _currentWidget.value!
: (internalRoutes[current] ?? () => const Error404Screen());
}
void toNamed(String newRoute, {duplicate = false, dynamic arguments}) {
if (newRoute.startsWith('/')) {
if (duplicate) {
_arguments.value = arguments;
_current.value = newRoute;
_currentWidget.value = null;
update();
} else if (newRoute != _current.value) {
_arguments.value = arguments;
_current.value = newRoute;
_currentWidget.value = null;
update();
}
}
try {
currentIndex = menuIndex(newRoute);
update();
} catch (e) {
//
}
}
to(Widget Function() screen, {dynamic arguments}) {
_current.value = '-';
_arguments.value = arguments;
currentWidget = screen;
update();
}
void toggleDrawer() {
if (mainKey?.currentState?.isDrawerOpen ?? false) {
mainKey?.currentState?.openDrawer();
} else {
mainKey?.currentState?.closeDrawer();
}
}
void closeDrawer() {
try {
mainKey?.currentState?.closeDrawer();
} catch (e) {
//
}
}
}
@@ -0,0 +1,71 @@
import '../classes/enums.class.dart';
import '../models/general_search.model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../classes/functions.class.dart';
import '../screens/search/general_search.screen.dart';
import 'routes.provider.dart';
class SearchProvider extends GetxController {
final searchController = TextEditingController();
final node = FocusNode();
final RxBool _isLoading = RxBool(false);
bool get isLoading => _isLoading.value;
@override
void dispose() {
searchController.dispose();
node.dispose();
super.dispose();
}
void loading(bool value) {
_isLoading.value = value;
update();
}
search(String value) async {
if (searchController.text.isEmpty) return;
loading(true);
await Future.delayed(const Duration(seconds: 1), () {
if (value.toLowerCase() == 'test') {
Routes.to(() => const GeneralSerch(<GeneralSearchModel>[]));
searchController.clear();
} else {
toast(
'$value Not found',
type: ToastType.warning,
callback: () => print('clicked'),
);
focus();
}
});
loading(false);
}
void unfocus() {
node.unfocus();
update();
}
void clear() {
node.unfocus();
searchController.clear();
update();
}
void focus() {
try {
node.requestFocus();
searchController.selection = TextSelection(
baseOffset: 0,
extentOffset: searchController.text.length,
);
} catch (e) {
//
}
}
}
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../classes/constents.class.dart';
import '../classes/enums.class.dart';
import '../widgets/notifications_widgets/toast.widget.dart';
class ToastProvider extends GetxController {
late GlobalKey<AnimatedListState> listKey;
@override
onInit() {
super.onInit();
setKey(GlobalKey<AnimatedListState>());
}
setKey(GlobalKey<AnimatedListState> key) {
listKey = key;
}
final Rx<List<ToastModel>> _toasts = Rx<List<ToastModel>>([]);
List<ToastModel> get toasts => _toasts.value;
addToast(String? message,
{ToastType type = ToastType.primary, VoidCallback? callback}) {
final item = ToastModel(type: type, message: message, callback: callback);
_toasts.value.insert(0, item);
listKey.currentState?.insertItem(0, duration: toastDuration);
update();
}
removeToast(ToastModel item, DissmissType type) {
try {
final index = _toasts.value.indexOf(item);
_toasts.value.removeAt(index);
listKey.currentState?.removeItem(
index,
(__, animation) =>
type == DissmissType.swipe ? const SizedBox() : buildItem(item, animation),
duration: toastDuration,
);
update();
} catch (e) {
//
}
}
Widget buildItem(ToastModel item, Animation<double> animation) {
return SizeTransition(
sizeFactor: animation,
child: FadeTransition(
opacity: animation,
child: Toast(item, removeToast),
),
);
}
}
@@ -0,0 +1,275 @@
import '../../../providers/auth.provider.dart';
import '../../../widgets/responsive_widgets/responsive.widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../../classes/enums.class.dart';
import '../../../classes/functions.class.dart';
import '../../../providers/alert.provider.dart';
import '../../../widgets/simple_loader.widget.dart';
class LoginScreenOld extends StatefulWidget {
static const route = '/login';
const LoginScreenOld({super.key});
@override
State<LoginScreenOld> createState() => _LoginScreenOldState();
}
class _LoginScreenOldState extends State<LoginScreenOld> {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
bool isVisible = false;
bool isLoading = false;
String? error;
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
update(VoidCallback func) {
if (mounted) setState(func);
}
bool validate() {
final email = emailController.text;
final pass = passwordController.text;
if (email.isEmpty) {
return !setError('The email address is required');
}
if (!email.isEmail) {
return !setError('The email address is inviled');
}
if (pass.isEmpty) {
return !setError('The password is required');
}
return true;
}
login() async {
setError('');
update(() => isLoading = true);
final state = validate();
if(Get.find<AuthProvider>().isAuth) {
Get.offAllNamed('/');
return;
}
if (state) {
TextInput.finishAutofillContext();
await Future.delayed(const Duration(seconds: 1), () async {
await Get.find<AuthProvider>().toggleAuth(true);
Get.offAllNamed('/');
});
initializeProviders();
}
update(() => isLoading = false);
}
togglePass() {
if (mounted) setState(() => isVisible = !isVisible);
}
bool setError(String error) {
final alerter = Get.find<AlertProvider>();
alerter.setAlert(
error,
type: AlertType.danger,
dismissable: false,
);
if (mounted) {
setState(() {});
}
if (error.isEmpty) return false;
return true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ResponsiveWidget(
builder: (cnx, screen) => Container(
width: double.infinity,
height: double.infinity,
decoration: !screen.isMobile
? BoxDecoration(
image: DecorationImage(
image: AssetImage(imgUrl('login.jpg')),
fit: BoxFit.cover,
),
)
: null,
child: Center(
child: SingleChildScrollView(
physics:
screen.isBig ? const NeverScrollableScrollPhysics() : null,
child: Container(
width: !screen.isMobile ? 550 : null,
decoration: BoxDecoration(
color: !screen.isMobile ? Colors.white : null,
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 50),
Text(
'Welcome Back',
style:
Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
color: const Color(0xff0f0f0f),
),
),
const SizedBox(height: 50),
GetBuilder<AlertProvider>(
init: AlertProvider(),
builder: (alerter) {
return Padding(
padding: const EdgeInsets.only(
bottom: 20,
left: 20,
right: 20,
),
child: alerter.alert ?? const SizedBox(),
);
},
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: AutofillGroup(
child: Column(
children: [
TextField(
controller: emailController,
autofocus: true,
autofillHints: const [AutofillHints.email],
decoration: InputDecoration(
labelText: 'Email',
icon: SizedBox(
width: 20,
child: svg(
'profile',
color: Colors.grey.shade800,
),
),
filled: true,
fillColor: const Color(0xFFf0f1f5),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
),
onChanged: (value) => setError(''),
onEditingComplete:
TextInput.finishAutofillContext,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
onSubmitted: (_) => login(),
),
const SizedBox(height: 20),
TextField(
autofillHints: const [AutofillHints.password],
controller: passwordController,
autofocus: false,
obscureText: !isVisible,
decoration: InputDecoration(
labelText: 'Password',
icon: SizedBox(
width: 20,
child: svg(
'password',
color: Colors.grey.shade800,
),
),
suffixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10),
child: IconButton(
onPressed: togglePass,
icon: isVisible
? svg('eye.slash.fill',
size: 16,
color: Colors.grey.shade800)
: svg('eye.fill',
size: 15,
color: Colors.grey.shade800),
),
),
filled: true,
fillColor: const Color(0xFFf0f1f5),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
),
onChanged: (value) => setError(''),
onEditingComplete:
TextInput.finishAutofillContext,
textInputAction: TextInputAction.go,
keyboardType: TextInputType.visiblePassword,
onSubmitted: (_) => login(),
),
const SizedBox(height: 50),
SizedBox(
width: 300,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
onPressed: isLoading ? null : login,
child: AnimatedCrossFade(
crossFadeState: !isLoading
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: const Duration(milliseconds: 300),
alignment: Alignment.center,
firstChild: const Text('Login'),
secondChild: const Center(
child: SimpleLoader(
strokeWidth: 3,
size: 20,
),
),
),
),
)
],
),
),
),
const SizedBox(height: 50, width: 200),
],
),
),
),
),
),
),
);
}
}
@@ -0,0 +1,176 @@
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:u_vineyard/template/src/models/dashboard_models/dashboard_card.model.dart';
import 'package:u_vineyard/template/src/providers/auth.provider.dart';
import 'package:u_vineyard/widgets/map.widget.dart';
import 'package:flutter/material.dart';
import '../../widgets/data_tables_widgets/data_table_card.widget.dart';
import '../../widgets/menu_list.widget.dart';
import '../../widgets/responsive_widgets/responsive_grid.widget.dart';
import '../../widgets/responsive_widgets/repesponsive_grid_item.widget.dart';
import 'dashboard_widgets/dashboard_card.widget.dart';
class DashboadScreen extends StatefulWidget {
const DashboadScreen({super.key});
@override
State<DashboadScreen> createState() => _DashboadScreenState();
}
class _VineyardDetails {
var vineyards = Get.find<AuthProvider>().profile?.owners?.expand((owner) => owner.vineyards ?? []).toList() ?? [];
int get numberOfVineyards => vineyards.length;
double get totalArea => vineyards.fold(0.0, (sum, vineyard) => sum! + (vineyard.hectares ?? 0.0)) ?? 0.0;
}
class _DashboadScreenState extends State<DashboadScreen> {
final dashboardCardModelValues = [
DashboardCardModel.fromJson({
'name': 'Vigneti',
'icon': Icons.eco_rounded,
'value': _VineyardDetails().numberOfVineyards,
}),
DashboardCardModel.fromJson({
'name': 'Ettari',
'icon': Icons.grass_rounded,
'value': _VineyardDetails().totalArea.toInt(),
'persentage': 64.1,
'persentageValue': '3.1',
'notes': 'Total 424,567 deliveries',
}),
];
//double _calculatedArea = 0.0;
@override
Widget build(BuildContext context) {
return ResponsiveGrid(
childrenBuilder: (cnx, screen) {
return [
...dashboardCardModelValues
.map(
(item) => ResponsiveGridItem(
animationIndex: dashboardCardModelValues.indexOf(item),
crossAxisCellCount: screen.setAxis(6, 9, 6, 6),
child: DashboardStatisticCard(
item,
callback: () {},
isSmall: screen.isSmall,
actions: [
if (dashboardCardModelValues.indexOf(item) == 0)
MenuListItem(
callback: () {},
svgName: 'menu',
title: 'Lista',
),
MenuListItem(
callback: () {},
svgName: 'menu',
title: 'Aggiungi',
),
],
),
),
)
.toList(),
// ResponsiveGridItem(
// animationIndex: 4,
// crossAxisCellCount: screen.setAxis(6, 9, 6, 6),
// child: CardWidget(
// height: 420,
// child: Column(
// children: [
// Row(
// children: [
// FittedBox(
// fit: BoxFit.scaleDown,
// child: SelectableText(
// 'Shot per distance',
// style: Theme.of(context)
// .textTheme
// .headlineSmall
// ?.copyWith(fontWeight: FontWeight.w700),
// ),
// ),
// ],
// ),
// const Spacer(),
// const AnimationRadialChart(),
// ],
// ),
// ),
// ),
ResponsiveGridItem(
animationIndex: 7,
crossAxisCellCount: 24,
child: MainMapWidget(),
),
// ResponsiveGridItem(
// animationIndex: 5,
// crossAxisCellCount: screen.setAxis(6, 9, 6, 6),
// child: CardWidget(
// height: 420,
// child: Column(
// children: [
// Row(
// children: [
// FittedBox(
// fit: BoxFit.scaleDown,
// child: SelectableText(
// 'Revenue',
// style: Theme.of(context)
// .textTheme
// .headlineSmall
// ?.copyWith(fontWeight: FontWeight.w700),
// ),
// ),
// const Spacer(),
// const SelectableText('Last 7 days')
// ],
// ),
// const Spacer(),
// const AnimationSplineChart(),
// ],
// ),
// ),
// ),
// ResponsiveGridItem(
// animationIndex: 12,
// crossAxisCellCount: screen.setAxis(12, 18, 24, 24),
// child: CardWidget(
// height: 420,
// child: Column(
// children: [
// Row(
// children: [
// FittedBox(
// fit: BoxFit.scaleDown,
// child: SelectableText(
// 'Revenue',
// style: Theme.of(context)
// .textTheme
// .headlineSmall
// ?.copyWith(fontWeight: FontWeight.w700),
// ),
// ),
// const Spacer(),
// const SelectableText('Last 7 days VS period week')
// ],
// ),
// const Spacer(),
// const AnimationSplineChart(),
// ],
// ),
// ),
// ),
ResponsiveGridItem(
animationIndex: 7,
crossAxisCellCount: 24,
child: DataTableCard(screen.isBig, screen.is4K, screen.isMobile),
),
const SizedBox(height: 30),
];
},
);
}
}
@@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import '../../../models/dashboard_models/dashboard_card.model.dart';
import '../../../widgets/animated_text.widget.dart';
import '../../../widgets/card.widget.dart';
import '../../../widgets/menu_list.widget.dart';
import '../../../widgets/rounded_progress_indicator.widget.dart';
class DashboardStatisticCard extends StatelessWidget {
const DashboardStatisticCard(
this.item, {
super.key,
this.isSmall = false,
this.actions = const [],
this.callback,
});
final DashboardCardModel item;
final bool isSmall;
final List<Widget> actions;
final VoidCallback? callback;
@override
Widget build(BuildContext context) {
return Hero(
tag: item,
child: CardWidget(
callback: callback,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
item.icon,
size: 20,
color: Colors.green,
),
const Spacer(),
// Uncomment and fix the widget below if needed
// RoundedProgressIndicator(
// textValue: Text(
// '+${item.persentageValue}%',
// style: Theme.of(context)
// .textTheme
// .labelSmall
// ?.copyWith(fontWeight: FontWeight.w900),
// ),
// value: (item.persentage) / 100,
// backgroundColor: Colors.grey.shade200,
// size: 50,
// stroke: 6,
// ),
],
),
const SizedBox(height: 4),
SelectableText(
item.name,
style: Theme.of(
context,
).textTheme.labelLarge?.copyWith(
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AnimatedNumber(
number: item.value,
style: Theme.of(
context,
).textTheme.displayMedium?.copyWith(
color: Colors.grey.shade900,
fontWeight: FontWeight.w900,
),
),
const SizedBox(height: 5),
// SelectableText(
// item.notes ?? '',
// maxLines: 1,
// style: Theme.of(
// context,
// ).textTheme.labelLarge?.copyWith(
// color: Colors.grey.shade600,
// fontWeight: FontWeight.w500,
// overflow: TextOverflow.ellipsis,
// ),
// ),
],
),
),
actions.isNotEmpty
? MenuList(
actions: actions,
offset: const Offset(0, 5),
builder: (controller) => IconButton.filledTonal(
onPressed: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
icon: const Icon(Icons.more_vert_rounded),
),
)
: IconButton.filledTonal(
onPressed: callback,
icon: const Icon(
Icons.arrow_forward_ios_rounded,
size: 12,
),
),
],
)
],
),
),
);
}
}
@@ -0,0 +1,101 @@
import 'dart:math';
import '../../../classes/constents.class.dart';
/// Package import
import 'package:flutter/material.dart';
/// Chart import
import 'package:syncfusion_flutter_charts/charts.dart';
/// Renders the spline chart sample with dynamically updated data points.
class AnimationSplineChart extends StatefulWidget {
/// Renders the spline chart sample with dynamically updated data points.
const AnimationSplineChart({super.key});
@override
State<AnimationSplineChart> createState() => _AnimationSplineChartState();
}
class _AnimationSplineChartState extends State<AnimationSplineChart> {
List<_ChartData>? _chartData;
List<_ChartData>? _chartData2;
@override
Widget build(BuildContext context) {
_getChartData();
return _buildAnimationSplineChart();
}
/// get the spline chart sample with dynamically updated data points.
SfCartesianChart _buildAnimationSplineChart() {
return SfCartesianChart(
plotAreaBorderWidth: 0,
primaryXAxis:
NumericAxis(majorGridLines: const MajorGridLines(width: 0)),
primaryYAxis: NumericAxis(
majorTickLines: const MajorTickLines(color: Colors.transparent),
axisLine: const AxisLine(width: 0),
minimum: 0,
maximum: 100),
series: _getDefaultSplineSeries());
}
/// get the spline series sample with dynamically updated data points.
List<SplineSeries<_ChartData, num>> _getDefaultSplineSeries() {
return <SplineSeries<_ChartData, num>>[
SplineSeries<_ChartData, num>(
dataSource: _chartData!,
xValueMapper: (_ChartData sales, _) => sales.x,
yValueMapper: (_ChartData sales, _) => sales.y,
color: primaryColor,
width: 4,
markerSettings: const MarkerSettings(
isVisible: true,
borderWidth: 5,
),
),
SplineSeries<_ChartData, num>(
dataSource: _chartData2!,
xValueMapper: (_ChartData sales, _) => sales.x,
yValueMapper: (_ChartData sales, _) => sales.y,
color: Colors.purple,
width: 4,
markerSettings: const MarkerSettings(
isVisible: true,
borderWidth: 5,
),
),
];
}
@override
void dispose() {
_chartData!.clear();
_chartData2!.clear();
super.dispose();
}
/// get the random value
int _getRandomInt(int min, int max) {
final Random random = Random();
return min + random.nextInt(max - min);
}
//Get the random data points
void _getChartData() {
_chartData = <_ChartData>[];
_chartData2 = <_ChartData>[];
for (int i = 0; i < 5; i++) {
_chartData!.add(_ChartData(i, _getRandomInt(15, 85)));
_chartData2!.add(_ChartData(i, _getRandomInt(15, 85)));
}
}
}
class _ChartData {
_ChartData(this.x, this.y);
final int x;
final int y;
}
@@ -0,0 +1,139 @@
/// Package imports
import '../../../classes/constents.class.dart';
import 'package:flutter/material.dart';
/// Chart import
import 'package:syncfusion_flutter_charts/charts.dart';
/// Render the radial bar customization.
class AnimationRadialChart extends StatefulWidget {
/// Cretaes customised radial bar series.
const AnimationRadialChart({super.key});
@override
State<AnimationRadialChart> createState() => _AnimationRadialChartState();
}
class _AnimationRadialChartState extends State<AnimationRadialChart> {
TooltipBehavior? _tooltipBehavior;
List<_ChartData>? dataSources;
List<Color>? colors;
@override
void initState() {
_tooltipBehavior =
TooltipBehavior(enable: true, format: 'point.x : point.y%');
dataSources = <_ChartData>[
_ChartData(
x: 'Vehicle',
y: 62.70,
text: '10%',
pointColor: const Color.fromRGBO(69, 186, 161, 1.0)),
_ChartData(
x: 'Education',
y: 29.20,
text: '10%',
pointColor: const Color.fromRGBO(230, 135, 111, 1.0)),
_ChartData(
x: 'Home',
y: 85.20,
text: '100%',
pointColor: const Color.fromRGBO(145, 132, 202, 1.0)),
_ChartData(
x: 'Personal',
y: 45.70,
text: '100%',
pointColor: const Color.fromRGBO(235, 96, 143, 1.0))
];
colors = const <Color>[
Color.fromRGBO(69, 186, 161, 1.0),
Color.fromRGBO(230, 135, 111, 1.0),
Color.fromRGBO(145, 132, 202, 1.0),
Color.fromRGBO(235, 96, 143, 1.0)
];
super.initState();
}
@override
Widget build(BuildContext context) {
return _buildCustomizedRadialBarChart();
}
/// Return the circular chart with radial customization.
SfCircularChart _buildCustomizedRadialBarChart() {
return SfCircularChart(
title: ChartTitle(text: ''),
series: _getRadialBarCustomizedSeries(),
tooltipBehavior: _tooltipBehavior,
);
}
/// Returns radial bar which need to be customized.
List<RadialBarSeries<_ChartData, String>> _getRadialBarCustomizedSeries() {
return <RadialBarSeries<_ChartData, String>>[
RadialBarSeries<_ChartData, String>(
animationDuration: 0,
maximumValue: 100,
gap: '10%',
radius: '100%',
dataSource: <_ChartData>[
_ChartData(
x: 'Vehicle',
y: 62.70,
text: '100%',
pointColor: Colors.redAccent,
),
_ChartData(
x: 'Education',
y: 29.20,
text: '100%',
pointColor: Colors.orange,
),
_ChartData(
x: 'Home',
y: 85.20,
text: '100%',
pointColor: Colors.indigoAccent,
),
_ChartData(
x: 'Personal',
y: 45.70,
text: '100%',
pointColor: primaryColor,
)
],
cornerStyle: CornerStyle.bothCurve,
innerRadius: '50%',
xValueMapper: (_ChartData data, _) => data.x,
yValueMapper: (_ChartData data, _) => data.y,
pointRadiusMapper: (_ChartData data, _) => data.text,
/// Color mapper for each bar in radial bar series,
/// which is get from datasource.
pointColorMapper: (_ChartData data, _) => data.pointColor,
legendIconType: LegendIconType.circle,
),
];
}
@override
void dispose() {
dataSources!.clear();
super.dispose();
}
}
class _ChartData {
_ChartData({
required this.x,
required this.y,
required this.text,
required this.pointColor,
});
final String x;
final double y;
final String text;
final Color pointColor;
}
@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../providers/auth.provider.dart';
class Error404Screen extends StatelessWidget {
const Error404Screen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: ElevatedButton(
onPressed: Get.find<AuthProvider>().logout,
child: const Text('logout'),
),
),
const Text('error NOT found'),
],
),
);
}
}
@@ -0,0 +1,83 @@
import '../../providers/alert.provider.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../classes/constents.class.dart';
import '../../providers/routes.provider.dart';
import '../../widgets/app_widgets/app_breadcamp.widget.dart';
import '../../widgets/app_widgets/app_drawer.widget.dart';
import '../../widgets/app_widgets/app_navbar.widget.dart';
import '../../widgets/notifications_widgets/toast.widget.dart';
import '../../widgets/responsive_widgets/responsive.widget.dart';
class MainScreen extends StatefulWidget {
static const route = '/';
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
@override
Widget build(BuildContext context) {
return ResponsiveWidget(
builder: (context, screen) => GetBuilder<RoutesProvider>(
init: RoutesProvider(),
builder: (router) {
return Scaffold(
key: router.mainKey,
appBar: const AppNarBar(),
drawer: !screen.isBig ? const AppDrawer() : null,
body: Stack(
children: [
Positioned.fill(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (screen.isBig) const AppDrawer(),
Expanded(
child: Column(
children: [
GetBuilder<AlertProvider>(
builder: (alerter) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10,
horizontal:
(Get.width - 304) > maxContainerWidth
? (((Get.width - 304) -
maxContainerWidth) /
2) +
2
: 20,
),
child: alerter.alert ?? const SizedBox(),
);
},
),
if (showBreadCamp) const BreadCrumbWidget(),
Expanded(
child: router.currentWidget.call(),
),
],
),
),
],
),
),
const Positioned(
top: 0,
right: 10,
width: 400,
child: Toasts(),
),
],
),
);
},
),
);
}
}
@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import '../../models/general_search.model.dart';
class GeneralSerch extends StatelessWidget {
static const route = '/search';
const GeneralSerch(this.result, {super.key});
final List<GeneralSearchModel> result;
@override
Widget build(BuildContext context) {
return Text(result.toString());
}
}
@@ -0,0 +1,91 @@
import '../providers/auth.provider.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../providers/routes.provider.dart';
import 'menu_list.widget.dart';
class AccountWidget extends StatelessWidget {
const AccountWidget({
required this.desktop,
super.key,
});
final bool desktop;
@override
Widget build(BuildContext context) {
return MenuList(
actions: [
MenuListItem(
callback: () => Routes.toNamed('/settings/account'),
svgName: 'profile',
title: 'Profile',
),
MenuListItem(
callback: Get.find<AuthProvider>().logout,
svgName: 'logout',
title: 'Logout',
color: Colors.redAccent,
),
],
builder: (controller) => GestureDetector(
onTap: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
child: Container(
width: desktop ? 200 : null,
padding: const EdgeInsets.symmetric(horizontal: 7),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(30)),
color: desktop ? Colors.grey.shade100 : null,
),
child: GetBuilder<AuthProvider>(builder: (provider) {
return desktop
? Row(
children: [
const SizedBox(width: 7),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
provider.profile?.username ?? '',
style: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(fontWeight: FontWeight.bold),
),
Text(
provider.profile!.role?.name ?? '',
style: Theme.of(context).textTheme.labelSmall,
),
],
),
),
const Icon(Icons.keyboard_arrow_down_rounded)
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.account_circle_rounded, size: 30),
const SizedBox(width: 10),
Text(
provider.profile?.username ?? '',
style: Theme.of(context).textTheme.labelMedium,
),
],
);
}),
),
),
);
}
}
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
class AnimatedNumber extends StatefulWidget {
const AnimatedNumber({
Key? key,
required this.number,
this.separator = 3,
this.style,
}) : super(key: key);
final int number;
final int separator;
final TextStyle? style;
@override
State<AnimatedNumber> createState() => _AnimatedNumberState();
}
class _AnimatedNumberState extends State<AnimatedNumber>
with SingleTickerProviderStateMixin {
late Animation<int> _animation;
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
);
_animation = IntTween(begin: 0, end: widget.number).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.fastOutSlowIn,
));
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedTextValue(
animation: _animation,
separator: widget.separator,
style: widget.style,
);
}
}
class AnimatedTextValue extends AnimatedWidget {
final Animation<int> animation;
const AnimatedTextValue({
Key? key,
required this.animation,
required this.separator,
this.style,
}) : super(key: key, listenable: animation);
final int separator;
final TextStyle? style;
_separatValue(int value) {
if (separator > 0 && value.toString().length > separator) {
final String valueString = value.toString();
final int valueLength = valueString.length;
final int separatorIndex = valueLength - separator;
final String valueBeforeSeparator =
valueString.substring(0, separatorIndex);
final String valueAfterSeparator = valueString.substring(separatorIndex);
return '$valueBeforeSeparator,$valueAfterSeparator';
}
return value.toString();
}
@override
Widget build(BuildContext context) {
return SelectableText(
_separatValue(animation.value),
maxLines: 1,
style: style ??
const TextStyle(
fontSize: 26,
height: 1.4,
fontWeight: FontWeight.w900,
),
);
}
}
@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:get/get.dart';
import '../../classes/constents.class.dart';
import '../../providers/routes.provider.dart';
class BreadCrumbWidget extends StatelessWidget {
const BreadCrumbWidget({this.onBreadTap, super.key});
final void Function(String)? onBreadTap;
@override
Widget build(BuildContext context) {
return GetBuilder<RoutesProvider>(
builder: (router) {
final items = router.current.trim().replaceFirst('/', '').split('/');
return Visibility(
visible: items.isNotEmpty,
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
),
child: Row(
children: [
Expanded(
child: BreadCrumb(
items: items
.map(
(e) => BreadCrumbItem(
padding: const EdgeInsets.all(5),
borderRadius:
const BorderRadius.all(Radius.circular(5)),
onTap: onBreadTap == null
? null
: () {
final route = items
.getRange(0, items.indexOf(e) + 1)
.toList()
.join('/');
onBreadTap?.call('/$route');
},
content: Text(
e.capitalize.toString(),
style: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(
fontWeight: FontWeight.w500,
color: onBreadTap == null
? Colors.grey.shade600
: primaryColor,
),
),
),
)
.toList(),
divider: Text(
' / ',
style: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Colors.grey),
),
overflow: const WrapOverflow(
keepLastDivider: false,
direction: Axis.horizontal,
),
),
),
],
),
),
);
},
);
}
}
@@ -0,0 +1,249 @@
import '../../classes/functions.class.dart';
import '../../providers/routes.provider.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../classes/constents.class.dart';
import '../../classes/enums.class.dart';
import '../../classes/routes.class.dart';
import '../../models/app_models/drawer_item.model.dart';
import 'app_logo.widget.dart';
import '../responsive_widgets/responsive.widget.dart';
class AppDrawer extends StatefulWidget {
const AppDrawer({super.key});
@override
State<AppDrawer> createState() => _AppDrawerState();
}
class _AppDrawerState extends State<AppDrawer> with TickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _animation;
final provider = Get.find<RoutesProvider>();
bool isLoading = true;
@override
void initState() {
_controller = AnimationController(
duration: const Duration(milliseconds: 275),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
);
provider.addListener(toggle);
if (provider.currentMenu.children.isNotEmpty) {
_controller.forward();
}
super.initState();
}
final scrollController = ScrollController();
toggle() {
if (provider.currentMenu.children.isNotEmpty) {
_controller.forward();
}
if (provider.currentMenu.children.isEmpty) {
_controller.reverse();
}
}
@override
void dispose() {
provider.removeListener(toggle);
// _controller.dispose();
// TODO:: find a sloution to dispose _controller
scrollController.dispose();
super.dispose();
}
List<Widget> getMenu(DrawerItem item) {
if (item.type == DrawerItemType.menu) {
final children = item.children;
return children.map(getItem).toList();
}
return [];
}
Widget getItem(DrawerItem item, {String? parent, bool sub = true}) {
if (item.type == DrawerItemType.menu) {
final children = item.children;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 1,
),
child: ExpansionTile(
key: GlobalKey(),
leading: svg(
item.icon,
size: sub ? 15 : 20,
color: item.isOpen ? Colors.blueGrey.shade800 : Colors.blueGrey,
),
title: Text(
item.name,
selectionColor: Colors.grey.shade900,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: item.isOpen ? FontWeight.w600 : FontWeight.w500,
fontSize: sub ? 14 : null,
),
),
// childrenPadding: const EdgeInsets.only(left: 10),
initiallyExpanded: item.isOpen,
backgroundColor: Colors.grey.withOpacity(.05),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
collapsedShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
children: children.map((v) => getItem(v, sub: true)).toList(),
),
);
}
final selected = provider.current == item.route;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 3),
child: ListTile(
key: ValueKey(item),
title: Text(item.name),
dense: sub,
leading: svg(
item.icon,
size: sub ? 15 : 20,
color: selected ? Colors.blueGrey.shade800 : Colors.blueGrey,
),
selectedColor: Colors.grey.shade900,
hoverColor: Colors.grey.withOpacity(.1),
selected: selected,
selectedTileColor: Colors.grey.withOpacity(.2),
shape: ContinuousRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
style: ListTileStyle.drawer,
onTap: () {
Routes.toNamed(
item.route,
duplicate: true,
);
// closeDrawerMenu(item.route);
if (provider.isDrawerOpen) {
provider.closeDrawer();
}
},
),
);
}
@override
Widget build(BuildContext context) {
return ResponsiveWidget(builder: (context, screen) {
return GetBuilder<RoutesProvider>(builder: (provider) {
return Row(
children: [
SizedBox(
width: 92,
child: NavigationRail(
onDestinationSelected: (value) {
final item = drawerMenu[value];
if (item.type == DrawerItemType.link) {
Routes.toNamed(
item.route,
duplicate: true,
);
if (provider.isDrawerOpen) {
provider.closeDrawer();
}
}
provider.currentIndex = value;
provider.update();
// change(value);
},
useIndicator: true,
indicatorColor: Colors.white,
backgroundColor: primaryColor,
labelType: NavigationRailLabelType.all,
unselectedLabelTextStyle: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Colors.white70),
selectedLabelTextStyle: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Colors.white),
indicatorShape: ContinuousRectangleBorder(
side:
BorderSide(color: Colors.grey.withOpacity(.2), width: 2),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
leading: !screen.isBig
? GestureDetector(
onTap: provider.closeDrawer,
child: const Padding(
padding: EdgeInsets.all(20),
child: Logo(color: Colors.white),
),
)
: null,
destinations: [
...drawerMenu
.map<NavigationRailDestination>(
(item) => NavigationRailDestination(
padding: const EdgeInsets.all(10),
icon: svg(
item.icon,
size: 20,
color: Colors.white70,
),
selectedIcon: svg(
item.icon,
size: 20,
),
label: Text(item.name),
),
)
.toList(),
],
selectedIndex: provider.currentIndex,
),
),
SizeTransition(
sizeFactor: _animation,
axis: Axis.horizontal,
axisAlignment: -1,
child: Drawer(
backgroundColor: drawerColor,
shadowColor: Colors.black38,
elevation: 5,
shape: const ContinuousRectangleBorder(),
child: Padding(
padding: EdgeInsets.only(top: screen.isMobile ? 90 : 10),
child: GetBuilder<RoutesProvider>(
builder: (provider) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Scrollbar(
controller: scrollController,
child: ListView(
controller: scrollController,
children: getMenu(provider.currentMenu),
),
),
);
},
),
),
),
)
],
);
});
});
}
}
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import '../../classes/functions.class.dart';
class Logo extends StatelessWidget {
const Logo({this.color, super.key});
final Color? color;
@override
Widget build(BuildContext context) {
return svg('logo', size: 35, color: color);
}
}
@@ -0,0 +1,103 @@
import '../../widgets/responsive_widgets/responsive.widget.dart';
import 'package:flutter/material.dart';
import '../../classes/constents.class.dart';
import '../../classes/functions.class.dart';
import '../account.widget.dart';
import '../navbar_buttom.widget.dart';
import 'app_logo.widget.dart';
import 'app_search.widget.dart';
class AppNarBar extends StatelessWidget implements PreferredSizeWidget {
const AppNarBar({
this.title = '',
super.key,
});
final String title;
@override
Size get preferredSize => const Size.fromHeight(80);
@override
Widget build(BuildContext context) {
return ResponsiveWidget(builder: (context, screen) {
return AppBar(
elevation: 5,
shadowColor: Colors.black26,
scrolledUnderElevation: 5,
leadingWidth: 92,
toolbarHeight: preferredSize.height,
backgroundColor: navbarColor,
centerTitle: true,
leading: !screen.isBig
? Container(
width: 92,
padding: const EdgeInsets.all(12),
child: IconButton.filledTonal(
icon: const Icon(Icons.menu),
style: IconButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: primaryColor,
shadowColor: Colors.black26,
elevation: 5,
shape: const CircleBorder(),
),
onPressed: () {
Scaffold.of(context).openDrawer();
},
tooltip:
MaterialLocalizations.of(context).openAppDrawerTooltip,
),
)
: Container(
color: primaryColor,
child: const Padding(
padding: EdgeInsets.all(15),
child: Logo(color: Colors.white),
),
),
title: Row(
children: [
const Expanded(
child: Center(
child: SizedBox(
width: 900,
child: AppSearch(),
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (!screen.isMobile)
SizedBox(
height: 50,
child: NavBarButtonWidget(
desktop: screen.isBig,
text: 'Show Model',
icon: Icons.add_rounded,
onTap: () => showModel('test'),
),
),
const Row(
mainAxisSize: MainAxisSize.min,
children: [],
),
SizedBox(
height: 50,
child: AccountWidget(
desktop: screen.isBig,
),
),
],
),
)
],
),
);
});
}
}
@@ -0,0 +1,117 @@
import '../../providers/search.provider.dart';
import '../../widgets/simple_loader.widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../classes/constents.class.dart';
import '../../classes/shortcuts.class.dart';
class AppSearch extends StatefulWidget {
const AppSearch({super.key});
@override
State<AppSearch> createState() => _AppSearchState();
}
class _AppSearchState extends State<AppSearch> {
final provider = Get.find<SearchProvider>();
@override
Widget build(BuildContext context) {
return CallbackShortcuts(
bindings: {
escape: provider.clear,
controlF: provider.focus,
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 5),
child: TextField(
controller: provider.searchController,
focusNode: provider.node,
onTapOutside: (e) => provider.unfocus(),
onSubmitted: provider.search,
onChanged: (value) => setState(() {}),
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
decoration: InputDecoration(
hintText: 'Search',
fillColor: Colors.white,
prefixIcon: Padding(
padding: const EdgeInsets.all(15),
child: GetBuilder<SearchProvider>(
builder: (provider) {
return provider.isLoading
? const SimpleLoader(
size: 20,
strokeWidth: 2,
)
: const Icon(Icons.search_rounded);
},
),
),
suffixIcon: GetBuilder<SearchProvider>(
builder: (provider) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Visibility(
visible: provider.searchController.text.isEmpty,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
decoration: const BoxDecoration(
color: Color(0x77EFEFEF),
borderRadius: BorderRadius.all(Radius.circular(5)),
),
child: Text(
'Ctrl + F',
style:
Theme.of(context).textTheme.labelSmall?.copyWith(
fontWeight: FontWeight.w600,
color: const Color(0xFF616162),
),
),
),
),
const SizedBox(width: 15),
Visibility(
visible: provider.searchController.text.isNotEmpty,
child: InkWell(
onTap: provider.clear,
child: const Icon(
Icons.cancel_rounded,
color: primaryColor,
),
),
),
Visibility(
visible: provider.searchController.text.isNotEmpty,
child: const SizedBox(width: 15),
),
],
);
},
),
filled: true,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: primaryColor.withOpacity(.1),
width: 2,
),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: primaryColor,
width: 2,
),
borderRadius: BorderRadius.all(Radius.circular(10)),
),
),
),
),
);
}
}
+37
View File
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import '../classes/constents.class.dart';
class CardWidget extends StatelessWidget {
const CardWidget({
required this.child,
this.callback,
this.width = 300,
this.height,
super.key,
});
final Widget child;
final VoidCallback? callback;
final double width;
final double? height;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: callback,
child: Container(
width: width,
height: height,
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(30),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
boxShadow: cardShadow,
color: Colors.white,
),
child: child,
),
);
}
}
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
typedef WidgetChildBuilder = Widget Function(
BuildContext context,
Widget child,
);
class ChildBuilder extends StatelessWidget {
/// Creates a widget that delegates its build to a callback.
///
/// The [builder] argument must not be null.
const ChildBuilder({
super.key,
required this.builder,
required this.child,
});
/// Called to obtain the child widget.
///
/// This function is called whenever this widget is included in its parent's
/// build and the old widget (if any) that it synchronizes with has a distinct
/// object identity. Typically the parent's build method will construct
/// a new tree of widgets and so a new Builder child will not be [identical]
/// to the corresponding old one.
final WidgetChildBuilder builder;
final Widget child;
@override
Widget build(BuildContext context) => builder(context, child);
}
@@ -0,0 +1,159 @@
import '../../classes/constents.class.dart';
import '../../widgets/responsive_widgets/responsive.widget.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
// ignore: depend_on_referenced_packages
import 'package:syncfusion_flutter_core/theme.dart';
import '../../classes/functions.class.dart';
import '../../models/data_table_models/grid_column.model.dart';
class DataTableWidget extends StatelessWidget {
const DataTableWidget(this.columnNames, {required this.dataRows, super.key});
final List<GridColumnModel> columnNames;
final List<DataGridRow> dataRows;
@override
Widget build(BuildContext context) {
return ResponsiveWidget(builder: (context, screen) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueGrey.withOpacity(.1),
),
),
child: SfDataGridTheme(
data: SfDataGridThemeData(
rowHoverColor: primaryColor.withOpacity(.1),
headerHoverColor: primaryColor.withOpacity(.1),
sortIcon: const Icon(
Icons.arrow_downward_rounded,
size: 20,
color: Colors.blueGrey,
),
filterIcon: const Icon(
Icons.filter_alt_rounded,
size: 20,
color: Colors.blueGrey,
),
),
child: SfDataGrid(
rowsPerPage: 30,
allowFiltering: true,
allowSorting: true,
columnWidthMode: ColumnWidthMode.fill,
rowHeight: 70,
gridLinesVisibility: GridLinesVisibility.horizontal,
headerGridLinesVisibility: GridLinesVisibility.both,
source: TableDataList(
context,
data: dataRows,
isSmallScreen: screen.isSmall,
),
// tableSummaryRows: [
// GridTableSummaryRow(
// showSummaryInRow: true,
// title: 'Total: {Count} User',
// columns: [
// const GridSummaryColumn(
// name: 'Count',
// columnName: 'ID',
// summaryType: GridSummaryType.count,
// )
// ],
// position: GridTableSummaryRowPosition.bottom,
// )
// ],
columns: columnNames
.map(
(e) => GridColumn(
columnName: e.name,
width: e.width,
allowFiltering: e.allowFiltering,
allowSorting: e.allowSorting,
columnWidthMode: e.columnWidthMode,
filterIconPadding: EdgeInsets.zero,
label: Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: FittedBox(
child: Text(
e.name,
style:
Theme.of(context).textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.w800,
color: Colors.blueGrey,
),
),
),
),
),
)
.toList(),
),
),
);
});
}
}
class TableDataList<T> extends DataGridSource {
TableDataList(
BuildContext cnx, {
required List<DataGridRow> data,
bool isSmallScreen = false,
}) {
context = cnx;
_usersList = data;
}
List<DataGridRow> _usersList = [];
late BuildContext context;
@override
List<DataGridRow> get rows => _usersList;
@override
Widget? buildTableSummaryCellWidget(
GridTableSummaryRow summaryRow,
GridSummaryColumn? summaryColumn,
RowColumnIndex rowColumnIndex,
String summaryValue) {
return Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.all(15.0),
child: SelectableText(
summaryValue,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w700,
color: Colors.blueGrey,
),
),
);
}
@override
DataGridRowAdapter? buildRow(DataGridRow row) {
return DataGridRowAdapter(
cells: row.getCells().map<Widget>((dataGridCell) {
return Container(
width: double.infinity,
alignment: Alignment.center,
padding: const EdgeInsets.all(15.0),
child: dataGridCell.value is Widget
? dataGridCell.value
: dataGridCell.value is DateTime
? SelectableText(
formatDate(dataGridCell.value as DateTime),
style: Theme.of(context).textTheme.labelMedium,
)
: SelectableText(
dataGridCell.value.toString(),
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color:
dataGridCell.value is int ? Colors.blueGrey : null),
),
);
}).toList());
}
}
@@ -0,0 +1,295 @@
import '../../classes/constents.class.dart';
import '../../classes/functions.class.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
import '../../../demo.data.dart';
import '../../classes/enums.class.dart';
import '../../models/data_table_models/grid_column.model.dart';
import '../card.widget.dart';
import '../dialogs/add_user.dialog.dart';
import '../menu_list.widget.dart';
import '../simple_loader.widget.dart';
import 'data_table.widget.dart';
import 'data_table_search.widget.dart';
class DataTableCard extends StatefulWidget {
const DataTableCard(this.isBig, this.is4K, this.isMobile, {super.key});
final bool isBig;
final bool is4K;
final bool isMobile;
@override
State<DataTableCard> createState() => _DataTableCardState();
}
class _DataTableCardState extends State<DataTableCard> {
bool isLoadingTable = true;
List<UsersModel> users = [];
List<UsersModel> filteredUsers = [];
final searchController = TextEditingController();
void set(VoidCallback func) => mounted ? setState(func) : null;
void onTableSearch(String value) {
if (value.isEmpty) {
filteredUsers = users.toList();
} else {
filteredUsers = users.where((e) {
return e.id == value ||
e.name.toLowerCase().contains(value.toLowerCase()) ||
e.email.toLowerCase().contains(value.toLowerCase()) ||
e.phone.toLowerCase().contains(value.toLowerCase());
}).toList();
}
set(() {});
}
Future<void> getTableData() async {
set(() => isLoadingTable = true);
await Future.delayed(const Duration(seconds: 1), () {
users = demoUsersList;
filteredUsers = demoUsersList;
set(() => isLoadingTable = false);
});
}
@override
void initState() {
getTableData();
super.initState();
}
@override
void dispose() {
searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CardWidget(
height: 700,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
FittedBox(
fit: BoxFit.scaleDown,
child: SelectableText(
'Users',
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.w700),
),
),
Row(
children: [
if (!widget.isMobile)
DataTableSearchWidget(
onChange: onTableSearch,
controller: searchController,
hint: 'Search Name, Email, Phone',
),
const SizedBox(width: 15),
IconButton.filledTonal(
onPressed: () {},
tooltip: 'Export CSV',
splashRadius: 25,
icon: const Icon(Icons.ios_share_rounded, size: 20),
),
const SizedBox(width: 15),
IconButton.filledTonal(
onPressed: getTableData,
tooltip: 'Refresh',
splashRadius: 25,
icon: isLoadingTable
? const SimpleLoader(
size: 20,
strokeWidth: 2,
)
: const Icon(Icons.refresh_rounded, size: 20),
),
const SizedBox(width: 15),
const IconButton.filledTonal(
onPressed: showAddUserDialog,
tooltip: 'Add User',
splashRadius: 25,
icon: Icon(Icons.add_rounded, size: 20),
),
],
),
],
),
const SizedBox(height: 20),
Expanded(
child: isLoadingTable
? const Center(child: SimpleLoader())
: DataTableWidget(
[
GridColumnModel(
name: '#',
width: 70,
allowFiltering: false,
allowSorting: false,
),
GridColumnModel(name: 'Name'),
if (widget.isBig) GridColumnModel(name: 'Date of birth'),
if (widget.isBig) GridColumnModel(name: 'Gender'),
GridColumnModel(name: 'Email'),
GridColumnModel(name: 'Phone'),
GridColumnModel(
name: 'Status',
allowFiltering: false,
allowSorting: false,
width: 120,
),
GridColumnModel(
name: 'Actions',
allowFiltering: false,
allowSorting: false,
columnWidthMode: ColumnWidthMode.lastColumnFill,
),
],
dataRows: filteredUsers
.map<DataGridRow>(
(user) => DataGridRow(
cells: [
DataGridCell<Widget>(
columnName: '#',
value: CircleAvatar(
radius: 35,
backgroundImage: NetworkImage(user.avatar),
),
),
DataGridCell<String>(
columnName: 'Name', value: user.name),
if (widget.isBig)
DataGridCell<DateTime>(
columnName: 'Date of birth',
value: user.dateOfBirth,
),
if (widget.isBig)
DataGridCell<String>(
columnName: 'Gender', value: user.gender),
DataGridCell<String>(
columnName: 'Email', value: user.email),
DataGridCell<String>(
columnName: 'Phone', value: user.phone),
DataGridCell<Widget>(
columnName: 'Status',
value: SizedBox(
height: 30,
child: FittedBox(
child: CupertinoSwitch(
value: user.status,
activeColor: primaryColor,
onChanged: (value) {
user.status = value;
set(() {});
toast(
'User "${user.name}" has been ${value ? 'Activated' : 'Deactivated'}',
type: value
? ToastType.success
: ToastType.warning,
);
},
),
),
),
),
DataGridCell<Widget>(
columnName: 'Actions',
value: widget.is4K
? Row(
mainAxisSize: MainAxisSize.min,
children: [
FittedBox(
child: IconButton(
onPressed: () {},
tooltip: 'Details',
splashRadius: 25,
icon: const Icon(
Icons.person_outline,
color: Colors.blueGrey,
size: 20),
),
),
const SizedBox(width: 10),
FittedBox(
child: IconButton(
onPressed: () {},
tooltip: 'Edit',
splashRadius: 25,
icon: const Icon(
Icons.edit_outlined,
color: Colors.blueGrey,
size: 20),
),
),
const SizedBox(width: 10),
FittedBox(
child: IconButton(
onPressed: () {},
tooltip: 'Delete Account',
splashRadius: 25,
icon: const Icon(
Icons.delete_outline_rounded,
color: Colors.red,
size: 20),
),
),
],
)
: MenuList(
actions: [
MenuListItem(
callback: () => print(user.id),
svgName: 'profile',
title: 'User Details',
),
MenuListItem(
callback: () => print(user.name),
svgName: 'menu',
title: 'Details',
),
MenuListItem(
callback: () => print(user.id),
title: 'Remove',
svgName: 'logout',
color: Colors.redAccent,
),
],
offset: const Offset(0, 5),
builder: (controller) => IconButton(
onPressed: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
icon: const Icon(
Icons.more_vert_rounded),
),
),
),
],
),
)
.toList(),
),
),
],
),
);
}
}
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import '../../classes/constents.class.dart';
import '../../classes/shortcuts.class.dart';
class DataTableSearchWidget extends StatelessWidget {
const DataTableSearchWidget(
{required this.controller, required this.onChange, this.hint, super.key});
final TextEditingController controller;
final void Function(String) onChange;
final String? hint;
clear() {
controller.clear();
onChange('');
}
@override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(maxWidth: 400),
padding: const EdgeInsets.symmetric(horizontal: 5),
child: CallbackShortcuts(
bindings: {escape: clear},
child: TextField(
controller: controller,
onSubmitted: onChange,
onChanged: onChange,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
decoration: InputDecoration(
hintText: hint ?? 'Search',
hintStyle: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Colors.blueGrey),
fillColor: Colors.white,
prefixIcon: const Padding(
padding: EdgeInsets.all(15),
child: Icon(Icons.search_rounded),
),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
Visibility(
visible: controller.text.isNotEmpty,
child: InkWell(
onTap: clear,
child: const Icon(
Icons.cancel_rounded,
color: primaryColor,
),
),
),
Visibility(
visible: controller.text.isNotEmpty,
child: const SizedBox(width: 15),
),
],
),
filled: true,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: primaryColor.withOpacity(.1),
width: 2,
),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: primaryColor,
width: 2,
),
borderRadius: BorderRadius.all(Radius.circular(10)),
),
),
),
),
);
}
}
@@ -0,0 +1,46 @@
import '../../classes/constents.class.dart';
import '../../classes/enums.class.dart';
import '../../classes/functions.class.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
Future<bool> showAddUserDialog() async {
bool ifSuccess() {
Get.back();
alert(
'User has been added !!',
type: AlertType.success,
autoDismissAfter: const Duration(seconds: 4),
);
return true;
}
bool ifFiled() {
return false;
}
return await Get.defaultDialog<bool>(
title: 'Add User',
backgroundColor: Colors.white,
buttonColor: primaryColor,
content: const AddUserDialogWidget(),
onConfirm: ifSuccess,
onCancel: ifFiled,
).then((value) {
return value ?? false ? ifSuccess() : ifFiled();
});
}
class AddUserDialogWidget extends StatelessWidget {
const AddUserDialogWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 1000,
height: Get.height - 300,
color: Colors.white,
child: const SingleChildScrollView(),
);
}
}
@@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import '../classes/functions.class.dart';
class MenuList extends StatelessWidget {
const MenuList({
required this.builder,
required this.actions,
this.offset,
super.key,
});
final Widget Function(MenuController) builder;
final List<Widget> actions;
final Offset? offset;
@override
Widget build(BuildContext context) {
return MenuAnchor(
alignmentOffset: offset ?? const Offset(0, 15),
menuChildren: actions,
builder: (_, controller, __) => builder(controller),
style: MenuStyle(
shape: MaterialStateProperty.all(
const ContinuousRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
alignment: Alignment.bottomLeft,
),
);
}
}
class MenuListItem extends StatelessWidget {
const MenuListItem({
required this.title,
required this.svgName,
this.width = 200,
this.callback,
this.color,
super.key,
});
final double width;
final VoidCallback? callback;
final String title;
final String svgName;
final Color? color;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: MenuItemButton(
style: MenuItemButton.styleFrom(
backgroundColor: Colors.white,
),
requestFocusOnHover: false,
onPressed: callback,
trailingIcon: svg(svgName, size: 16, color: color ?? Colors.blueGrey),
child: Text(
title,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(color: color ?? Colors.blueGrey.shade900),
),
),
);
}
}
@@ -0,0 +1,55 @@
import '../classes/constents.class.dart';
import 'package:flutter/material.dart';
class NavBarButtonWidget extends StatelessWidget {
const NavBarButtonWidget({
required this.desktop,
required this.onTap,
required this.icon,
required this.text,
super.key,
});
final bool desktop;
final VoidCallback onTap;
final IconData icon;
final String text;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
child: desktop
? ElevatedButton.icon(
onPressed: onTap,
icon: Icon(icon),
label: Text(text),
style: ElevatedButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
),
)
: SizedBox(
width: 45,
child: Tooltip(
message: text,
child: IconButton.filled(
onPressed: onTap,
icon: Icon(icon),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: primaryColor,
shadowColor: Colors.black26,
elevation: 5,
shape: const CircleBorder(),
// shape: const ContinuousRectangleBorder(
// borderRadius: BorderRadius.all(Radius.circular(10)),
// ),
),
),
),
),
);
}
}
@@ -0,0 +1,168 @@
import '../../classes/constents.class.dart';
import '../../providers/alert.provider.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../classes/enums.class.dart';
class Alert extends StatefulWidget {
const Alert(
this.message, {
this.type = AlertType.primary,
this.autoDismissAfter,
this.dismissible = false,
this.onDismiss,
super.key,
});
final String message;
final AlertType type;
final bool dismissible;
final Duration? autoDismissAfter;
final VoidCallback? onDismiss;
@override
State<Alert> createState() => _AlertState();
}
class _AlertState extends State<Alert> with TickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _animation;
final provider = Get.find<AlertProvider>();
@override
void initState() {
_controller = AnimationController(
duration: const Duration(milliseconds: 275),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
);
provider.addListener(toggle);
if (provider.alert is Widget) {
_controller.forward();
}
super.initState();
}
toggle() {
if (provider.alert is Widget) {
_controller.forward();
}
if (provider.alert == null) {
_controller.reverse();
}
}
@override
void dispose() {
provider.removeListener(toggle);
_controller.dispose();
super.dispose();
}
Color getColor() {
switch (widget.type) {
case AlertType.success:
return Colors.green;
case AlertType.danger:
return Colors.red;
case AlertType.warning:
return Colors.amber;
case AlertType.info:
return Colors.blue;
default:
return primaryColor;
}
}
IconData getIcon() {
switch (widget.type) {
case AlertType.success:
return Icons.check_circle_rounded;
case AlertType.danger:
return Icons.cancel_rounded;
case AlertType.warning:
return Icons.warning_rounded;
case AlertType.info:
return Icons.info_rounded;
default:
return Icons.notifications_rounded;
}
}
void dissmiss() {
if (mounted) {
_controller.reverse().then((value) {
if (widget.onDismiss is VoidCallback) widget.onDismiss?.call();
});
}
}
@override
Widget build(BuildContext context) {
if (widget.autoDismissAfter is Duration &&
widget.autoDismissAfter!.inSeconds > 0) {
Future.delayed(widget.autoDismissAfter as Duration, dissmiss);
}
final color = getColor();
return SizeTransition(
sizeFactor: _animation,
axis: Axis.vertical,
axisAlignment: -1,
child: Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: BoxDecoration(
color: color.withOpacity(.1),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
width: double.infinity,
child: Row(
children: [
const SizedBox(width: 5),
Icon(getIcon(), color: color),
const SizedBox(width: 10),
Expanded(
child: SelectableText(
widget.message,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(color: color),
),
),
if (widget.dismissible) ...[
const SizedBox(width: 10),
InkWell(
onTap: dissmiss,
borderRadius: const BorderRadius.all(Radius.circular(5)),
radius: 25,
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
decoration: BoxDecoration(
color: color.withOpacity(.1),
borderRadius: const BorderRadius.all(Radius.circular(5)),
),
child: Text(
'clear',
style: Theme.of(context)
.textTheme
.labelSmall
?.copyWith(color: color, fontWeight: FontWeight.bold),
),
),
),
const SizedBox(width: 10),
],
],
),
),
);
}
}
@@ -0,0 +1,142 @@
import '../../providers/toast.provider.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../classes/constents.class.dart';
import '../../classes/enums.class.dart';
class ToastModel {
final String? message;
final ToastType type;
final VoidCallback? callback;
ToastModel({
this.message,
this.callback,
this.type = ToastType.primary,
});
Color get color => getColor();
IconData get icon => getIcon();
Color getColor() {
switch (type) {
case ToastType.success:
return Colors.green;
case ToastType.danger:
return Colors.red;
case ToastType.warning:
return Colors.amber;
case ToastType.info:
return Colors.blue;
default:
return primaryColor;
}
}
IconData getIcon() {
switch (type) {
case ToastType.success:
return Icons.check_circle_rounded;
case ToastType.danger:
return Icons.cancel_rounded;
case ToastType.warning:
return Icons.warning_rounded;
case ToastType.info:
return Icons.info_rounded;
default:
return Icons.notifications_rounded;
}
}
}
class Toasts extends StatefulWidget {
const Toasts({super.key});
@override
State<Toasts> createState() => _ToastsState();
}
class _ToastsState extends State<Toasts> {
final listKey = GlobalKey<AnimatedListState>();
final provider = Get.find<ToastProvider>();
@override
void initState() {
provider.setKey(listKey);
super.initState();
}
final slideInTween =
Tween<Offset>(begin: const Offset(3, 0), end: Offset.zero);
@override
Widget build(BuildContext context) {
return AnimatedList(
shrinkWrap: true,
key: listKey,
itemBuilder: (cnx, i, animation) => provider.buildItem(provider.toasts[i], animation),
);
}
}
class Toast extends StatelessWidget {
const Toast(this.item, this.dismiss, {super.key});
final ToastModel item;
final void Function(ToastModel, DissmissType) dismiss;
@override
Widget build(BuildContext context) {
final color = item.color;
if (toastAutoDissmiss) {
Future.delayed(
toastDissmissDuration, () => dismiss(item, DissmissType.auto))
.catchError((e) {});
}
return Dismissible(
key: ValueKey(item.hashCode),
onDismissed: (_) => dismiss(item, DissmissType.swipe),
confirmDismiss: (direction) async {
return direction == DismissDirection.startToEnd;
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10),
child: GestureDetector(
onTap: item.callback,
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10)),
color: Colors.white,
border: Border.fromBorderSide(
BorderSide(color: color.withOpacity(.5), width: 2)),
),
child: Row(
children: [
Icon(
item.icon,
color: color,
),
const SizedBox(width: 10),
Expanded(
child: Text(item.message ?? ''),
),
const SizedBox(width: 10),
InkWell(
onTap: () => dismiss(item, DissmissType.click),
borderRadius: const BorderRadius.all(Radius.circular(5)),
splashColor: color.withOpacity(.1),
child: Icon(
Icons.close_rounded,
size: 20,
color: color,
),
),
],
),
),
),
),
);
}
}
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import '../child_builder.widget.dart';
class ResponsiveGridItem extends StatelessWidget {
const ResponsiveGridItem({
required this.child,
this.crossAxisCellCount = 3,
this.animationIndex = 0,
super.key,
});
final Widget child;
final int crossAxisCellCount;
final int animationIndex;
@override
Widget build(BuildContext context) {
return ChildBuilder(
child: AnimationConfiguration.staggeredGrid(
position: animationIndex,
duration: const Duration(milliseconds: 375),
columnCount: 6,
child: ScaleAnimation(
child: FadeInAnimation(
child: child,
),
),
),
builder: (context, child) {
return StaggeredGridTile.fit(
crossAxisCellCount: crossAxisCellCount,
child: child,
);
},
);
}
}
@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import '../../classes/enums.class.dart';
typedef ResponsiveBuilder = Widget Function(
BuildContext context,
ScreenSize screen,
);
class ScreenSize {
final double width;
ScreenSize(this.width);
bool get is4K => width > 1600;
bool get isDesktop => width > 1024 && !is4K;
bool get isTablet => width > 768 && !isDesktop;
bool get isMobile => !isTablet && !isDesktop && !is4K;
bool get isSmall => isMobile || isTablet;
bool get isBig => isDesktop || is4K;
ScreenSizeType get screenType => calculateScreenType();
ScreenSizeType calculateScreenType() {
if (isDesktop) return ScreenSizeType.desktop;
if (isTablet) return ScreenSizeType.tablet;
return ScreenSizeType.mobile;
}
int gridAxis() {
if (is4K) return 24;
if (isDesktop) return 18;
if (isTablet) return 12;
return 6;
}
int setAxis(int? xl, int? lg, int? md, int? sm) {
xl ??= 24;
if (is4K) return xl;
if (isDesktop) return lg ?? xl;
if (isTablet) return md ?? lg ?? xl;
return sm ?? md ?? lg ?? xl;
}
}
class ResponsiveWidget extends StatelessWidget {
const ResponsiveWidget({
required this.builder,
super.key,
});
final ResponsiveBuilder builder;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
ScreenSize size = ScreenSize(constraints.maxWidth);
return builder(context, size);
},
);
}
}
@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
import '../../classes/constents.class.dart';
import 'responsive.widget.dart';
class ResponsiveGrid extends StatefulWidget {
const ResponsiveGrid({required this.childrenBuilder, super.key});
final List<Widget> Function(
BuildContext,
ScreenSize,
) childrenBuilder;
@override
State<ResponsiveGrid> createState() => _ResponsiveGridState();
}
class _ResponsiveGridState extends State<ResponsiveGrid> {
late final ScrollController controller;
@override
void initState() {
super.initState();
controller = ScrollController();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveWidget(
builder: (cnx, screen) {
return Scrollbar(
thickness: 10,
thumbVisibility: true,
controller: controller,
child: SingleChildScrollView(
controller: controller,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: (Get.width - 304) > maxContainerWidth
? (((Get.width - 304) - maxContainerWidth) / 2)
: 20,
),
child: AnimationLimiter(
child: StaggeredGrid.count(
crossAxisCount: screen.gridAxis(),
children: widget.childrenBuilder(cnx, screen),
),
),
),
),
);
},
);
}
}
@@ -0,0 +1,145 @@
import '../classes/constents.class.dart';
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math.dart' as math;
class RoundedProgressIndicator extends StatefulWidget {
const RoundedProgressIndicator({
this.textValue,
this.color = primaryColor,
this.backgroundColor = Colors.white,
this.value = 0.5,
this.size = 40,
this.stroke = 4,
super.key,
});
final Widget? textValue;
final Color color;
final Color backgroundColor;
final double value;
final double size;
final double stroke;
@override
State<RoundedProgressIndicator> createState() =>
_RoundedProgressIndicatorState();
}
class _RoundedProgressIndicatorState extends State<RoundedProgressIndicator>
with SingleTickerProviderStateMixin {
late Animation<double> _animation;
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
);
_animation =
Tween<double>(begin: 0, end: widget.value).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.fastOutSlowIn,
));
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: widget.size,
width: widget.size,
child: widget.textValue is Widget
? Stack(
children: [
Positioned.fill(
child: AnimatedRoundedProgressIndicator(
backgroundColor: widget.backgroundColor,
color: widget.color,
stroke: widget.stroke,
animation: _animation,
),
),
Positioned.fill(
child: Center(child: widget.textValue as Widget),
),
],
)
: AnimatedRoundedProgressIndicator(
backgroundColor: widget.backgroundColor,
color: widget.color,
stroke: widget.stroke,
animation: _animation,
),
);
}
}
class AnimatedRoundedProgressIndicator extends AnimatedWidget {
const AnimatedRoundedProgressIndicator({
Key? key,
this.color = primaryColor,
this.backgroundColor = Colors.white,
this.stroke = 4,
required this.animation,
}) : super(key: key, listenable: animation);
final Animation<double> animation;
final Color color;
final Color backgroundColor;
final double stroke;
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CartCircularprogress(
backColor: backgroundColor,
frontColor: color,
strokeWidth: stroke,
value: animation.value,
),
);
}
}
class CartCircularprogress extends CustomPainter {
const CartCircularprogress({
required this.backColor,
required this.frontColor,
required this.strokeWidth,
required this.value,
});
final Color backColor, frontColor;
final double strokeWidth, value;
@override
void paint(Canvas canvas, Size size) {
final w = size.width, h = size.height;
final paint1 = Paint()
..strokeWidth = strokeWidth
..color = backColor
..style = PaintingStyle.stroke;
final paint2 = Paint()
..strokeWidth = strokeWidth
..color = frontColor
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
final r =
Rect.fromCenter(center: Offset(w / 2, h / 2), width: w, height: h);
canvas.drawArc(r, math.radians(0), math.radians(360), false, paint1);
canvas.drawArc(
r, math.radians(270), math.radians(360 * value), false, paint2);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class SimpleLoader extends StatelessWidget {
const SimpleLoader({
this.color,
this.strokeWidth = 4,
this.size = 40,
super.key,
});
final Color? color;
final double strokeWidth;
final double size;
@override
Widget build(BuildContext context) {
return SizedBox(
height: size,
width: size,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(color),
strokeWidth: strokeWidth,
),
);
}
}