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.
 
 
 
 
 
 

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