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

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:sonnat/core/extensions/context_extension.dart';
import 'package:sonnat/core/extensions/string_extension.dart';
import 'package:sonnat/core/language/translator.dart';
import 'package:sonnat/core/select_language/cubit/select_language_cubit.dart';
import 'package:sonnat/core/select_language/screen/select_language_screen.dart';
import 'package:sonnat/core/utils/app_constants.dart';
import 'package:sonnat/core/utils/base_cubit_type.dart';
import 'package:sonnat/core/utils/initializer.dart';
import 'package:sonnat/core/widgets/loading_list_widget.dart';
import 'package:sonnat/core/widgets/more_options_widget.dart';
import 'package:sonnat/features/about_us/about_us_screen.dart';
import 'package:sonnat/features/posts/cubit/posts_cubit.dart';
import 'package:sonnat/features/posts/widgets/filter_item_widget.dart';
import 'package:sonnat/features/posts/widgets/post_item_widget.dart';
import 'package:sonnat/features/posts/widgets/search_widget.dart';
import 'package:sonnat/features/single_post/cubit/single_post_cubit.dart';
import 'package:sonnat/features/single_post/screen/single_post_screen.dart';
import 'package:sonnat/features/single_post/view_models/post.dart';
class PostsScreen extends StatefulWidget {
final String title;
final bool searchMode;
const PostsScreen({super.key, required this.title, this.searchMode = false});
@override
State<PostsScreen> createState() => _PostsScreenState();
}
class _PostsScreenState extends State<PostsScreen> {
late final PostsCubit _cubit;
bool _loading = true;
bool _getListFlag = false;
late bool _searchMode;
final ScrollController _controller = ScrollController();
int _selectedFilterIndex = 1;
final List<FilterItem> _filterList = [
const FilterItem(selected: false, title: 'پر تکرارترین‌ها'),
const FilterItem(selected: true, title: 'مشابه‌ها'),
const FilterItem(selected: false, title: 'محبوبترین‌ها'),
const FilterItem(selected: false, title: 'برای‌ شما'),
const FilterItem(selected: false, title: 'محبوبترین‌ها'),
const FilterItem(selected: false, title: 'پر تکرارترین‌ها'),
const FilterItem(selected: true, title: 'مشابه‌ها'),
const FilterItem(selected: false, title: 'محبوبترین‌ها'),
const FilterItem(selected: false, title: 'برای‌ شما'),
const FilterItem(selected: false, title: 'محبوبترین‌ها'),
];
@override
void initState() {
_searchMode = widget.searchMode;
_cubit = BlocProvider.of<PostsCubit>(context);
_cubit.getData();
_controller.addListener(_onScroll);
super.initState();
}
void _onScroll() {
final double maxScroll = _controller.position.maxScrollExtent;
final double currentScroll = _controller.position.pixels;
if (maxScroll - currentScroll <= 400) {
if (_getListFlag) {
_getListFlag = false;
_cubit.getData();
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: BlocBuilder<PostsCubit, BaseCubitType<PostsState>>(
builder: (context, state) {
switch (state.eventName!) {
case PostsState.empty:
break;
case PostsState.data:
_loading = false;
_getListFlag = true;
break;
case PostsState.loading:
_loading = true;
break;
case PostsState.loadingPagination:
break;
case PostsState.changeFilterIndex:
_selectedFilterIndex = state.data;
_loading = true;
break;
}
return CustomScrollView(
controller: _controller,
slivers: [
SliverAppBar(
floating: true,
elevation: 0,
backgroundColor: const Color(0xffE7E7F5),
automaticallyImplyLeading: false,
forceElevated: false,
shadowColor: Colors.transparent,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(116),
child: Column(
children: [
SizedBox(height: context.height * 26 / AppConstants.instance.appHeight),
Padding(
padding: EdgeInsets.symmetric(
horizontal: context.width * 26 / AppConstants.instance.appWidth,
),
child: _searchMode
? Row(
children: [
Expanded(
child: SearchWidget(
search: (query) {
_cubit.search(query: query);
},
),
),
SizedBox(width: context.width * 12 / AppConstants.instance.appWidth),
GestureDetector(
onTap: () {
_cubit.clearSearch();
setState(() {
_searchMode = !_searchMode;
});
},
child: SvgPicture.asset(
'ic_back'.svgPath,
),
),
],
)
: Stack(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: GestureDetector(
onTap: _showOptions,
child: SvgPicture.asset(
'ic_more'.svgPath,
),
),
),
PositionedDirectional(
start: context.width * 44 / AppConstants.instance.appWidth,
child: GestureDetector(
onTap: () {
setState(() {
_searchMode = !_searchMode;
});
},
child: SvgPicture.asset('ic_rounded_search'.svgPath),
),
),
Align(
alignment: AlignmentDirectional.center,
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
widget.title,
style: const TextStyle(
color: Color(0xff404966),
fontSize: 22,
),
),
),
),
Align(
alignment: AlignmentDirectional.centerEnd,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: RotatedBox(
quarterTurns:
Initializer.instance.getTextDirection() == TextDirection.ltr ? 90 : 0,
child: SvgPicture.asset('ic_back'.svgPath),
),
),
),
],
),
),
if (_searchMode)
Padding(
padding: EdgeInsets.only(
top: context.height * 17 / AppConstants.instance.appHeight,
left: context.width * 26 / AppConstants.instance.appWidth,
right: context.width * 26 / AppConstants.instance.appWidth,
),
child: Row(
children: [
SvgPicture.asset(
'ic_search'.svgPath,
colorFilter: const ColorFilter.mode(
Color(0xff26237A),
BlendMode.srcIn,
),
),
const SizedBox(width: 8),
Text(
'${Translator.translate('search')}:',
style: const TextStyle(
color: Color(0xff26237A),
fontSize: 16,
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
_cubit.query,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Color(0xff26237A),
fontSize: 16,
),
),
),
const Spacer(),
Text(
_loading
? 'در حال جستجو'
: _cubit.query.isEmpty
? ''
: _cubit.searchedList.isEmpty
? 'موردی یافت نشد'
: '${_cubit.searchedList.length} مورد یافت شد',
style: const TextStyle(
color: Color(0xff636E88),
fontSize: 12,
),
)
],
),
),
SizedBox(height: context.height * 35 / AppConstants.instance.appHeight),
SizedBox(
height: context.height * 31 / AppConstants.instance.appHeight,
child: ListView.builder(
padding: EdgeInsetsDirectional.only(
start: context.width * 26 / AppConstants.instance.appWidth,
),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_cubit.changeFilter(index);
},
child: FilterItemWidget(
title: _filterList[index].title,
selected: index == _selectedFilterIndex,
),
);
},
itemCount: _filterList.length,
scrollDirection: Axis.horizontal,
),
),
SizedBox(height: context.height * 26 / AppConstants.instance.appHeight),
],
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (_loading) {
return const LoadingListWidget();
}
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
if (_searchMode) {
return GestureDetector(
onTap: () => _clickOnPost(_cubit.searchedList[index]),
child: PostItemWidget(post: _cubit.searchedList[index]),
);
}
return GestureDetector(
onTap: () => _clickOnPost(_cubit.postList[index]),
child: PostItemWidget(post: _cubit.postList[index]),
);
},
itemCount: _searchMode ? _cubit.searchedList.length : _cubit.postList.length,
);
},
childCount: 1,
),
),
],
);
},
),
),
);
}
void _clickOnPost(Post post) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return BlocProvider(
child: SinglePostScreen(post: post),
create: (context) => SinglePostCubit(),
);
},
),
);
}
void _showOptions() {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (context) {
return Container(
height: 120,
width: context.width,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.white, width: 0.2),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
padding: const EdgeInsets.only(
top: 16,
left: 4,
right: 4,
),
child: Column(
children: [
MoreOptionsWidget(
title: Translator.translate('about_us'),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return const AboutUsScreen();
},
),
);
},
),
const SizedBox(height: 8),
Container(
width: context.width,
height: 1,
color: const Color(0xffD3D8E9),
),
const SizedBox(height: 8),
MoreOptionsWidget(
title: Translator.translate('select_language'),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return BlocProvider(
child: const SelectLanguageScreen(),
create: (context) => SelectLanguageCubit(),
);
},
),
);
},
),
],
),
);
},
);
}
}
class FilterItem {
final bool selected;
final String title;
const FilterItem({required this.selected, required this.title});
}