You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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