statistics basic skeletun

This commit is contained in:
MohammadTaha Basiri 2022-04-10 02:47:23 +04:30
parent dc7a26c644
commit bba9e2d364
12 changed files with 455 additions and 154 deletions

View File

@ -1,8 +1,9 @@
class CategoryData { class CategoryData {
final int id; final int id;
final String label; final String label;
String? asset;
const CategoryData({required this.id, required this.label}); CategoryData({required this.id, required this.label, this.asset});
factory CategoryData.fromJson(Map<String, dynamic> json) => CategoryData( factory CategoryData.fromJson(Map<String, dynamic> json) => CategoryData(
id: json['id'], id: json['id'],

View File

@ -1,7 +0,0 @@
class RadarCategory {
final int id;
final String title;
final String asset;
RadarCategory({required this.id, required this.title, required this.asset});
}

View File

@ -24,6 +24,7 @@ import 'package:didvan/views/home/settings/direct_list/direct_list_state.dart';
import 'package:didvan/views/home/settings/general_settings/settings.dart'; import 'package:didvan/views/home/settings/general_settings/settings.dart';
import 'package:didvan/views/home/settings/general_settings/settings_state.dart'; import 'package:didvan/views/home/settings/general_settings/settings_state.dart';
import 'package:didvan/views/home/settings/profile/profile.dart'; import 'package:didvan/views/home/settings/profile/profile.dart';
import 'package:didvan/views/home/statistics/statistics_state.dart';
import 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart' import 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart'
if (dart.library.io) 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart' if (dart.library.io) 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart'
if (dart.library.html) 'package:didvan/views/home/studio/studio_details/studio_details.web.dart'; if (dart.library.html) 'package:didvan/views/home/studio/studio_details/studio_details.web.dart';
@ -64,6 +65,9 @@ class RouteGenerator {
ChangeNotifierProvider<StudioState>( ChangeNotifierProvider<StudioState>(
create: (context) => StudioState(), create: (context) => StudioState(),
), ),
ChangeNotifierProvider<StatisticsState>(
create: (context) => StatisticsState(),
),
], ],
child: const Home(), child: const Home(),
), ),

View File

@ -37,7 +37,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
controller: _tabController, controller: _tabController,
children: const [ children: const [
News(), News(),
Statictics(), Statistics(),
Radar(), Radar(),
Studio(), Studio(),
Settings(), Settings(),

View File

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/requests/news.dart'; import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
@ -11,8 +10,7 @@ import 'package:didvan/views/home/widgets/logo_app_bar.dart';
import 'package:didvan/views/home/widgets/overview/news.dart'; import 'package:didvan/views/home/widgets/overview/news.dart';
import 'package:didvan/views/home/widgets/search_field.dart'; import 'package:didvan/views/home/widgets/search_field.dart';
import 'package:didvan/views/widgets/item_title.dart'; import 'package:didvan/views/widgets/item_title.dart';
import 'package:didvan/views/widgets/state_handlers/empty_result.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -34,57 +32,55 @@ class _NewsState extends State<News> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.watch<NewsState>(); return Consumer<NewsState>(
return CustomScrollView( builder: (context, state, child) => StateHandler<NewsState>(
slivers: [
const SliverToBoxAdapter(child: LogoAppBar()),
if (state.appState != AppState.failed)
SliverPadding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
sliver: SliverToBoxAdapter(
child: SearchField(
focusNode: _focusNode,
title: 'اخبار',
onChanged: _onChanged,
onFilterButtonPressed: _showFilterBottomSheet,
isFiltered: state.isFiltering,
),
),
),
SliverStateHandler<NewsState>(
onRetry: () => state.getNews(page: state.page), onRetry: () => state.getNews(page: state.page),
state: state, state: state,
builder: (context, state, index) { builder: (context, state) => ListView.builder(
index += 2; cacheExtent: 1000,
if (index % 15 == 0 && state.lastPage != state.page) { itemBuilder: (context, index) {
state.getNews(page: state.page + 1); if (index == 0) {
} return const LogoAppBar();
index -= 2; }
if (index >= state.news.length) { if (index == 1) {
return NewsOverview.placeholder; return Padding(
} padding: const EdgeInsets.only(
final news = state.news[index]; left: 16,
return NewsOverview( right: 16,
news: news, bottom: 16,
onMarkChanged: state.onMarkChanged, ),
newsRequestArgs: NewsRequestArgs( child: SearchField(
page: state.page, focusNode: _focusNode,
endDate: state.endDate, title: 'اخبار',
startDate: state.startDate, onChanged: _onChanged,
search: state.search, onFilterButtonPressed: _showFilterBottomSheet,
), isFiltered: state.isFiltering,
); ),
}, );
enableEmptyState: state.news.isEmpty, }
emptyState: EmptyResult( index -= 2;
onNewSearch: () => _focusNode.requestFocus(), index += 2;
), if (index % 15 == 0 && state.lastPage != state.page) {
childCount: state.getNews(page: state.page + 1);
state.news.length + (state.lastPage == state.page ? 0 : 3), }
itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), index -= 2;
placeholder: NewsOverview.placeholder, if (index >= state.news.length) {
), return NewsOverview.placeholder;
], }
final news = state.news[index];
return NewsOverview(
news: news,
onMarkChanged: state.onMarkChanged,
newsRequestArgs: NewsRequestArgs(
page: state.page,
endDate: state.endDate,
startDate: state.startDate,
search: state.search,
),
);
},
itemCount: state.news.length + 2,
)),
); );
} }

