Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

515 lignes
23 KiB

  1. // ignore_for_file: public_member_api_docs, sort_constructors_first
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
  4. import 'package:go_router/go_router.dart';
  5. import 'package:intl/intl.dart';
  6. import 'package:provider/provider.dart';
  7. import 'package:qadirneyriz/global/global_state/global_state.dart';
  8. import 'package:qadirneyriz/setting/setting.dart';
  9. import 'package:qadirneyriz/utils/tools/tools.dart';
  10. import 'package:qadirneyriz/widgets/card_meeting.dart';
  11. import 'package:qadirneyriz/widgets/custom_appbar.dart';
  12. import 'package:qadirneyriz/widgets/custom_button.dart';
  13. import 'package:qadirneyriz/widgets/empty_widget.dart';
  14. import 'package:qadirneyriz/widgets/error_widget.dart';
  15. import 'package:qadirneyriz/widgets/today_widget.dart';
  16. import 'package:flutter_gen/gen_l10n/app_localizations.dart';
  17. import 'package:qadirneyriz/config/config.dart';
  18. import 'package:qadirneyriz/screens/home/state.dart';
  19. import 'package:qadirneyriz/utils/enums/status.dart';
  20. import 'package:qadirneyriz/widgets/loading_widget.dart';
  21. class HomeScreen extends StatefulWidget {
  22. const HomeScreen({super.key});
  23. @override
  24. State<HomeScreen> createState() => _HomeScreenState();
  25. }
  26. class _HomeScreenState extends State<HomeScreen> {
  27. @override
  28. void initState() {
  29. super.initState();
  30. HomeState homeState = Provider.of(context, listen: false);
  31. Future.delayed(Duration.zero, () async {
  32. await homeState.getTodayMeetings();
  33. });
  34. }
  35. @override
  36. Widget build(BuildContext context) {
  37. DateTime now = DateTime.now();
  38. String dateMiladi = setting.userLocalDb.getUser().language == 'fa'
  39. ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now))
  40. : DateFormat('yyyy-MM-dd').format(now);
  41. String dateJalali = Tools.convertToPersianDigits(
  42. '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}');
  43. return Consumer2<HomeState, GlobalState>(
  44. builder: (context, homeState, globalState, child) {
  45. switch (homeState.todayMettingsStatus) {
  46. case Status.ready:
  47. return RefreshIndicator(
  48. onRefresh: () async {
  49. await homeState.getTodayMeetings();
  50. },
  51. child: CustomScrollView(
  52. slivers: [
  53. CustomAppbar(),
  54. SliverToBoxAdapter(
  55. child: Padding(
  56. padding: const EdgeInsets.symmetric(
  57. horizontal: 20, vertical: 10),
  58. child: Container(
  59. decoration: BoxDecoration(
  60. color: const Color(0xffF4F9F6),
  61. boxShadow: [
  62. BoxShadow(
  63. color: config.ui.mainGray.withOpacity(.1),
  64. spreadRadius: .1,
  65. offset: const Offset(0, 5),
  66. blurRadius: 6)
  67. ],
  68. borderRadius: BorderRadius.circular(25)),
  69. width: double.infinity,
  70. child: Padding(
  71. padding: const EdgeInsets.all(20),
  72. child: Row(
  73. crossAxisAlignment: CrossAxisAlignment.start,
  74. children: [
  75. Icon(
  76. Icons.edit_outlined,
  77. color: config.ui.mainGreen,
  78. ),
  79. const SizedBox(
  80. width: 10,
  81. ),
  82. Expanded(
  83. child: Text(
  84. style: const TextStyle(fontSize: 13),
  85. homeState.todayMeetingsModel!.note ?? ''),
  86. ),
  87. ],
  88. ),
  89. ),
  90. ),
  91. ),
  92. ),
  93. SliverToBoxAdapter(
  94. child: TodayWidget(
  95. formattedDate:
  96. setting.userLocalDb.getUser().language == 'en'
  97. ? dateMiladi
  98. : dateJalali),
  99. ),
  100. SliverToBoxAdapter(
  101. child: SizedBox(
  102. height: 170,
  103. child: homeState
  104. .todayMeetingsModel!.meetings!.isNotEmpty ||
  105. homeState.todayMeetingsModel!.privateMeetings!
  106. .isNotEmpty
  107. ? ListView.builder(
  108. scrollDirection: Axis.horizontal,
  109. shrinkWrap: true,
  110. itemCount: homeState
  111. .todayMeetingsModel!.meetings!.length +
  112. homeState.todayMeetingsModel!.privateMeetings!
  113. .length,
  114. itemBuilder: (BuildContext context, int index) {
  115. // ترکیب دو لیست
  116. final meetingsLength = homeState
  117. .todayMeetingsModel!.meetings!.length;
  118. if (index < meetingsLength) {
  119. // آیتم از لیست `meetings`
  120. final meeting = homeState
  121. .todayMeetingsModel!.meetings![index];
  122. return Padding(
  123. padding: const EdgeInsets.only(
  124. right: 5, left: 1),
  125. child: Stack(
  126. children: [
  127. CustomCardMeeting(
  128. status: meeting.accepted ?? 0,
  129. titel: meeting.subject != null
  130. ? meeting.subject!.subject ?? ''
  131. : '',
  132. fromTime: meeting.azHour ?? '',
  133. toTime: meeting.taHour ?? "",
  134. location: meeting.location != null
  135. ? meeting.location!.address ??
  136. ''
  137. : '',
  138. date: meeting.dateJalali ?? '',
  139. cardId: meeting.id ?? 0,
  140. ),
  141. Positioned(
  142. left: setting.userLocalDb
  143. .getUser()
  144. .language ==
  145. 'fa'
  146. ? 20
  147. : 280,
  148. top: 20,
  149. child: Container(
  150. decoration: BoxDecoration(
  151. borderRadius:
  152. BorderRadius.circular(10),
  153. color: Colors.green,
  154. ),
  155. child: Padding(
  156. padding:
  157. const EdgeInsets.all(5.0),
  158. child: Text(
  159. AppLocalizations.of(context)!
  160. .meetings,
  161. style: TextStyle(
  162. fontSize: 12,
  163. color: Colors.white),
  164. ),
  165. ),
  166. ),
  167. ),
  168. ],
  169. ));
  170. } else {
  171. // آیتم از لیست `privateMeetings`
  172. final privateMeeting = homeState
  173. .todayMeetingsModel!
  174. .privateMeetings![index - meetingsLength];
  175. return Padding(
  176. padding: const EdgeInsets.only(
  177. right: 5, left: 1),
  178. child: Stack(
  179. children: [
  180. CustomCardMeeting(
  181. cardId: privateMeeting.id ?? -1,
  182. titel: privateMeeting.subject != null
  183. ? privateMeeting
  184. .subject!.subject ??
  185. ''
  186. : '',
  187. location:
  188. privateMeeting.location != null
  189. ? privateMeeting
  190. .location!.address ??
  191. ''
  192. : '',
  193. status: privateMeeting.accepted ?? 0,
  194. fromTime: "${privateMeeting.azHour}",
  195. date: privateMeeting.dateJalali ?? '',
  196. toTime: privateMeeting.taHour ?? '',
  197. ),
  198. Positioned(
  199. left: setting.userLocalDb
  200. .getUser()
  201. .language ==
  202. 'fa'
  203. ? 20
  204. : 250,
  205. child: Container(
  206. decoration: BoxDecoration(
  207. borderRadius:
  208. BorderRadius.circular(10),
  209. color: Colors.green),
  210. child: Padding(
  211. padding:
  212. const EdgeInsets.all(5.0),
  213. child: Text(
  214. AppLocalizations.of(context)!
  215. .privatemeeting,
  216. style: TextStyle(
  217. fontSize: 12,
  218. color: Colors.white),
  219. ),
  220. )),
  221. top: 20,
  222. ),
  223. ],
  224. ),
  225. );
  226. }
  227. },
  228. )
  229. : Center(
  230. child: Column(
  231. mainAxisAlignment: MainAxisAlignment.center,
  232. children: [
  233. Icon(Icons.error_outline,
  234. size: 40,
  235. color:
  236. config.ui.mainGray.withOpacity(.5)),
  237. const SizedBox(
  238. height: 20,
  239. ),
  240. Text(
  241. AppLocalizations.of(context)!
  242. .nomeetingfortoday,
  243. style: TextStyle(
  244. color:
  245. config.ui.mainGray.withOpacity(.5)),
  246. ),
  247. ],
  248. ),
  249. ),
  250. ),
  251. ),
  252. SliverPadding(
  253. padding: const EdgeInsets.symmetric(
  254. vertical: 30, horizontal: 10),
  255. sliver: SliverToBoxAdapter(
  256. child: StaggeredGrid.count(
  257. crossAxisCount: 4,
  258. mainAxisSpacing: 4,
  259. crossAxisSpacing: 4,
  260. children: [
  261. StaggeredGridTile.count(
  262. crossAxisCellCount: 2,
  263. mainAxisCellCount: 1,
  264. child: ItemInGrid(
  265. icon: Icons.assessment,
  266. backColor: const Color(0xff03C85F),
  267. text: AppLocalizations.of(context)!.reports,
  268. onTap: () {
  269. context.pushNamed('navigate',
  270. pathParameters: {'tab': '3'});
  271. },
  272. ),
  273. ),
  274. StaggeredGridTile.count(
  275. crossAxisCellCount: 2,
  276. mainAxisCellCount: 2,
  277. child: ItemInGrid(
  278. icon: Icons.people,
  279. backColor: const Color(0xff04A54F),
  280. text: AppLocalizations.of(context)!.meetings,
  281. onTap: () {
  282. context.pushNamed('navigate',
  283. pathParameters: {'tab': '1'});
  284. },
  285. ),
  286. ),
  287. StaggeredGridTile.count(
  288. crossAxisCellCount: 2,
  289. mainAxisCellCount: 2,
  290. child: ItemInGrid(
  291. icon: Icons.calendar_today,
  292. backColor: const Color(0xff37A068),
  293. text: AppLocalizations.of(context)!.events,
  294. onTap: () {
  295. context.pushNamed('navigate',
  296. pathParameters: {'tab': '2'});
  297. },
  298. ),
  299. ),
  300. StaggeredGridTile.count(
  301. crossAxisCellCount: 2,
  302. mainAxisCellCount: 1,
  303. child: ItemInGrid(
  304. icon: Icons.exit_to_app,
  305. backColor: const Color(0xff00843D),
  306. text: AppLocalizations.of(context)!.exit,
  307. onTap: () {
  308. showModalBottomSheet(
  309. context: context,
  310. builder: (context) {
  311. return DraggableScrollableSheet(
  312. initialChildSize: .5,
  313. expand: false,
  314. snap: false,
  315. builder: (context, scrollController) {
  316. return Column(
  317. mainAxisSize: MainAxisSize.min,
  318. children: [
  319. Padding(
  320. padding: const EdgeInsets.only(
  321. top: 8, bottom: 30),
  322. child: Container(
  323. width: 60,
  324. height: 4,
  325. decoration: BoxDecoration(
  326. color: Colors.black
  327. .withOpacity(.4),
  328. borderRadius:
  329. BorderRadius.circular(
  330. 10)),
  331. ),
  332. ),
  333. Text(
  334. AppLocalizations.of(context)!
  335. .exit,
  336. style: TextStyle(
  337. color: config.ui.mainGreen,
  338. fontSize: 18,
  339. fontWeight: FontWeight.w500),
  340. ),
  341. const SizedBox(
  342. height: 15,
  343. ),
  344. Text(
  345. AppLocalizations.of(context)!
  346. .areusurelog,
  347. style: TextStyle(
  348. color: Colors.black,
  349. fontSize: 14,
  350. ),
  351. ),
  352. const SizedBox(
  353. height: 30,
  354. ),
  355. logOutButton(),
  356. const SizedBox(
  357. height: 40,
  358. )
  359. ],
  360. );
  361. },
  362. );
  363. },
  364. );
  365. },
  366. ),
  367. ),
  368. ],
  369. ),
  370. ),
  371. ),
  372. ],
  373. ),
  374. );
  375. case Status.loading:
  376. return const LoadingWidget();
  377. case Status.error:
  378. return CustomErrorWidget(
  379. onPressed: () async {
  380. await homeState.getTodayMeetings(refresh: true);
  381. },
  382. );
  383. case Status.empty:
  384. return EmptyStateWidget();
  385. default:
  386. return Container();
  387. }
  388. },
  389. );
  390. }
  391. Consumer<HomeState> logOutButton() {
  392. return Consumer<HomeState>(
  393. builder: (context, value, child) {
  394. switch (value.statusLogOut) {
  395. case Status.loading:
  396. return const LoadingWidget();
  397. default:
  398. return Row(
  399. mainAxisAlignment: MainAxisAlignment.center,
  400. crossAxisAlignment: CrossAxisAlignment.center,
  401. children: [
  402. CustomButton(
  403. fontSize: 13,
  404. color: config.ui.mainGreen,
  405. onPressed: () {
  406. Navigator.pop(context);
  407. },
  408. hieght: 50,
  409. // width: 150,
  410. text: AppLocalizations.of(context)!.no,
  411. ),
  412. const SizedBox(
  413. width: 10,
  414. ),
  415. CustomButton(
  416. fontSize: 13,
  417. hieght: 50,
  418. // width: 150,
  419. text: AppLocalizations.of(context)!.logout,
  420. textColor: Colors.black,
  421. color: const Color(0xffD0D5ED),
  422. onPressed: () async {
  423. final status = await value.logOut();
  424. if (status == Status.error) {
  425. Tools.showCustomSnackBar(context,
  426. text: value.messageLogOut ??
  427. AppLocalizations.of(context)!.error,
  428. isError: true);
  429. } else if (status == Status.ready) {
  430. final logOut = await setting.userLocalDb.logOut();
  431. if (logOut) {
  432. context.goNamed('login');
  433. Tools.showCustomSnackBar(context,
  434. text: value.messageLogOut ?? 'Done successfully',
  435. isError: false);
  436. } else {
  437. Tools.showCustomSnackBar(context,
  438. text: value.messageLogOut ??
  439. AppLocalizations.of(context)!.error,
  440. isError: true);
  441. }
  442. }
  443. },
  444. ),
  445. ],
  446. );
  447. }
  448. },
  449. );
  450. }
  451. }
  452. class ItemInGrid extends StatelessWidget {
  453. final IconData icon;
  454. final Color backColor;
  455. final String text;
  456. final void Function() onTap;
  457. const ItemInGrid(
  458. {super.key,
  459. required this.icon,
  460. required this.backColor,
  461. required this.text,
  462. required this.onTap});
  463. @override
  464. Widget build(BuildContext context) {
  465. return Padding(
  466. padding: const EdgeInsets.all(4.0),
  467. child: GestureDetector(
  468. onTap: onTap,
  469. child: Container(
  470. decoration: BoxDecoration(
  471. borderRadius: BorderRadius.circular(20),
  472. color: backColor,
  473. boxShadow: [
  474. BoxShadow(
  475. color: config.ui.mainGray.withOpacity(.5),
  476. spreadRadius: .1,
  477. offset: const Offset(0, 2),
  478. blurRadius: 6)
  479. ],
  480. ),
  481. child: Column(
  482. mainAxisAlignment: MainAxisAlignment.center,
  483. children: [
  484. Icon(
  485. icon,
  486. size: 40,
  487. color: Colors.white,
  488. ),
  489. const SizedBox(
  490. height: 8,
  491. ),
  492. Text(
  493. text,
  494. style: const TextStyle(color: Colors.white, fontSize: 16),
  495. ),
  496. ],
  497. ),
  498. ),
  499. ),
  500. );
  501. }
  502. }