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.

283 lines
11 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year 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:sonnat/core/extensions/context_extension.dart';
  5. import 'package:sonnat/core/extensions/string_extension.dart';
  6. import 'package:sonnat/core/language/translator.dart';
  7. import 'package:sonnat/core/utils/app_constants.dart';
  8. import 'package:sonnat/core/utils/base_cubit_type.dart';
  9. import 'package:sonnat/core/widgets/loading_list_widget.dart';
  10. import 'package:sonnat/features/posts/cubit/posts_cubit.dart';
  11. import 'package:sonnat/features/posts/widgets/filter_item_widget.dart';
  12. import 'package:sonnat/features/posts/widgets/post_item_widget.dart';
  13. import 'package:sonnat/features/posts/widgets/search_widget.dart';
  14. import 'package:sonnat/features/single_post/cubit/single_post_cubit.dart';
  15. import 'package:sonnat/features/single_post/screen/single_post_screen.dart';
  16. import 'package:sonnat/features/single_post/view_models/post.dart';
  17. class PostsScreen extends StatefulWidget {
  18. final String title;
  19. final bool searchMode;
  20. const PostsScreen({super.key, required this.title, this.searchMode = false});
  21. @override
  22. State<PostsScreen> createState() => _PostsScreenState();
  23. }
  24. class _PostsScreenState extends State<PostsScreen> {
  25. late final PostsCubit _cubit;
  26. bool _loading = true;
  27. bool _getListFlag = false;
  28. late bool _searchMode;
  29. final ScrollController _controller = ScrollController();
  30. int _selectedFilterIndex = 1;
  31. final List<FilterItem> _filterList = [
  32. const FilterItem(selected: false, title: 'پر تکرارترین‌ها'),
  33. const FilterItem(selected: true, title: 'مشابه‌ها'),
  34. const FilterItem(selected: false, title: 'محبوبترین‌ها'),
  35. const FilterItem(selected: false, title: 'برای‌ شما'),
  36. const FilterItem(selected: false, title: 'محبوبترین‌ها'),
  37. const FilterItem(selected: false, title: 'پر تکرارترین‌ها'),
  38. const FilterItem(selected: true, title: 'مشابه‌ها'),
  39. const FilterItem(selected: false, title: 'محبوبترین‌ها'),
  40. const FilterItem(selected: false, title: 'برای‌ شما'),
  41. const FilterItem(selected: false, title: 'محبوبترین‌ها'),
  42. ];
  43. @override
  44. void initState() {
  45. _searchMode = widget.searchMode;
  46. _cubit = BlocProvider.of<PostsCubit>(context);
  47. _cubit.getData();
  48. _controller.addListener(_onScroll);
  49. super.initState();
  50. }
  51. void _onScroll() {
  52. final double maxScroll = _controller.position.maxScrollExtent;
  53. final double currentScroll = _controller.position.pixels;
  54. if (maxScroll - currentScroll <= 400) {
  55. if (_getListFlag) {
  56. _getListFlag = false;
  57. _cubit.getData();
  58. }
  59. }
  60. }
  61. @override
  62. Widget build(BuildContext context) {
  63. return Scaffold(
  64. body: SafeArea(
  65. child: BlocBuilder<PostsCubit, BaseCubitType<PostsState>>(
  66. builder: (context, state) {
  67. switch (state.eventName!) {
  68. case PostsState.empty:
  69. break;
  70. case PostsState.data:
  71. _loading = false;
  72. _getListFlag = true;
  73. break;
  74. case PostsState.loading:
  75. _loading = true;
  76. break;
  77. case PostsState.loadingPagination:
  78. break;
  79. case PostsState.changeFilterIndex:
  80. _selectedFilterIndex = state.data;
  81. _loading = true;
  82. break;
  83. }
  84. return Column(
  85. children: [
  86. SizedBox(height: context.height * 26 / AppConstants.instance.appHeight),
  87. Padding(
  88. padding: EdgeInsets.symmetric(horizontal: context.width * 26 / AppConstants.instance.appWidth),
  89. child: _searchMode
  90. ? Row(
  91. children: [
  92. Expanded(
  93. child: SearchWidget(
  94. search: (query) {
  95. _cubit.search(query: query);
  96. },
  97. ),
  98. ),
  99. SizedBox(width: context.width * 12 / AppConstants.instance.appWidth),
  100. GestureDetector(
  101. onTap: () {
  102. _cubit.clearSearch();
  103. setState(() {
  104. _searchMode = !_searchMode;
  105. });
  106. },
  107. child: SvgPicture.asset(
  108. 'ic_back'.svgPath,
  109. ),
  110. ),
  111. ],
  112. )
  113. : Row(
  114. children: [
  115. GestureDetector(
  116. onTap: () {},
  117. child: SvgPicture.asset(
  118. 'ic_more'.svgPath,
  119. ),
  120. ),
  121. SizedBox(width: context.width * 8 / AppConstants.instance.appWidth),
  122. GestureDetector(
  123. onTap: () {
  124. setState(() {
  125. _searchMode = !_searchMode;
  126. });
  127. },
  128. child: SvgPicture.asset(
  129. 'ic_rounded_search'.svgPath,
  130. ),
  131. ),
  132. const Spacer(),
  133. Text(
  134. widget.title,
  135. style: const TextStyle(
  136. color: Color(0xff404966),
  137. fontSize: 22,
  138. ),
  139. ),
  140. const Spacer(),
  141. GestureDetector(
  142. onTap: () {
  143. Navigator.pop(context);
  144. },
  145. child: SvgPicture.asset(
  146. 'ic_back'.svgPath,
  147. ),
  148. ),
  149. ],
  150. ),
  151. ),
  152. if (_searchMode)
  153. Padding(
  154. padding: EdgeInsets.only(
  155. top: context.height * 17 / AppConstants.instance.appHeight,
  156. left: context.width * 26 / AppConstants.instance.appWidth,
  157. right: context.width * 26 / AppConstants.instance.appWidth,
  158. ),
  159. child: Row(
  160. children: [
  161. SvgPicture.asset(
  162. 'ic_search'.svgPath,
  163. colorFilter: const ColorFilter.mode(
  164. Color(0xff26237A),
  165. BlendMode.srcIn,
  166. ),
  167. ),
  168. const SizedBox(width: 8),
  169. Text(
  170. '${Translator.translate('search')}:',
  171. style: const TextStyle(
  172. color: Color(0xff26237A),
  173. fontSize: 16,
  174. ),
  175. ),
  176. const SizedBox(width: 8),
  177. Expanded(
  178. child: Text(
  179. _cubit.query,
  180. maxLines: 1,
  181. overflow: TextOverflow.ellipsis,
  182. style: const TextStyle(
  183. color: Color(0xff26237A),
  184. fontSize: 16,
  185. ),
  186. ),
  187. ),
  188. const Spacer(),
  189. Text(
  190. _loading
  191. ? 'در حال جستجو'
  192. : _cubit.query.isEmpty
  193. ? ''
  194. : _cubit.searchedList.isEmpty
  195. ? "موردی یافت نشد"
  196. : "${_cubit.searchedList.length} مورد یافت شد",
  197. style: const TextStyle(
  198. color: Color(0xff636E88),
  199. fontSize: 12,
  200. ),
  201. )
  202. ],
  203. ),
  204. ),
  205. SizedBox(height: context.height * 35 / AppConstants.instance.appHeight),
  206. SizedBox(
  207. height: context.height * 31 / AppConstants.instance.appHeight,
  208. child: ListView.builder(
  209. padding: EdgeInsetsDirectional.only(start: context.width * 26 / AppConstants.instance.appWidth),
  210. itemBuilder: (context, index) {
  211. return GestureDetector(
  212. onTap: () {
  213. _cubit.changeFilter(index);
  214. },
  215. child: FilterItemWidget(
  216. title: _filterList[index].title,
  217. selected: index == _selectedFilterIndex,
  218. ),
  219. );
  220. },
  221. itemCount: _filterList.length,
  222. scrollDirection: Axis.horizontal,
  223. ),
  224. ),
  225. SizedBox(height: context.height * 26 / AppConstants.instance.appHeight),
  226. if (_loading)
  227. const Expanded(child: LoadingListWidget())
  228. else
  229. Expanded(
  230. child: ListView.builder(
  231. controller: _controller,
  232. itemBuilder: (context, index) {
  233. if (_searchMode) {
  234. return GestureDetector(
  235. onTap: () => _clickOnPost(_cubit.searchedList[index]),
  236. child: PostItemWidget(post: _cubit.searchedList[index]),
  237. );
  238. }
  239. return GestureDetector(
  240. onTap: () => _clickOnPost(_cubit.postList[index]),
  241. child: PostItemWidget(post: _cubit.postList[index]),
  242. );
  243. },
  244. itemCount: _searchMode ? _cubit.searchedList.length : _cubit.postList.length,
  245. ),
  246. )
  247. ],
  248. );
  249. },
  250. ),
  251. ),
  252. );
  253. }
  254. void _clickOnPost(Post post) {
  255. Navigator.push(
  256. context,
  257. MaterialPageRoute(
  258. builder: (context) {
  259. return BlocProvider(
  260. child: SinglePostScreen(post: post),
  261. create: (context) => SinglePostCubit(),
  262. );
  263. },
  264. ),
  265. );
  266. }
  267. }
  268. class FilterItem {
  269. final bool selected;
  270. final String title;
  271. const FilterItem({required this.selected, required this.title});
  272. }