View File

@ -1,17 +1,16 @@
// ignore_for_file: prefer_const_constructors
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/views/home/radar/radar_state.dart'; import 'package:didvan/views/home/radar/radar_state.dart';
import 'package:didvan/views/home/radar/widgets/categories_gird.dart'; import 'package:didvan/views/home/widgets/categories_gird.dart';
import 'package:didvan/views/home/radar/widgets/categories_list.dart'; import 'package:didvan/views/home/widgets/categories_list.dart';
import 'package:didvan/views/home/widgets/date_picker_button.dart'; import 'package:didvan/views/home/widgets/date_picker_button.dart';
import 'package:didvan/views/home/widgets/logo_app_bar.dart'; import 'package:didvan/views/home/widgets/logo_app_bar.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
@ -162,17 +161,46 @@ class _RadarState extends State<Radar> {
), ),
], ],
), ),
if (state.appState != AppState.failed) CategoriesRow1(), if (state.appState != AppState.failed)
if (state.appState != AppState.failed) CategoriesRow2(), CategoriesRow1(
topPadding: 300,
onSelected: _onCategorySelected,
categories: state.categories,
isColapsed:
state.isColapsed || state.searching || state.filtering,
),
if (state.appState != AppState.failed)
CategoriesRow2(
categories: state.categories,
isColapsed:
state.isColapsed || state.searching || state.filtering,
onSelected: _onCategorySelected,
),
if (state.appState != AppState.failed && if (state.appState != AppState.failed &&
!state.searching && !state.searching &&
!state.filtering) !state.filtering)
CategoriesList(), CategoriesList(
isRadar: true,
categories: state.categories,
isColapsed:
state.isColapsed || state.searching || state.filtering,
onSelected: () => state.getRadars(page: 1),
selectedCats: state.selectedCats,
),
], ],
), ),
); );
} }
void _onCategorySelected(CategoryData category) {
final state = context.read<RadarState>();
state.selectedCats.clear();
if (category.id != 0) {
state.selectedCats.add(category);
}
state.getRadars(page: 1);
}
void _onChanged(String value) { void _onChanged(String value) {
final state = context.read<RadarState>(); final state = context.read<RadarState>();
if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) { if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) {
@ -268,7 +296,7 @@ class _RadarState extends State<Radar> {
SizedBox( SizedBox(
width: (MediaQuery.of(context).size.width - 40) / 2, width: (MediaQuery.of(context).size.width - 40) / 2,
child: DidvanCheckbox( child: DidvanCheckbox(
title: state.categories[i].title, title: state.categories[i].label,
value: state.selectedCats.contains(state.categories[i]), value: state.selectedCats.contains(state.categories[i]),
onChanged: (value) { onChanged: (value) {
if (value) { if (value) {

View File

@ -1,8 +1,8 @@
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/models/view/radar_category.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
@ -16,8 +16,8 @@ class RadarState extends CoreProvier {
int lastPage = 1; int lastPage = 1;
bool isScrolled = false; bool isScrolled = false;
bool shouldColapse = false; bool shouldColapse = false;
final List<RadarCategory> selectedCats = []; final List<CategoryData> selectedCats = [];
List<RadarCategory> categories = []; List<CategoryData> categories = [];
final List<OverviewData> radars = []; final List<OverviewData> radars = [];
bool get filtering => bool get filtering =>
@ -99,34 +99,34 @@ class RadarState extends CoreProvier {
getRadars(page: 1); getRadars(page: 1);
}); });
categories = [ categories = [
RadarCategory( CategoryData(
id: 1, id: 1,
title: 'اقتصادی', label: 'اقتصادی',
asset: Assets.economicCategoryIcon, asset: Assets.economicCategoryIcon,
), ),
RadarCategory( CategoryData(
id: 2, id: 2,
title: 'سیاسی', label: 'سیاسی',
asset: Assets.politicalCategoryIcon, asset: Assets.politicalCategoryIcon,
), ),
RadarCategory( CategoryData(
id: 3, id: 3,
title: 'فناوری', label: 'فناوری',
asset: Assets.techCategoryIcon, asset: Assets.techCategoryIcon,
), ),
RadarCategory( CategoryData(
id: 4, id: 4,
title: 'کسب و کار', label: 'کسب و کار',
asset: Assets.businessCategoryIcon, asset: Assets.businessCategoryIcon,
), ),
RadarCategory( CategoryData(
id: 5, id: 5,
title: 'زیست محیطی', label: 'زیست محیطی',
asset: Assets.enviromentalCategoryIcon, asset: Assets.enviromentalCategoryIcon,
), ),
RadarCategory( CategoryData(
id: 6, id: 6,
title: 'اجتماعی', label: 'اجتماعی',
asset: Assets.socialCategoryIcon, asset: Assets.socialCategoryIcon,
), ),
]; ];

View File

@ -1,26 +1,196 @@
import 'dart:math';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/views/home/statistics/statistics_state.dart';
import 'package:didvan/views/home/widgets/categories_gird.dart';
import 'package:didvan/views/home/widgets/categories_list.dart';
import 'package:didvan/views/home/widgets/logo_app_bar.dart'; import 'package:didvan/views/home/widgets/logo_app_bar.dart';
import 'package:didvan/views/home/widgets/overview/radar.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Statictics extends StatelessWidget { class Statistics extends StatefulWidget {
const Statictics({Key? key}) : super(key: key); const Statistics({Key? key}) : super(key: key);
@override
State<Statistics> createState() => _RadarState();
}
class _RadarState extends State<Statistics> {
final ScrollController _scrollController = ScrollController();
bool _isAnimating = false;
@override
void initState() {
_scrollController.addListener(() {
_handleAnimations();
});
final state = context.read<StatisticsState>();
state.addListener(() {
if (state.shouldColapse && mounted) {
_handleAnimations(true);
state.shouldColapse = false;
}
});
state.init();
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Consumer<StatisticsState>(
children: [ builder: (context, state, child) => Stack(
const LogoAppBar(), children: [
Expanded( CustomScrollView(
child: EmptyState( physics: _isAnimating
asset: Assets.emptyChart, ? const NeverScrollableScrollPhysics()
title: 'قیمت‌ها و شاخص‌های اقتصادی', : const ClampingScrollPhysics(),
subtitle: 'به زودی...', controller: _scrollController,
titleColor: Theme.of(context).colorScheme.title, slivers: [
const SliverToBoxAdapter(child: LogoAppBar()),
if (state.appState != AppState.failed)
const SliverToBoxAdapter(
child: SizedBox(height: 156),
),
if (state.appState != AppState.failed)
SliverPadding(
padding: const EdgeInsets.only(right: 16, bottom: 20),
sliver: SliverToBoxAdapter(
child: Align(
alignment: Alignment.centerRight,
child: AnimatedVisibility(
isVisible: !state.isColapsed,
duration: DesignConfig.lowAnimationDuration,
child: DidvanText(
'شاخص‌های منتخب',
style: Theme.of(context).textTheme.subtitle1,
color: Theme.of(context).colorScheme.title,
),
),
),
),
),
SliverStateHandler<StatisticsState>(
onRetry: () => state.getStatistics(page: state.page),
state: state,
itemPadding: const EdgeInsets.only(
bottom: 20,
left: 16,
right: 16,
),
enableEmptyState: state.statistics.isEmpty,
emptyState: Padding(
padding: const EdgeInsets.only(bottom: 120),
child: EmptyState(
asset: Assets.emptyResult,
title: 'موردی برای نمایش وجود ندارد.',
),
),
placeholder: RadarOverview.placeholder,
builder: (context, state, index) {
index += 2;
if (index % 15 == 0 && state.lastPage != state.page) {
state.getStatistics(page: state.page + 1);
}
index -= 2;
if (index >= state.statistics.length) {
return RadarOverview.placeholder;
}
final radar = state.statistics[index];
return RadarOverview(
radar: radar,
onMarkChanged: state.changeMark,
onCommentsChanged: (id, count) => {},
radarRequestArgs: RadarRequestArgs(
page: state.page,
categories:
List.from(state.selectedCats.map((cat) => cat.id)),
isSingleItem: false,
),
);
},
childCount: state.statistics.length +
(state.lastPage == state.page ? 0 : 3),
),
if (state.statistics.length == 1)
const SliverToBoxAdapter(
child: SizedBox(height: 320),
),
],
), ),
), if (state.appState != AppState.failed)
], CategoriesRow1(
onSelected: _onCategorySelected,
categories: state.categories,
isColapsed: state.isColapsed,
topPadding: 120,
),
if (state.appState != AppState.failed)
CategoriesList(
isRadar: false,
categories: state.categories,
isColapsed: state.isColapsed,
onSelected: () => state.getStatistics(page: 1),
selectedCats: state.selectedCats,
),
],
),
); );
} }
void _onCategorySelected(CategoryData category) {
final state = context.read<StatisticsState>();
state.selectedCats.clear();
if (category.id != 0) {
state.selectedCats.add(category);
}
state.getStatistics(page: 1);
}
void _handleAnimations([bool forceAnimate = false]) async {
final state = context.read<StatisticsState>();
if (_isAnimating) return;
final double position = _scrollController.offset;
if (position > 5 && !state.isColapsed || forceAnimate) {
state.isScrolled = true;
_isAnimating = true;
setState(() {});
await _scrollController.animateTo(
200,
duration: DesignConfig.mediumAnimationDuration,
curve: Curves.easeIn,
);
_isAnimating = false;
setState(() {});
} else if (position <
min(_scrollController.position.maxScrollExtent, 200) &&
state.isColapsed) {
state.isScrolled = false;
_isAnimating = true;
setState(() {});
await _scrollController.animateTo(
0,
duration: DesignConfig.mediumAnimationDuration,
curve: Curves.easeIn,
);
_isAnimating = false;
setState(() {});
}
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
} }

View File

@ -0,0 +1,96 @@
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
class StatisticsState extends CoreProvier {
int page = 1;
int lastPage = 1;
bool isScrolled = false;
bool shouldColapse = false;
final List<CategoryData> selectedCats = [];
List<CategoryData> categories = [];
final List<OverviewData> statistics = [];
bool get isColapsed => (isCategorySelected && isScrolled) || isScrolled;
bool get isCategorySelected => selectedCats.length == 1;
void resetFilters(bool isInit) {
selectedCats.clear();
isScrolled = false;
if (!isInit) {
getStatistics(page: 1);
}
}
Future<void> getStatistics({
required int page,
}) async {
this.page = page;
if (this.page == page) {
statistics.clear();
}
if (page == 1) {
appState = AppState.busy;
}
final RequestService service = RequestService(
RequestHelper.radarOverviews(
args: RadarRequestArgs(
page: page,
categories: selectedCats.map((e) => e.id).toList(),
),
),
);
await service.httpGet();
if (service.isSuccess) {
lastPage = service.result['lastPage'];
final radarsList = service.result['radars'];
for (var i = 0; i < radarsList.length; i++) {
statistics.add(OverviewData.fromJson(radarsList[i]));
}
if (isColapsed || isCategorySelected) {
shouldColapse = true;
}
appState = AppState.idle;
return;
}
appState = AppState.failed;
}
Future<void> changeMark(int id, bool value, bool shouldUpdate) async {
statistics.firstWhere((element) => element.id == id).marked = value;
if (shouldUpdate) {
notifyListeners();
}
}
void init() {
resetFilters(true);
Future.delayed(Duration.zero, () {
getStatistics(page: 1);
});
categories = [
CategoryData(
id: 1,
label: 'اقتصاد کلان',
asset: Assets.economicCategoryIcon,
),
CategoryData(
id: 2,
label: 'صنعت فولاد',
asset: Assets.politicalCategoryIcon,
),
CategoryData(
id: 3,
label: 'بازار سرمایه',
asset: Assets.techCategoryIcon,
),
];
}
}

View File

@ -1,27 +1,32 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/views/home/radar/radar_state.dart'; import 'package:didvan/models/category.dart';
import 'package:didvan/views/home/radar/widgets/category_item.dart'; import 'package:didvan/views/home/widgets/category_item.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CategoriesRow1 extends StatelessWidget { class CategoriesRow1 extends StatelessWidget {
const CategoriesRow1({Key? key}) : super(key: key); final List<CategoryData> categories;
final bool isColapsed;
final double topPadding;
final void Function(CategoryData data) onSelected;
const CategoriesRow1({
Key? key,
required this.categories,
required this.isColapsed,
required this.onSelected,
required this.topPadding,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.read<RadarState>();
final isColapsed = state.isColapsed || state.searching || state.filtering;
final MediaQueryData d = MediaQuery.of(context); final MediaQueryData d = MediaQuery.of(context);
return AnimatedPositioned( return AnimatedPositioned(
curve: Curves.easeIn, curve: Curves.easeIn,
duration: DesignConfig.mediumAnimationDuration, duration: DesignConfig.mediumAnimationDuration,
top: isColapsed ? -60 : 300 + d.padding.top, top: isColapsed ? -60 : topPadding + d.padding.top,
left: isColapsed ? -80 : 0, left: isColapsed ? -80 : 0,
right: isColapsed ? 124 : 0, right: isColapsed ? 124 : 0,
child: Row( child: Row(
children: context children: categories
.read<RadarState>()
.categories
.sublist(0, 3) .sublist(0, 3)
.map( .map(
(category) => Expanded( (category) => Expanded(
@ -30,6 +35,7 @@ class CategoriesRow1 extends StatelessWidget {
child: CategoryItem( child: CategoryItem(
category: category, category: category,
isColapsed: isColapsed, isColapsed: isColapsed,
onSelected: () => onSelected(category),
), ),
), ),
), ),
@ -41,14 +47,19 @@ class CategoriesRow1 extends StatelessWidget {
} }
class CategoriesRow2 extends StatelessWidget { class CategoriesRow2 extends StatelessWidget {
final List<CategoryData> categories;
final bool isColapsed;
final void Function(CategoryData data) onSelected;
const CategoriesRow2({ const CategoriesRow2({
Key? key, Key? key,
required this.categories,
required this.isColapsed,
required this.onSelected,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.read<RadarState>();
final isColapsed = state.isColapsed || state.searching || state.filtering;
final MediaQueryData d = MediaQuery.of(context); final MediaQueryData d = MediaQuery.of(context);
return AnimatedPositioned( return AnimatedPositioned(
curve: Curves.easeIn, curve: Curves.easeIn,
@ -57,14 +68,13 @@ class CategoriesRow2 extends StatelessWidget {
left: isColapsed ? -d.size.width : 0, left: isColapsed ? -d.size.width : 0,
right: isColapsed ? d.size.width : 0, right: isColapsed ? d.size.width : 0,
child: Row( child: Row(
children: context children: categories
.read<RadarState>()
.categories
.sublist(3, 6) .sublist(3, 6)
.map( .map(
(category) => Expanded( (category) => Expanded(
child: CategoryItem( child: CategoryItem(
category: category, category: category,
onSelected: () => onSelected(category),
isColapsed: isColapsed, isColapsed: isColapsed,
), ),
), ),

View File

@ -1,14 +1,24 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/models/view/radar_category.dart'; import 'package:didvan/models/category.dart';
import 'package:didvan/views/home/radar/radar_state.dart';
import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CategoriesList extends StatefulWidget { class CategoriesList extends StatefulWidget {
const CategoriesList({Key? key}) : super(key: key); final bool isColapsed;
final List<CategoryData> selectedCats;
final List<CategoryData> categories;
final VoidCallback onSelected;
final bool isRadar;
const CategoriesList({
Key? key,
required this.isColapsed,
required this.selectedCats,
required this.categories,
required this.onSelected,
required this.isRadar,
}) : super(key: key);
@override @override
State<CategoriesList> createState() => _CategoriesListState(); State<CategoriesList> createState() => _CategoriesListState();
@ -21,12 +31,11 @@ class _CategoriesListState extends State<CategoriesList> {
@override @override
void didUpdateWidget(covariant CategoriesList oldWidget) { void didUpdateWidget(covariant CategoriesList oldWidget) {
final RadarState state = context.read<RadarState>(); if (widget.selectedCats.isNotEmpty &&
if (state.selectedCats.isNotEmpty && _lastSelectedCategoryId != widget.selectedCats.first.id) {
_lastSelectedCategoryId != state.selectedCats.first.id) { _lastSelectedCategoryId = widget.selectedCats.first.id;
_lastSelectedCategoryId = state.selectedCats.first.id;
_scrollController.animateTo( _scrollController.animateTo(
state.selectedCats.first.id * 100, widget.selectedCats.first.id * 100,
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
curve: Curves.easeIn, curve: Curves.easeIn,
); );
@ -37,15 +46,14 @@ class _CategoriesListState extends State<CategoriesList> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final MediaQueryData d = MediaQuery.of(context); final MediaQueryData d = MediaQuery.of(context);
final state = context.read<RadarState>();
final isColapsed = state.isColapsed || state.searching || state.filtering;
return Positioned( return Positioned(
top: 0, top: 0,
left: 0, left: 0,
right: 0, right: 0,
child: AnimatedCrossFade( child: AnimatedCrossFade(
crossFadeState: crossFadeState: widget.isColapsed
isColapsed ? CrossFadeState.showSecond : CrossFadeState.showFirst, ? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: DesignConfig.mediumAnimationDuration, duration: DesignConfig.mediumAnimationDuration,
reverseDuration: DesignConfig.lowAnimationDuration, reverseDuration: DesignConfig.lowAnimationDuration,
sizeCurve: Curves.easeIn, sizeCurve: Curves.easeIn,
@ -57,7 +65,7 @@ class _CategoriesListState extends State<CategoriesList> {
boxShadow: DesignConfig.defaultShadow, boxShadow: DesignConfig.defaultShadow,
), ),
child: AnimatedVisibility( child: AnimatedVisibility(
isVisible: isColapsed, isVisible: widget.isColapsed,
duration: DesignConfig.mediumAnimationDuration, duration: DesignConfig.mediumAnimationDuration,
child: SingleChildScrollView( child: SingleChildScrollView(
controller: _scrollController, controller: _scrollController,
@ -71,11 +79,14 @@ class _CategoriesListState extends State<CategoriesList> {
child: Row( child: Row(
children: [ children: [
_itemBuilder( _itemBuilder(
RadarCategory(title: 'همه', asset: '', id: 0), CategoryData(
label: widget.isRadar ? 'همه' : 'منتخب',
id: 0,
),
context, context,
), ),
for (var i = 0; i < state.categories.length; i++) for (var i = 0; i < widget.categories.length; i++)
_itemBuilder(state.categories[i], context), _itemBuilder(widget.categories[i], context),
], ],
), ),
), ),
@ -85,20 +96,19 @@ class _CategoriesListState extends State<CategoriesList> {
); );
} }
Widget _itemBuilder(RadarCategory category, BuildContext context) { Widget _itemBuilder(CategoryData category, BuildContext context) {
final state = context.read<RadarState>();
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (state.selectedCats.isNotEmpty && if (widget.selectedCats.isNotEmpty &&
state.selectedCats.first.id == category.id) return; widget.selectedCats.first.id == category.id) return;
state.selectedCats.clear(); widget.selectedCats.clear();
if (category.id != 0) state.selectedCats.add(category); if (category.id != 0) widget.selectedCats.add(category);
await _scrollController.animateTo( await _scrollController.animateTo(
category.id * 100, category.id * 100,
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
curve: Curves.easeIn, curve: Curves.easeIn,
); );
state.getRadars(page: 1); widget.onSelected();
}, },
child: Container( child: Container(
margin: const EdgeInsets.only(left: 12), margin: const EdgeInsets.only(left: 12),
@ -108,15 +118,15 @@ class _CategoriesListState extends State<CategoriesList> {
child: FittedBox( child: FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: DidvanText( child: DidvanText(
category.title, category.label,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.focusedBorder, color: Theme.of(context).colorScheme.focusedBorder,
), ),
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: state.selectedCats.length == 1 && color: widget.selectedCats.length == 1 &&
state.selectedCats.contains(category) || widget.selectedCats.contains(category) ||
category.id == 0 && state.selectedCats.isEmpty category.id == 0 && widget.selectedCats.isEmpty
? Theme.of(context).colorScheme.focused ? Theme.of(context).colorScheme.focused
: null, : null,
border: Border.all( border: Border.all(

View File

@ -1,22 +1,22 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/models/view/radar_category.dart'; import 'package:didvan/models/category.dart';
import 'package:didvan/views/home/radar/radar_state.dart';
import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
class CategoryItem extends StatelessWidget { class CategoryItem extends StatelessWidget {
final RadarCategory category; final CategoryData category;
final bool isColapsed; final bool isColapsed;
final VoidCallback onSelected;
const CategoryItem({ const CategoryItem({
Key? key, Key? key,
required this.isColapsed, required this.isColapsed,
required this.category, required this.category,
required this.onSelected,
}) : super(key: key); }) : super(key: key);
double _width(context) { double _width(context) {
@ -40,14 +40,7 @@ class CategoryItem extends StatelessWidget {
final Size ds = MediaQuery.of(context).size; final Size ds = MediaQuery.of(context).size;
return Center( return Center(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: onSelected,
final state = context.read<RadarState>();
state.selectedCats.clear();
if (category.id != 0) {
state.selectedCats.add(category);
}
state.getRadars(page: 1);
},
child: AnimatedContainer( child: AnimatedContainer(
duration: DesignConfig.mediumAnimationDuration, duration: DesignConfig.mediumAnimationDuration,
padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero, padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero,
@ -77,14 +70,14 @@ class CategoryItem extends StatelessWidget {
borderRadius: DesignConfig.mediumBorderRadius, borderRadius: DesignConfig.mediumBorderRadius,
), ),
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: SvgPicture.asset(category.asset), child: SvgPicture.asset(category.asset!),
), ),
), ),
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
DidvanText( DidvanText(
category.title, category.label,
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
color: Theme.of(context).colorScheme.title, color: Theme.of(context).colorScheme.title,
), ),