Sonnat Project
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.

394 lines
16 KiB

2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_bloc/flutter_bloc.dart';
  3. import 'package:flutter_svg/flutter_svg.dart';
  4. import 'package:repositories/app_api_domain/models/post_model.dart';
  5. import 'package:sonnat/core/extensions/context_extension.dart';
  6. import 'package:sonnat/core/extensions/string_extension.dart';
  7. import 'package:sonnat/core/language/translator.dart';
  8. import 'package:sonnat/core/select_language/cubit/select_language_cubit.dart';
  9. import 'package:sonnat/core/select_language/screen/select_language_screen.dart';
  10. import 'package:sonnat/core/utils/app_constants.dart';
  11. import 'package:sonnat/core/utils/base_cubit_type.dart';
  12. import 'package:sonnat/core/utils/initializer.dart';
  13. import 'package:sonnat/core/widgets/loading_list_widget.dart';
  14. import 'package:sonnat/core/widgets/more_options_widget.dart';
  15. import 'package:sonnat/features/about_us/about_us_screen.dart';
  16. import 'package:sonnat/features/posts/cubit/posts_cubit.dart';
  17. import 'package:sonnat/features/posts/widgets/filter_item_widget.dart';
  18. import 'package:sonnat/features/posts/widgets/post_item_widget.dart';
  19. import 'package:sonnat/features/posts/widgets/search_widget.dart';
  20. import 'package:sonnat/features/single_post/cubit/single_post_cubit.dart';
  21. import 'package:sonnat/features/single_post/screen/single_post_screen.dart';
  22. class PostsScreen extends StatefulWidget {
  23. final String title;
  24. final bool searchMode;
  25. final bool showFilters;
  26. const PostsScreen({
  27. super.key,
  28. required this.title,
  29. this.searchMode = false,
  30. this.showFilters = false,
  31. });
  32. @override
  33. State<PostsScreen> createState() => _PostsScreenState();
  34. }
  35. class _PostsScreenState extends State<PostsScreen> {
  36. late final PostsCubit _cubit;
  37. bool _loading = true;
  38. bool _getListFlag = false;
  39. late bool _searchMode;
  40. final ScrollController _controller = ScrollController();
  41. int _selectedFilterIndex = -1;
  42. final List<FilterItem> _filterList = [
  43. const FilterItem(selected: false, title: 'ابوبکر', catId: '77'),
  44. const FilterItem(selected: true, title: 'عمر', catId: '78'),
  45. const FilterItem(selected: false, title: 'عثمان', catId: '79'),
  46. ];
  47. @override
  48. void initState() {
  49. _searchMode = widget.searchMode;
  50. _cubit = BlocProvider.of<PostsCubit>(context);
  51. _cubit.getData(catId: _cubit.lastCatId);
  52. _controller.addListener(_onScroll);
  53. super.initState();
  54. }
  55. void _onScroll() {
  56. final double maxScroll = _controller.position.maxScrollExtent;
  57. final double currentScroll = _controller.position.pixels;
  58. if (maxScroll - currentScroll <= 400) {
  59. if (_getListFlag) {
  60. _getListFlag = false;
  61. _cubit.getData(catId: _cubit.lastCatId);
  62. }
  63. }
  64. }
  65. @override
  66. Widget build(BuildContext context) {
  67. return Scaffold(
  68. body: SafeArea(
  69. child: BlocBuilder<PostsCubit, BaseCubitType<PostsState>>(
  70. builder: (context, state) {
  71. switch (state.eventName!) {
  72. case PostsState.empty:
  73. break;
  74. case PostsState.data:
  75. _loading = false;
  76. _getListFlag = true;
  77. break;
  78. case PostsState.loading:
  79. _loading = true;
  80. break;
  81. case PostsState.loadingPagination:
  82. break;
  83. case PostsState.changeFilterIndex:
  84. _selectedFilterIndex = state.data;
  85. _controller.jumpTo(0);
  86. _loading = true;
  87. break;
  88. }
  89. return CustomScrollView(
  90. controller: _controller,
  91. slivers: [
  92. SliverAppBar(
  93. floating: true,
  94. elevation: 0,
  95. backgroundColor: const Color(0xffE7E7F5),
  96. automaticallyImplyLeading: false,
  97. forceElevated: false,
  98. shadowColor: Colors.transparent,
  99. bottom: PreferredSize(
  100. preferredSize: const Size.fromHeight(116),
  101. child: Column(
  102. children: [
  103. SizedBox(height: context.height * 26 / AppConstants.instance.appHeight),
  104. Padding(
  105. padding: EdgeInsets.symmetric(
  106. horizontal: context.width * 26 / AppConstants.instance.appWidth,
  107. ),
  108. child: _searchMode
  109. ? Row(
  110. children: [
  111. Expanded(
  112. child: SearchWidget(
  113. search: (query) {
  114. _cubit.search(query: query);
  115. },
  116. ),
  117. ),
  118. SizedBox(width: context.width * 12 / AppConstants.instance.appWidth),
  119. GestureDetector(
  120. onTap: () {
  121. _cubit.clearSearch();
  122. setState(() {
  123. _searchMode = !_searchMode;
  124. });
  125. },
  126. child: SvgPicture.asset(
  127. 'ic_back'.svgPath,
  128. ),
  129. ),
  130. ],
  131. )
  132. : Stack(
  133. children: [
  134. Align(
  135. alignment: AlignmentDirectional.centerStart,
  136. child: GestureDetector(
  137. onTap: _showOptions,
  138. child: SvgPicture.asset('ic_more'.svgPath),
  139. ),
  140. ),
  141. PositionedDirectional(
  142. start: context.width * 44 / AppConstants.instance.appWidth,
  143. child: GestureDetector(
  144. onTap: () {
  145. setState(() {
  146. _searchMode = !_searchMode;
  147. });
  148. },
  149. child: SvgPicture.asset('ic_rounded_search'.svgPath),
  150. ),
  151. ),
  152. Align(
  153. alignment: AlignmentDirectional.center,
  154. child: Padding(
  155. padding: const EdgeInsets.only(top: 8),
  156. child: Text(
  157. widget.title,
  158. style: const TextStyle(
  159. color: Color(0xff404966),
  160. fontSize: 22,
  161. ),
  162. ),
  163. ),
  164. ),
  165. Align(
  166. alignment: AlignmentDirectional.centerEnd,
  167. child: GestureDetector(
  168. onTap: () {
  169. Navigator.pop(context);
  170. },
  171. child: RotatedBox(
  172. quarterTurns:
  173. Initializer.instance.getTextDirection() == TextDirection.ltr ? 90 : 0,
  174. child: SvgPicture.asset('ic_back'.svgPath),
  175. ),
  176. ),
  177. ),
  178. ],
  179. ),
  180. ),
  181. if (_searchMode)
  182. Padding(
  183. padding: EdgeInsets.only(
  184. top: context.height * 17 / AppConstants.instance.appHeight,
  185. left: context.width * 26 / AppConstants.instance.appWidth,
  186. right: context.width * 26 / AppConstants.instance.appWidth,
  187. ),
  188. child: Row(
  189. children: [
  190. SvgPicture.asset(
  191. 'ic_search'.svgPath,
  192. colorFilter: const ColorFilter.mode(
  193. Color(0xff26237A),
  194. BlendMode.srcIn,
  195. ),
  196. ),
  197. const SizedBox(width: 8),
  198. Text(
  199. '${Translator.translate('search')}:',
  200. style: const TextStyle(
  201. color: Color(0xff26237A),
  202. fontSize: 16,
  203. ),
  204. ),
  205. const SizedBox(width: 8),
  206. Expanded(
  207. child: Text(
  208. _cubit.query,
  209. maxLines: 1,
  210. overflow: TextOverflow.ellipsis,
  211. style: const TextStyle(
  212. color: Color(0xff26237A),
  213. fontSize: 16,
  214. ),
  215. ),
  216. ),
  217. const Spacer(),
  218. Text(
  219. _loading
  220. ? 'در حال جستجو'
  221. : _cubit.query.isEmpty
  222. ? ''
  223. : _cubit.searchedList.isEmpty
  224. ? 'موردی یافت نشد'
  225. : '${_cubit.searchedList.length} مورد یافت شد',
  226. style: const TextStyle(
  227. color: Color(0xff636E88),
  228. fontSize: 12,
  229. ),
  230. )
  231. ],
  232. ),
  233. ),
  234. SizedBox(height: context.height * 35 / AppConstants.instance.appHeight),
  235. if (widget.showFilters)
  236. SizedBox(
  237. height: context.height * 31 / AppConstants.instance.appHeight,
  238. child: ListView.builder(
  239. padding: EdgeInsetsDirectional.only(
  240. start: context.width * 26 / AppConstants.instance.appWidth,
  241. ),
  242. itemBuilder: (context, index) {
  243. return GestureDetector(
  244. onTap: () {
  245. _cubit.changeFilter(index, _filterList[index].catId);
  246. },
  247. child: FilterItemWidget(
  248. title: _filterList[index].title,
  249. selected: index == _selectedFilterIndex,
  250. ),
  251. );
  252. },
  253. itemCount: _filterList.length,
  254. scrollDirection: Axis.horizontal,
  255. ),
  256. ),
  257. SizedBox(height: context.height * 26 / AppConstants.instance.appHeight),
  258. ],
  259. ),
  260. ),
  261. ),
  262. SliverList(
  263. delegate: SliverChildBuilderDelegate(
  264. (context, index) {
  265. if (_loading) {
  266. return const LoadingListWidget();
  267. }
  268. return ListView.builder(
  269. shrinkWrap: true,
  270. physics: const NeverScrollableScrollPhysics(),
  271. itemBuilder: (context, index) {
  272. if (_searchMode) {
  273. return GestureDetector(
  274. onTap: () => _clickOnPost(_cubit.searchedList[index]),
  275. child: PostItemWidget(post: _cubit.searchedList[index]),
  276. );
  277. }
  278. return GestureDetector(
  279. onTap: () => _clickOnPost(_cubit.postList[index]),
  280. child: PostItemWidget(post: _cubit.postList[index]),
  281. );
  282. },
  283. itemCount: _searchMode ? _cubit.searchedList.length : _cubit.postList.length,
  284. );
  285. },
  286. childCount: 1,
  287. ),
  288. ),
  289. ],
  290. );
  291. },
  292. ),
  293. ),
  294. );
  295. }
  296. void _clickOnPost(PostModel post) {
  297. Navigator.push(
  298. context,
  299. MaterialPageRoute(
  300. builder: (context) {
  301. return BlocProvider(
  302. child: SinglePostScreen(postId: post.id),
  303. create: (context) => SinglePostCubit(),
  304. );
  305. },
  306. ),
  307. );
  308. }
  309. void _showOptions() {
  310. showModalBottomSheet(
  311. context: context,
  312. backgroundColor: Colors.transparent,
  313. builder: (context) {
  314. return Container(
  315. height: 120,
  316. width: context.width,
  317. decoration: BoxDecoration(
  318. color: Colors.white,
  319. border: Border.all(color: Colors.white, width: 0.2),
  320. borderRadius: const BorderRadius.only(
  321. topLeft: Radius.circular(16),
  322. topRight: Radius.circular(16),
  323. ),
  324. ),
  325. padding: const EdgeInsets.only(
  326. top: 16,
  327. left: 4,
  328. right: 4,
  329. ),
  330. child: Column(
  331. children: [
  332. MoreOptionsWidget(
  333. title: Translator.translate('about_us'),
  334. onTap: () {
  335. Navigator.pop(context);
  336. Navigator.push(
  337. context,
  338. MaterialPageRoute(
  339. builder: (context) {
  340. return const AboutUsScreen();
  341. },
  342. ),
  343. );
  344. },
  345. ),
  346. const SizedBox(height: 8),
  347. Container(
  348. width: context.width,
  349. height: 1,
  350. color: const Color(0xffD3D8E9),
  351. ),
  352. const SizedBox(height: 8),
  353. MoreOptionsWidget(
  354. title: Translator.translate('select_language'),
  355. onTap: () {
  356. Navigator.pop(context);
  357. Navigator.push(
  358. context,
  359. MaterialPageRoute(
  360. builder: (context) {
  361. return BlocProvider(
  362. child: const SelectLanguageScreen(),
  363. create: (context) => SelectLanguageCubit(),
  364. );
  365. },
  366. ),
  367. );
  368. },
  369. ),
  370. ],
  371. ),
  372. );
  373. },
  374. );
  375. }
  376. }
  377. class FilterItem {
  378. final bool selected;
  379. final String title;
  380. final String catId;
  381. const FilterItem({required this.selected, required this.title, required this.catId});
  382. }