From fe43bb9bfd69edc2929cd7e877945c5a1fc82c95 Mon Sep 17 00:00:00 2001 From: sidhdhi canopas <122426509+cp-sidhdhi-p@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:36:33 +0530 Subject: [PATCH] Update old designs to new (#111) * redesign add substitute * adjust spacing * add fielding tab * remove deleted user from substitute * use firstWhereOrNull for data * minor change --- .../ball_score/ball_score_service.dart | 25 -- data/lib/service/match/match_service.dart | 24 -- khelo/assets/locales/app_en.arb | 14 +- .../ui/flow/home/search/search_screen.dart | 2 +- .../home/view_all/home_view_all_screen.dart | 41 +-- khelo/lib/ui/flow/main/main_screen.dart | 4 +- .../add_match/add_match_view_model.dart | 7 +- .../match_official_selection_view.dart | 4 +- .../add_match_officials_screen.dart | 5 +- .../search_user/search_user_screen.dart | 10 +- .../power_play/power_play_screen.dart | 10 +- .../select_squad/select_squad_screen.dart | 16 +- .../components/commentary_ball_summary.dart | 4 +- .../match_detail_commentary_view.dart | 4 +- .../match_detail_highlight_view.dart | 4 +- .../components/match_detail_info_view.dart | 4 +- .../components/match_detail_overs_view.dart | 14 +- .../match_detail_scorecard_view.dart | 30 +- .../match_detail/match_detail_tab_screen.dart | 1 - .../match_detail_tab_view_model.dart | 34 +-- .../ui/flow/my_game/my_game_tab_screen.dart | 5 +- .../add_substitute_sheet.dart | 265 +++++++++--------- .../add_toss_detail_view_model.dart | 5 +- .../components/bottom_sheet_wrapper.dart | 6 +- .../components/match_complete_sheet.dart | 4 +- .../components/over_complete_sheet.dart | 4 +- .../components/score_display_view.dart | 11 +- .../components/select_player_sheet.dart | 11 +- .../flow/score_board/score_board_screen.dart | 6 +- .../score_board/score_board_view_model.dart | 200 ++++++------- .../support/contact_support_view_model.dart | 5 +- .../ui/flow/stats/my_stats_tab_screen.dart | 113 -------- .../flow/stats/my_stats_tab_view_model.dart | 25 -- .../my_stats_tab_view_model.freezed.dart | 147 ---------- .../user_match/user_match_list_screen.dart | 82 ------ .../user_match_list_view_model.dart | 74 ----- .../user_match_list_view_model.freezed.dart | 188 ------------- .../stats/user_stat/user_stat_screen.dart | 263 ++++++----------- .../stats/user_stat/user_stat_view_model.dart | 219 +++++---------- .../user_stat_view_model.freezed.dart | 169 +++++++++-- .../flow/team/add_team/add_team_screen.dart | 2 +- .../add_team_member_screen.dart | 1 + .../contact_selection_screen.dart | 1 + .../contact_selection_view_model.dart | 4 +- .../components/primer_progress_bar.dart | 40 +-- .../team_detail_member_content.dart | 11 +- .../components/team_detail_stat_content.dart | 11 +- .../make_admin/make_team_admin_screen.dart | 93 +++--- .../make_team_admin_view_model.dart | 9 + .../make_team_admin_view_model.freezed.dart | 25 +- .../flow/team/detail/team_detail_screen.dart | 60 ++-- .../team/search_team/search_team_screen.dart | 27 +- khelo/lib/ui/flow/team/team_list_screen.dart | 2 +- .../user_detail_batting_content.dart | 23 +- .../user_detail_bowling_content.dart | 24 +- .../user_detail_fielding_content.dart | 63 +++++ .../component/user_detail_info_content.dart | 33 +-- .../team/user_detail/user_detail_screen.dart | 83 +++--- 58 files changed, 956 insertions(+), 1615 deletions(-) delete mode 100644 khelo/lib/ui/flow/stats/my_stats_tab_screen.dart delete mode 100644 khelo/lib/ui/flow/stats/my_stats_tab_view_model.dart delete mode 100644 khelo/lib/ui/flow/stats/my_stats_tab_view_model.freezed.dart delete mode 100644 khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart delete mode 100644 khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart delete mode 100644 khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart create mode 100644 khelo/lib/ui/flow/team/user_detail/component/user_detail_fielding_content.dart diff --git a/data/lib/service/ball_score/ball_score_service.dart b/data/lib/service/ball_score/ball_score_service.dart index 93127b34..144920ea 100644 --- a/data/lib/service/ball_score/ball_score_service.dart +++ b/data/lib/service/ball_score/ball_score_service.dart @@ -5,7 +5,6 @@ import '../../errors/app_error.dart'; import '../../extensions/double_extensions.dart'; import '../innings/inning_service.dart'; import '../match/match_service.dart'; -import '../../storage/app_preferences.dart'; import '../../utils/constant/firestore_constant.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -16,16 +15,12 @@ final ballScoreServiceProvider = Provider((ref) { FirebaseFirestore.instance, ref.read(matchServiceProvider), ref.read(inningServiceProvider), - ref.read(currentUserPod)?.id, ); - ref.listen(currentUserPod, (_, next) => service._currentUserId = next?.id); return service; }); class BallScoreService { - String? _currentUserId; - final FirebaseFirestore _firestore; final MatchService _matchService; final InningsService _inningsService; @@ -34,7 +29,6 @@ class BallScoreService { this._firestore, this._matchService, this._inningsService, - this._currentUserId, ); CollectionReference get _ballScoreCollection => @@ -135,25 +129,6 @@ class BallScoreService { .handleError((error, stack) => throw AppError.fromError(error, stack)); } - Stream> streamCurrentUserRelatedBalls() { - if (_currentUserId == null) { - return Stream.value([]); - } - - return _ballScoreCollection - .where( - Filter.or( - Filter(FireStoreConst.bowlerId, isEqualTo: _currentUserId), - Filter(FireStoreConst.batsmanId, isEqualTo: _currentUserId), - Filter(FireStoreConst.wicketTakerId, isEqualTo: _currentUserId), - Filter(FireStoreConst.playerOutId, isEqualTo: _currentUserId), - ), - ) - .snapshots() - .map((event) => event.docs.map((score) => score.data()).toList()) - .handleError((error, stack) => throw AppError.fromError(error, stack)); - } - Future deleteBallAndUpdateTeamDetails({ required String ballId, required String matchId, diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index 057b744a..730080d0 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -94,30 +94,6 @@ class MatchService { } } - Stream> streamCurrentUserPlayedMatches() { - if (_currentUserId == null) { - return Stream.value([]); - } - - final filter = Filter.and( - Filter(FireStoreConst.matchStatus, isEqualTo: MatchStatus.finish.value), - Filter(FireStoreConst.players, arrayContains: _currentUserId), - ); - - return _matchCollection - .where(filter) - .snapshots() - .asyncMap((snapshot) async { - return await Future.wait( - snapshot.docs.map((mainDoc) async { - final match = mainDoc.data(); - final List teams = await getTeamsList(match.teams); - return match.copyWith(teams: teams); - }).toList(), - ); - }).handleError((error, stack) => throw AppError.fromError(error, stack)); - } - Stream> streamCurrentUserRelatedMatches() { if (_currentUserId == null) { return Stream.value([]); diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 8100aeea..a32090cc 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -302,6 +302,7 @@ "team_detail_add_members_title": "Add members", "team_detail_add_match_title": "Add match", "team_detail_make_admin": "Make admin", + "make_admin_selection_error": "Can't select deactivated user as admin", "team_detail_make_admin_screen_title": "Make Admin", "team_detail_admin": "{count, plural, =0{{count} admins} =1{{count} admin} other{{count} admins}}", "@team_detail_admin": { @@ -354,6 +355,7 @@ "user_detail_info_title": "Info", "user_detail_batting_title": "Batting", "user_detail_bowling_title": "Bowling", + "user_detail_fielding_title": "Fielding", "user_detail_personal_information_title": "Personal information", "user_detail_participation_title": "Participation", "user_detail_role_title": "Role", @@ -375,6 +377,9 @@ "user_detail_eco_title": "Eco", "user_detail_no_ball_title": "NB", "user_detail_wide_ball_title": "WB", + "user_detail_catches_title": "Catches", + "user_detail_run_out_title": "Run out", + "user_detail_stumping_title": "Stumping", "match_status_yet_to_start_title": "Yet to start", "match_status_running_title": "Running", @@ -762,15 +767,6 @@ "@_STATS": { }, "tab_stats_title": "Stats", - "my_stat_stats_batting_statics_title": "Batting Statistics", - "my_stat_stats_run_scored_title": "Run scored", - "my_stat_stats_strike_rate_title": "Strike rate", - "my_stat_stats_ball_faced_title": "Ball faced", - "my_stat_stats_bowling_statics_title": "Bowling Statistics", - "my_stat_stats_economy_rate_title": "Economy rate", - "my_stat_stats_fielding_statics_title": "Fielding Statistics", - "my_stat_stats_catches_title": "Catches", - "my_stat_stats_stumping_title": "Stumping", "@_PROFILE": { }, diff --git a/khelo/lib/ui/flow/home/search/search_screen.dart b/khelo/lib/ui/flow/home/search/search_screen.dart index 37499cb2..22cbd5d5 100644 --- a/khelo/lib/ui/flow/home/search/search_screen.dart +++ b/khelo/lib/ui/flow/home/search/search_screen.dart @@ -124,7 +124,7 @@ class _SearchHomeScreenState extends ConsumerState { final match = matches[index - 1]; return MatchDetailCell( match: match, - onTap: () => AppRoute.matchDetailTab(matchId: match.id), + onTap: () => AppRoute.matchDetailTab(matchId: match.id).push(context), ); }, separatorBuilder: (context, index) => const SizedBox(height: 16), diff --git a/khelo/lib/ui/flow/home/view_all/home_view_all_screen.dart b/khelo/lib/ui/flow/home/view_all/home_view_all_screen.dart index a4582d81..62e25173 100644 --- a/khelo/lib/ui/flow/home/view_all/home_view_all_screen.dart +++ b/khelo/lib/ui/flow/home/view_all/home_view_all_screen.dart @@ -57,24 +57,27 @@ class _HomeViewAllScreenState extends ConsumerState { ); } - return ListView.separated( - padding: const EdgeInsets.all(16) + context.mediaQueryPadding, - itemCount: state.matches.length + 1, - itemBuilder: (context, index) { - if (index < state.matches.length) { - final match = state.matches[index]; - return MatchDetailCell( - match: match, - onTap: () => - AppRoute.matchDetailTab(matchId: match.id).push(context), - ); - } - return OnVisibleCallback( - onVisible: () => runPostFrame(() => notifier.loadMatches()), - child: (state.loading && state.matches.isNotEmpty) - ? const Center(child: AppProgressIndicator()) - : const SizedBox()); - }, - separatorBuilder: (context, index) => const SizedBox(height: 16)); + return Padding( + padding: context.mediaQueryPadding, + child: ListView.separated( + padding: const EdgeInsets.all(16), + itemCount: state.matches.length + 1, + itemBuilder: (context, index) { + if (index < state.matches.length) { + final match = state.matches[index]; + return MatchDetailCell( + match: match, + onTap: () => + AppRoute.matchDetailTab(matchId: match.id).push(context), + ); + } + return OnVisibleCallback( + onVisible: () => runPostFrame(() => notifier.loadMatches()), + child: (state.loading && state.matches.isNotEmpty) + ? const Center(child: AppProgressIndicator()) + : const SizedBox()); + }, + separatorBuilder: (context, index) => const SizedBox(height: 16)), + ); } } diff --git a/khelo/lib/ui/flow/main/main_screen.dart b/khelo/lib/ui/flow/main/main_screen.dart index fa76f6d1..6c25bb6b 100644 --- a/khelo/lib/ui/flow/main/main_screen.dart +++ b/khelo/lib/ui/flow/main/main_screen.dart @@ -8,7 +8,7 @@ import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/gen/assets.gen.dart'; import 'package:khelo/ui/flow/my_game/my_game_tab_screen.dart'; import 'package:khelo/ui/flow/profile/profile_screen.dart'; -import 'package:khelo/ui/flow/stats/my_stats_tab_screen.dart'; +import 'package:khelo/ui/flow/stats/user_stat/user_stat_screen.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/navigation/bottom_navigation_bar.dart'; @@ -31,7 +31,7 @@ class _MainScreenState extends ConsumerState static final List _widgets = [ const HomeScreen(), const MyGameTabScreen(), - const MyStatsTabScreen(), + const UserStatScreen(), const ProfileScreen(), ]; diff --git a/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart b/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart index 11e509cf..d58128e9 100644 --- a/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart +++ b/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/match/match_model.dart'; import 'package:data/api/team/team_model.dart'; import 'package:data/service/match/match_service.dart'; @@ -134,9 +135,9 @@ class AddMatchViewNotifier extends StateNotifier { .map((e) => e.user.id) .toList(); final refereeId = state.officials - .where((element) => element.type == MatchOfficials.referee) - .map((e) => e.user.id) - .firstOrNull; + .firstWhereOrNull((element) => element.type == MatchOfficials.referee) + ?.user + .id; final firstSquad = state.squadA ?.map((e) => MatchPlayer( diff --git a/khelo/lib/ui/flow/matches/add_match/components/match_official_selection_view.dart b/khelo/lib/ui/flow/matches/add_match/components/match_official_selection_view.dart index 9b0f5dcb..4400eacb 100644 --- a/khelo/lib/ui/flow/matches/add_match/components/match_official_selection_view.dart +++ b/khelo/lib/ui/flow/matches/add_match/components/match_official_selection_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; @@ -32,8 +33,7 @@ class MatchOfficialSelectionView extends StatelessWidget { type: official, isWholeCellTappable: true, user: state.officials - .where((element) => element.type == official) - .firstOrNull + .firstWhereOrNull((element) => element.type == official) ?.user, onCardTap: () async { final officials = await AppRoute.addMatchOfficials( diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart index 0666377a..742f8030 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart @@ -49,13 +49,14 @@ class _AddMatchOfficialsScreenState return Stack( children: [ ListView( - padding: context.mediaQueryPadding + BottomStickyOverlay.padding, + padding: context.mediaQueryPadding + + BottomStickyOverlay.padding + + const EdgeInsets.only(bottom: 40), children: [ for (final type in MatchOfficials.values) ...[ _sectionTitle(context, type.getTitle(context)), _officialList(context, notifier, state, type), ], - const SizedBox(height: 40) ], ), _stickyButton(context, state) diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_screen.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_screen.dart index a9991cc2..ae99f10e 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_screen.dart @@ -36,10 +36,11 @@ class SearchUserBottomSheet extends ConsumerWidget { final notifier = ref.watch(searchUserStateProvider.notifier); final state = ref.watch(searchUserStateProvider); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: SizedBox( - height: context.mediaQuerySize.height * 0.8, + return SizedBox( + height: context.mediaQuerySize.height * 0.8, + child: Padding( + padding: + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), child: Column( children: [ _searchTextField(context, notifier, state), @@ -73,6 +74,7 @@ class SearchUserBottomSheet extends ConsumerWidget { isShowButton: false, ) : ListView.separated( + padding: const EdgeInsets.only(top: 16, bottom: 40), separatorBuilder: (context, index) { return Divider( color: context.colorScheme.outline, diff --git a/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart b/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart index ea983a57..0064b83e 100644 --- a/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart @@ -103,10 +103,12 @@ class _PowerPlayScreenState extends ConsumerState { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary), + Expanded( + child: Text( + title, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), ), OnTapScale( onTap: onResetTap, diff --git a/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart b/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart index a57f2e04..b7d8dada 100644 --- a/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart @@ -75,13 +75,15 @@ class _SelectSquadScreenState extends ConsumerState { )), ], body: Builder(builder: (context) { - return ListView( - padding: context.mediaQueryPadding + - const EdgeInsets.symmetric(horizontal: 16), - children: [ - _playingSquadList(context, notifier, state), - _teamMemberList(context, notifier, state) - ], + return Padding( + padding: context.mediaQueryPadding, + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 16), + children: [ + _playingSquadList(context, notifier, state), + _teamMemberList(context, notifier, state) + ], + ), ); }), ); diff --git a/khelo/lib/ui/flow/matches/match_detail/components/commentary_ball_summary.dart b/khelo/lib/ui/flow/matches/match_detail/components/commentary_ball_summary.dart index f844b9c7..807d26ae 100644 --- a/khelo/lib/ui/flow/matches/match_detail/components/commentary_ball_summary.dart +++ b/khelo/lib/ui/flow/matches/match_detail/components/commentary_ball_summary.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -52,8 +53,7 @@ class CommentaryBallSummary extends StatelessWidget { Widget _ballSummaryTextView(BuildContext context) { final outPlayerSummary = overSummary.outPlayers - .where((element) => element.player.id == ball.player_out_id) - .firstOrNull; + .firstWhereOrNull((element) => element.player.id == ball.player_out_id); final wicketTakerText = ball.wicket_type != null ? " ${ball.wicket_type?.getString(context)}${outPlayerSummary?.catchBy != null ? context.l10n.match_commentary_by_fielder_text(outPlayerSummary?.catchBy?.name ?? "") : ""}!!" : ""; diff --git a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_commentary_view.dart b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_commentary_view.dart index 38412561..601824fa 100644 --- a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_commentary_view.dart +++ b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_commentary_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/team/team_model.dart'; import 'package:flutter/material.dart'; @@ -109,8 +110,7 @@ class MatchDetailCommentaryView extends ConsumerWidget { TeamModel? _getTeamByTeamId(MatchDetailTabState state, String teamId) { return state.match?.teams - .where((element) => element.team.id == teamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == teamId) ?.team; } diff --git a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_highlight_view.dart b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_highlight_view.dart index a5ea2c30..17c6a16f 100644 --- a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_highlight_view.dart +++ b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_highlight_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; import 'package:flutter/material.dart'; @@ -163,8 +164,7 @@ class MatchDetailHighlightView extends ConsumerWidget { String _getTeamNameByInningId(MatchDetailTabState state) { final teamName = state.match?.teams - .where((element) => element.team.id == state.highlightTeamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == state.highlightTeamId) ?.team .name; return teamName ?? "--"; diff --git a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_info_view.dart b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_info_view.dart index 239df00e..abc73bc3 100644 --- a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_info_view.dart +++ b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_info_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/match/match_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -120,8 +121,7 @@ class MatchDetailInfoView extends ConsumerWidget { Widget _tossDetailView(BuildContext context, MatchModel match) { String? teamName = match.teams - .where((element) => element.team.id == match.toss_winner_id) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == match.toss_winner_id) ?.team .name; String? decision = match.toss_decision?.getString(context); diff --git a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_overs_view.dart b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_overs_view.dart index 3c1ebadd..977505d7 100644 --- a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_overs_view.dart +++ b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_overs_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -68,10 +69,9 @@ class MatchDetailOversView extends ConsumerWidget { state, over.inning_id, state.allInnings - .where( + .firstWhereOrNull( (element) => element.id == over.inning_id, ) - .firstOrNull ?.index ?? 1), ); @@ -143,15 +143,11 @@ class MatchDetailOversView extends ConsumerWidget { String _getTeamNameByInningId(MatchDetailTabState state, String inningId) { final teamId = state.allInnings - .where( - (element) => element.id == inningId, - ) - .firstOrNull + .firstWhereOrNull((element) => element.id == inningId) ?.team_id; final teamName = state.match?.teams - .where((element) => element.team.id == teamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == teamId) ?.team .name; return teamName ?? "--"; @@ -161,7 +157,7 @@ class MatchDetailOversView extends ConsumerWidget { String inningId, int inningCount) { final title = _getTeamNameByInningId(state, inningId); return Padding( - padding: const EdgeInsets.only(top: 32, bottom: 16, left: 16, right: 16), + padding: const EdgeInsets.fromLTRB(16, 32, 16, 16), child: Text( "${context.l10n.match_commentary_inning_count_text(inningCount)}: $title", style: AppTextStyle.subtitle1 diff --git a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_scorecard_view.dart b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_scorecard_view.dart index 5852ef4d..66e286ca 100644 --- a/khelo/lib/ui/flow/matches/match_detail/components/match_detail_scorecard_view.dart +++ b/khelo/lib/ui/flow/matches/match_detail/components/match_detail_scorecard_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; import 'package:flutter/cupertino.dart'; @@ -82,8 +83,8 @@ class MatchDetailScorecardView extends ConsumerWidget { final bowler = _getBowlers(inningOvers); final teamSquad = state.match?.teams - .where((element) => element.team.id == overs?.team_id) - .firstOrNull + .firstWhereOrNull( + (element) => element.team.id == overs?.team_id) ?.squad; final yetToPlayPlayers = teamSquad @@ -323,7 +324,8 @@ class MatchDetailScorecardView extends ConsumerWidget { player.strikeRate.toString(), ], onTap: player.player.isActive - ? () => AppRoute.userDetail(userId: player.player.id).push(context) + ? () => + AppRoute.userDetail(userId: player.player.id).push(context) : null, header: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -377,7 +379,8 @@ class MatchDetailScorecardView extends ConsumerWidget { bowler.economy.toString(), ], onTap: bowler.player.isActive - ? () => AppRoute.userDetail(userId: bowler.player.id).push(context) + ? () => + AppRoute.userDetail(userId: bowler.player.id).push(context) : null, header: Text( bowler.player.name ?? '', @@ -480,7 +483,8 @@ class MatchDetailScorecardView extends ConsumerWidget { return WidgetSpan( child: OnTapScale( onTap: player.player.isActive - ? () => AppRoute.userDetail(userId: player.player.id).push(context) + ? () => AppRoute.userDetail(userId: player.player.id) + .push(context) : null, child: Text(name, style: AppTextStyle.caption @@ -538,7 +542,8 @@ class MatchDetailScorecardView extends ConsumerWidget { children.add(WidgetSpan( child: OnTapScale( onTap: batsmen.player.isActive - ? () => AppRoute.userDetail(userId: batsmen.player.id).push(context) + ? () => AppRoute.userDetail(userId: batsmen.player.id) + .push(context) : null, child: Text(batsmen.player.name ?? '', style: AppTextStyle.caption @@ -611,8 +616,7 @@ class MatchDetailScorecardView extends ConsumerWidget { String _getTeamNameByTeamId(MatchDetailTabState state, String teamId) { return state.match?.teams - .where((element) => element.team.id == teamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == teamId) ?.team .name ?? ""; @@ -625,8 +629,7 @@ class MatchDetailScorecardView extends ConsumerWidget { } final inningIndex = state.allInnings - .where((element) => element.id == inningId) - .firstOrNull + .firstWhereOrNull((element) => element.id == inningId) ?.index; if (inningIndex == 1 || inningIndex == 2) { @@ -684,14 +687,13 @@ class MatchDetailScorecardView extends ConsumerWidget { (startOver - 1).isNegative || (startOver - 1) == 0 ? 0 : inningOvers - .where((element) => element.overNumber == startOver - 1) - .firstOrNull + .firstWhereOrNull( + (element) => element.overNumber == startOver - 1) ?.totalRuns ?? 0; final runsAfterEndOver = inningOvers - .where((element) => element.overNumber == endOver) - .firstOrNull + .firstWhereOrNull((element) => element.overNumber == endOver) ?.totalRuns ?? 0; diff --git a/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_screen.dart b/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_screen.dart index 1bab206e..5812ed68 100644 --- a/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_screen.dart +++ b/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_screen.dart @@ -82,7 +82,6 @@ class _MatchDetailTabScreenState extends ConsumerState { onPageChanged: (index) { notifier.onTabChange(index); _scrollToIndex(index); - setState(() {}); }, ), ), diff --git a/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_view_model.dart b/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_view_model.dart index 84314174..9fe34227 100644 --- a/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_view_model.dart +++ b/khelo/lib/ui/flow/matches/match_detail/match_detail_tab_view_model.dart @@ -1,5 +1,7 @@ import 'dart:async'; + import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/innings/inning_model.dart'; import 'package:data/api/match/match_model.dart'; @@ -69,8 +71,8 @@ class MatchDetailTabViewNotifier extends StateNotifier { .lastOrNull ?.id; final runningInningId = innings - .where((element) => element.innings_status == InningStatus.running) - .firstOrNull + .firstWhereOrNull( + (element) => element.innings_status == InningStatus.running) ?.id; onScorecardExpansionChange( winnerInningId ?? runningInningId ?? innings.firstOrNull?.id ?? '', @@ -190,22 +192,18 @@ class MatchDetailTabViewNotifier extends StateNotifier { catchBy = Player(id: player.id, name: player.name ?? ''); } - final currentOver = overList - .where((element) => - element.overNumber == ball.over_number && - element.inning_id == ball.inning_id) - .firstOrNull; + final currentOver = overList.firstWhereOrNull((element) => + element.overNumber == ball.over_number && + element.inning_id == ball.inning_id); if (currentOver != null) { return currentOver .copyWith(striker: striker, nonStriker: nonStriker, bowler: bowler) .addBall(ball, catchBy: catchBy); } else { - final lastOver = overList - .where((element) => - element.overNumber == ball.over_number - 1 && - element.inning_id == ball.inning_id) - .firstOrNull; + final lastOver = overList.firstWhereOrNull((element) => + element.overNumber == ball.over_number - 1 && + element.inning_id == ball.inning_id); return OverSummary( inning_id: ball.inning_id, overNumber: ball.over_number, @@ -225,8 +223,7 @@ class MatchDetailTabViewNotifier extends StateNotifier { String _getTeamIdByInningId(String inningId) { final teamId = state.allInnings - .where((element) => element.id == inningId) - .firstOrNull + .firstWhereOrNull((element) => element.id == inningId) ?.team_id; return teamId ?? ""; } @@ -300,18 +297,15 @@ class MatchDetailTabViewNotifier extends StateNotifier { bool isFieldingTeam = false, }) { final teamId = state.allInnings - .where((element) => element.id == inningId) - .firstOrNull + .firstWhereOrNull((element) => element.id == inningId) ?.team_id; final player = state.match?.teams - .where((element) => isFieldingTeam + .firstWhereOrNull((element) => isFieldingTeam ? teamId != element.team.id : teamId == element.team.id) - .firstOrNull ?.squad - .where((element) => element.player.id == playerId) - .firstOrNull + .firstWhereOrNull((element) => element.player.id == playerId) ?.player; return player ?? const UserModel(id: ''); diff --git a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart index 0f2d3a81..32655ebb 100644 --- a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart +++ b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart @@ -72,11 +72,8 @@ class _MyGameTabScreenState extends ConsumerState Expanded( child: PageView( controller: _controller, + onPageChanged: notifier.onTabChange, children: _tabs, - onPageChanged: (index) { - notifier.onTabChange(index); - setState(() {}); - }, ), ), ], diff --git a/khelo/lib/ui/flow/score_board/add_substitute_sheet/add_substitute_sheet.dart b/khelo/lib/ui/flow/score_board/add_substitute_sheet/add_substitute_sheet.dart index c3ca3ac6..3d941736 100644 --- a/khelo/lib/ui/flow/score_board/add_substitute_sheet/add_substitute_sheet.dart +++ b/khelo/lib/ui/flow/score_board/add_substitute_sheet/add_substitute_sheet.dart @@ -3,16 +3,16 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; -import 'package:khelo/components/empty_screen.dart'; import 'package:khelo/components/error_screen.dart'; +import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/components/user_detail_cell.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/widget_extension.dart'; import 'package:khelo/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart'; import 'package:khelo/ui/flow/score_board/add_substitute_sheet/add_substitute_view_model.dart'; -import 'package:khelo/ui/flow/score_board/components/user_cell_view.dart'; +import 'package:khelo/ui/flow/score_board/components/bottom_sheet_wrapper.dart'; import 'package:khelo/ui/flow/team/add_team_member/components/verify_team_member_sheet.dart'; -import 'package:style/button/bottom_sticky_overlay.dart'; +import 'package:style/animations/on_tap_scale.dart'; import 'package:style/button/primary_button.dart'; import 'package:style/button/secondary_button.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -80,89 +80,42 @@ class _AddSubstituteSheetState extends ConsumerState { Widget build(BuildContext context) { final state = ref.watch(addSubstituteStateProvider); - return Container( - height: context.mediaQuerySize.height * 0.8, - decoration: BoxDecoration( - color: context.colorScheme.surface, - borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), - ), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.only(top: 44) + - const EdgeInsets.symmetric(horizontal: 16.0) + - context.mediaQueryPadding, - child: CustomScrollView( - slivers: _content(context, state), - ), - ), - _dragHandle(context), - if (state.error == null) _stickyButton(context), - ], - ), + return BottomSheetWrapper( + contentBottomSpacing: 16, + content: _content(context, state), + action: [ + PrimaryButton( + context.l10n.common_select_title, + enabled: selectedPlayer != null, + onPressed: () => context.pop(selectedPlayer), + ), + ], ); } - Widget _dragHandle(BuildContext context) { - return Align( - alignment: Alignment.topCenter, - child: Container( - height: 4, - width: 32, - margin: const EdgeInsets.symmetric(vertical: 20), - decoration: BoxDecoration( - color: context.colorScheme.outline, - borderRadius: BorderRadius.circular(10)), - ), - ); - } - - Widget _stickyButton(BuildContext context) { - return BottomStickyOverlay( - child: PrimaryButton( - context.l10n.common_select_title, - enabled: selectedPlayer != null, - onPressed: () => context.pop(selectedPlayer), - ), - ); - } - - List _content(BuildContext context, AddSubstituteViewState state) { + Widget _content(BuildContext context, AddSubstituteViewState state) { if (state.error != null) { - return [ - SliverToBoxAdapter( - child: Padding( - padding: BottomStickyOverlay.padding, - child: ErrorScreen( - error: state.error, - onRetryTap: notifier.onSearchChanged, - ), - ), - ) - ]; + return ErrorScreen( + error: state.error, + onRetryTap: notifier.onSearchChanged, + ); } - return [ - SliverPadding( - padding: BottomStickyOverlay.padding, - sliver: SliverMainAxisGroup(slivers: [ - SliverToBoxAdapter( - child: Text( - context.l10n.score_board_add_substitute_title, - style: AppTextStyle.header3 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - const SliverToBoxAdapter(child: SizedBox(height: 16)), - SliverToBoxAdapter( - child: _benchPlayerView(context, state.nonPlayingPlayers)), - SliverToBoxAdapter(child: _searchTextField(context, notifier, state)), - const SliverToBoxAdapter(child: SizedBox(height: 24)), - SliverFillRemaining( - hasScrollBody: false, - child: _searchResultView(context, notifier, state)), - ]), - ) - ]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.score_board_add_substitute_title, + style: AppTextStyle.header3 + .copyWith(color: context.colorScheme.textPrimary), + ), + const SizedBox(height: 16), + _benchPlayerView(context, state.nonPlayingPlayers), + _searchTextField(context, notifier, state), + const SizedBox(height: 24), + _searchResultView(context, notifier, state), + ], + ); } Widget _benchPlayerView(BuildContext context, List bench) { @@ -172,12 +125,35 @@ class _AddSubstituteSheetState extends ConsumerState { children: bench.map((player) { return Padding( padding: const EdgeInsets.only(right: 16, bottom: 16), - child: UserCellView( - title: player.name ?? context.l10n.common_anonymous_title, - imageUrl: player.profile_img_url, - initial: player.nameInitial, - isSelected: selectedPlayer?.id == player.id, + child: OnTapScale( onTap: () => setState(() => selectedPlayer = player), + child: Container( + padding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + decoration: BoxDecoration( + border: Border.all( + color: selectedPlayer?.id == player.id + ? context.colorScheme.primary + : Colors.transparent), + borderRadius: BorderRadius.circular(16), + color: context.colorScheme.containerLow), + child: Column( + children: [ + ImageAvatar( + initial: player.nameInitial, + imageUrl: player.profile_img_url, + size: 40, + ), + const SizedBox(height: 4), + Text( + player.name ?? context.l10n.common_anonymous_title, + style: AppTextStyle.caption.copyWith( + color: context.colorScheme.textPrimary, + ), + ) + ], + ), + ), ), ); }).toList(), @@ -190,46 +166,52 @@ class _AddSubstituteSheetState extends ConsumerState { AddSubstituteViewNotifier notifier, AddSubstituteViewState state, ) { - return state.searchedUsers.isEmpty - ? EmptyScreen( - title: (state.searchController.text.isNotEmpty) - ? context.l10n.add_team_member_search_no_result_title - : context.l10n.add_substitute_search_substitute_title, - description: (state.searchController.text.isNotEmpty) - ? context.l10n.add_team_member_search_description_text - : context.l10n.add_substitute_search_substitute_description, - isShowButton: false, - ) - : Column( - children: state.searchedUsers.map( - (user) { - final enableAction = !state.playingSquadIds.contains(user.id); - return Padding( - padding: const EdgeInsets.only(bottom: 16), - child: UserDetailCell( - user: user, - trailing: _addButton(context, user, enableAction), - onTap: () => UserDetailSheet.show( - context, - user, - actionButtonTitle: enableAction - ? context.l10n.common_select_title - : null, - onButtonTap: () async { - if (user.phone != null) { - final res = await VerifyTeamMemberSheet.show(context, - phoneNumber: user.phone!); - if (res != null && res && context.mounted) { - context.pop(user); - } - } - }, - ), - ), - ); - }, - ).toList(), + return AnimatedCrossFade( + duration: const Duration(milliseconds: 300), + sizeCurve: Curves.decelerate, + crossFadeState: state.searchedUsers.isEmpty + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: _emptyScreen( + context, + title: (state.searchController.text.isNotEmpty) + ? context.l10n.add_team_member_search_no_result_title + : context.l10n.add_substitute_search_substitute_title, + description: (state.searchController.text.isNotEmpty) + ? context.l10n.add_team_member_search_description_text + : context.l10n.add_substitute_search_substitute_description, + ), + secondChild: Column( + children: state.searchedUsers.map( + (user) { + final enableAction = !state.playingSquadIds.contains(user.id); + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: UserDetailCell( + user: user, + trailing: _addButton(context, user, enableAction), + onTap: () => UserDetailSheet.show( + context, + user, + actionButtonTitle: + enableAction ? context.l10n.common_select_title : null, + onButtonTap: () => _handleUserSelection(context, user), + ), + ), ); + }, + ).toList()), + ); + } + + void _handleUserSelection(BuildContext context, UserModel user) async { + if (user.phone != null) { + final res = + await VerifyTeamMemberSheet.show(context, phoneNumber: user.phone!); + if (res != null && res && context.mounted) { + context.pop(user); + } + } } Widget _searchTextField( @@ -251,15 +233,30 @@ class _AddSubstituteSheetState extends ConsumerState { return SecondaryButton( context.l10n.common_select_title, enabled: enabled, - onPressed: () async { - if (user.phone != null) { - final res = await VerifyTeamMemberSheet.show(context, - phoneNumber: user.phone!); - if (res != null && res && context.mounted) { - context.pop(user); - } - } - }, + onPressed: () => _handleUserSelection(context, user), + ); + } + + Widget _emptyScreen( + BuildContext context, { + required String title, + required String description, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: AppTextStyle.header4 + .copyWith(color: context.colorScheme.textPrimary), + ), + const SizedBox(height: 16), + Text( + description, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textSecondary), + ), + ], ); } } diff --git a/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.dart b/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.dart index b99e72b1..79e76dfd 100644 --- a/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.dart +++ b/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/match/match_model.dart'; import 'package:data/service/match/match_service.dart'; import 'package:flutter/cupertino.dart'; @@ -58,8 +59,8 @@ class AddTossDetailViewNotifier extends StateNotifier { final currentPlayingTeamId = state.tossWinnerDecision == TossDecision.bat ? state.tossWinnerTeamId : state.match!.teams - .where((element) => element.team.id != state.tossWinnerTeamId) - .firstOrNull + .firstWhereOrNull( + (element) => element.team.id != state.tossWinnerTeamId) ?.team .id; if (state.match?.id == null || diff --git a/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart b/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart index e3a6ffcd..0ea5d750 100644 --- a/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart +++ b/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart @@ -21,8 +21,10 @@ class BottomSheetWrapper extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - constraints: - BoxConstraints(maxHeight: context.mediaQuerySize.height * 0.8), + constraints: BoxConstraints( + maxHeight: (context.mediaQuerySize.height - + MediaQuery.of(context).viewInsets.bottom) * + 0.8), decoration: BoxDecoration( color: context.colorScheme.surface, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), diff --git a/khelo/lib/ui/flow/score_board/components/match_complete_sheet.dart b/khelo/lib/ui/flow/score_board/components/match_complete_sheet.dart index fcf51092..fc314301 100644 --- a/khelo/lib/ui/flow/score_board/components/match_complete_sheet.dart +++ b/khelo/lib/ui/flow/score_board/components/match_complete_sheet.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; import 'package:flutter/material.dart'; @@ -116,8 +117,7 @@ class MatchCompleteSheet extends ConsumerWidget { ...state.allInnings.map( (inning) { final teamName = state.match?.teams - .where((element) => element.team.id == inning.team_id) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == inning.team_id) ?.team .name; return _teamScore(context, state, diff --git a/khelo/lib/ui/flow/score_board/components/over_complete_sheet.dart b/khelo/lib/ui/flow/score_board/components/over_complete_sheet.dart index 37150290..5fab5253 100644 --- a/khelo/lib/ui/flow/score_board/components/over_complete_sheet.dart +++ b/khelo/lib/ui/flow/score_board/components/over_complete_sheet.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; import 'package:flutter/material.dart'; @@ -195,8 +196,7 @@ class OverCompleteSheet extends ConsumerWidget { String _getCurrentTeamName(BuildContext context, ScoreBoardViewState state) { final teamId = state.currentInning?.team_id; final teamName = state.match?.teams - .where((element) => element.team.id == teamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == teamId) ?.team .name; return teamName ?? context.l10n.score_board_current_team_title; diff --git a/khelo/lib/ui/flow/score_board/components/score_display_view.dart b/khelo/lib/ui/flow/score_board/components/score_display_view.dart index 713659ab..02d9de8a 100644 --- a/khelo/lib/ui/flow/score_board/components/score_display_view.dart +++ b/khelo/lib/ui/flow/score_board/components/score_display_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; import 'package:flutter/material.dart'; @@ -167,12 +168,10 @@ class ScoreDisplayView extends ConsumerWidget { } int getRequiredRun(ScoreBoardViewState state) { - final currentPlayingTeam = state.match?.teams - .where((element) => element.team.id == state.currentInning?.team_id) - .firstOrNull; - final otherTeam = state.match?.teams - .where((element) => element.team.id != currentPlayingTeam?.team.id) - .firstOrNull; + final currentPlayingTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id == state.currentInning?.team_id); + final otherTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id != currentPlayingTeam?.team.id); final revisedRun = state.match?.revised_target?.runs; diff --git a/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart b/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart index 7cac004a..ac75bef9 100644 --- a/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart +++ b/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/innings/inning_model.dart'; import 'package:data/api/match/match_model.dart'; @@ -179,8 +180,8 @@ class _SelectPlayerSheetState extends ConsumerState { runSpacing: 16, children: list.map((player) { final status = player.performance - .where((element) => element.inning_id == state.currentInning?.id) - .firstOrNull + .firstWhereOrNull( + (element) => element.inning_id == state.currentInning?.id) ?.status; final isSelected = type == PlayerSelectionType.batsMan @@ -274,17 +275,15 @@ class _SelectPlayerSheetState extends ConsumerState { notifier.onContinueWithInjuredPlayersChange(isEnabled); if (!isEnabled) { if (batsMan1?.performance - .where( + .firstWhereOrNull( (element) => element.inning_id == state.currentInning?.id) - .firstOrNull ?.status == PlayerStatus.injured) { batsMan1 = null; } if (batsMan2?.performance - .where( + .firstWhereOrNull( (element) => element.inning_id == state.currentInning?.id) - .firstOrNull ?.status == PlayerStatus.injured) { batsMan2 = null; diff --git a/khelo/lib/ui/flow/score_board/score_board_screen.dart b/khelo/lib/ui/flow/score_board/score_board_screen.dart index 75e3e0e6..d3ff7811 100644 --- a/khelo/lib/ui/flow/score_board/score_board_screen.dart +++ b/khelo/lib/ui/flow/score_board/score_board_screen.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; import 'package:data/api/user/user_models.dart'; @@ -16,7 +17,6 @@ import 'package:khelo/ui/flow/score_board/components/add_extra_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/add_penalty_run_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/match_complete_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/over_complete_sheet.dart'; -import 'package:khelo/ui/flow/score_board/revise_target_sheet/revise_target_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/score_board_buttons.dart'; import 'package:khelo/ui/flow/score_board/components/score_display_view.dart'; import 'package:khelo/ui/flow/score_board/components/select_fielding_position_sheet.dart'; @@ -24,6 +24,7 @@ import 'package:khelo/ui/flow/score_board/components/select_player_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/select_wicket_taker_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/select_wicket_type_sheet.dart'; import 'package:khelo/ui/flow/score_board/components/striker_selection_sheet.dart'; +import 'package:khelo/ui/flow/score_board/revise_target_sheet/revise_target_sheet.dart'; import 'package:khelo/ui/flow/score_board/score_board_view_model.dart'; import 'package:style/button/more_option_button.dart'; import 'package:style/button/toggle_button.dart'; @@ -540,9 +541,8 @@ class _ScoreBoardScreenState extends ConsumerState { if (next != null) { final actualTarget = ref.read(scoreBoardStateProvider.select((value) => (value.match?.teams - .where((element) => + .firstWhereOrNull((element) => element.team.id != value.currentInning?.team_id) - .firstOrNull ?.run ?? 0) + 1)); diff --git a/khelo/lib/ui/flow/score_board/score_board_view_model.dart b/khelo/lib/ui/flow/score_board/score_board_view_model.dart index c3aaf772..0ee96873 100644 --- a/khelo/lib/ui/flow/score_board/score_board_view_model.dart +++ b/khelo/lib/ui/flow/score_board/score_board_view_model.dart @@ -1,14 +1,16 @@ import 'dart:async'; + import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:data/api/match/match_model.dart'; -import 'package:data/api/innings/inning_model.dart'; +import 'package:collection/collection.dart'; import 'package:data/api/ball_score/ball_score_model.dart'; +import 'package:data/api/innings/inning_model.dart'; +import 'package:data/api/match/match_model.dart'; import 'package:data/api/user/user_models.dart'; import 'package:data/errors/app_error.dart'; import 'package:data/extensions/list_extensions.dart'; -import 'package:data/service/match/match_service.dart'; -import 'package:data/service/innings/inning_service.dart'; import 'package:data/service/ball_score/ball_score_service.dart'; +import 'package:data/service/innings/inning_service.dart'; +import 'package:data/service/match/match_service.dart'; import 'package:data/utils/combine_latest.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -75,30 +77,22 @@ class ScoreBoardViewNotifier extends StateNotifier { await _createInnings( isForTestMatch: match.match_type == MatchType.testMatch); } else { - final inningFirst = innings - .where( - (element) => element.innings_status == InningStatus.running) - .firstOrNull; + final inningFirst = innings.firstWhereOrNull( + (element) => element.innings_status == InningStatus.running); InningModel? inningSecond; InningModel? nextInning; if (match.match_type == MatchType.testMatch) { final isIndexEven = (inningFirst?.index ?? 0) % 2 == 0; - inningSecond = innings - .where((inning) => _isSelectInning( - inning, isIndexEven, inningFirst?.index ?? 0)) - .firstOrNull; + inningSecond = innings.firstWhereOrNull((inning) => + _isSelectInning(inning, isIndexEven, inningFirst?.index ?? 0)); } else { - inningSecond = innings - .where( - (element) => element.innings_status != InningStatus.running) - .firstOrNull; + inningSecond = innings.firstWhereOrNull( + (element) => element.innings_status != InningStatus.running); } - nextInning = innings - .where( - (inning) => inning.index == (inningFirst?.index ?? 0) + 1, - ) - .firstOrNull; + nextInning = innings.firstWhereOrNull( + (inning) => inning.index == (inningFirst?.index ?? 0) + 1, + ); if (inningFirst != state.currentInning || inningSecond != state.otherInning || @@ -256,9 +250,8 @@ class ScoreBoardViewNotifier extends StateNotifier { } final List? battingTeamSquad = state.match!.teams - .where((element) => + .firstWhereOrNull((element) => state.match!.current_playing_team_id == element.team.id) - .firstOrNull ?.squad; final currentPlayingBatsMan = battingTeamSquad ?.where((element) => element.performance.any((element) => @@ -269,9 +262,8 @@ class ScoreBoardViewNotifier extends StateNotifier { int lastPlayerIndex = battingTeamSquad ?.map( (e) => e.performance - .where( + .firstWhereOrNull( (element) => element.inning_id == state.currentInning?.id) - .firstOrNull ?.index, ) .reduce((value, element) => @@ -301,8 +293,7 @@ class ScoreBoardViewNotifier extends StateNotifier { .firstWhere( (element) => state.otherInning?.team_id == element.team.id) .squad - .where((element) => element.player.id == bowlerId) - .firstOrNull + .firstWhereOrNull((element) => element.player.id == bowlerId) : null; state = state.copyWith( @@ -369,10 +360,9 @@ class ScoreBoardViewNotifier extends StateNotifier { if (striker == lastBall.player_out_id) { // new BatsmanId final newBatsmanId = state.batsMans - ?.where((element) => + ?.firstWhereOrNull((element) => element.player.id != lastBall.batsman_id && element.player.id != lastBall.non_striker_id) - .firstOrNull ?.player .id; if (newBatsmanId != null) { @@ -397,8 +387,8 @@ class ScoreBoardViewNotifier extends StateNotifier { final tossWinnerId = state.match?.toss_winner_id; final tossDecision = state.match?.toss_decision; final opponentTeamId = state.match?.teams - .where((element) => element.team.id != state.match?.toss_winner_id) - .firstOrNull + .firstWhereOrNull( + (element) => element.team.id != state.match?.toss_winner_id) ?.team .id; if (matchId == null || @@ -559,8 +549,7 @@ class ScoreBoardViewNotifier extends StateNotifier { final bowlerId = state.bowler?.player.id; final strikerId = state.strikerId; final nonStrikerId = state.batsMans - ?.where((element) => element.player.id != state.strikerId) - .firstOrNull + ?.firstWhereOrNull((element) => element.player.id != state.strikerId) ?.player .id; final matchId = state.match?.id; @@ -629,16 +618,12 @@ class ScoreBoardViewNotifier extends StateNotifier { final updatedPlayer = _getUpdatedOutPlayerStatus(wicketType, playerOutId); state = state.copyWith(isMatchUpdated: false); - final otherBattingInning = state.allInnings - .where((element) => - element.team_id == battingTeamId && - element.id != battingTeamInningId) - .firstOrNull; - final otherBowlingInning = state.allInnings - .where((element) => - element.team_id == bowlingTeamId && - element.id != bowlingTeamInningId) - .firstOrNull; + final otherBattingInning = state.allInnings.firstWhereOrNull((element) => + element.team_id == battingTeamId && + element.id != battingTeamInningId); + final otherBowlingInning = state.allInnings.firstWhereOrNull((element) => + element.team_id == bowlingTeamId && + element.id != bowlingTeamInningId); await _ballScoreService.addBallScoreAndUpdateTeamDetails( score: ball, matchId: matchId, @@ -672,8 +657,7 @@ class ScoreBoardViewNotifier extends StateNotifier { } final outPlayer = state.batsMans - ?.where((element) => element.player.id == playerOutId) - .firstOrNull; + ?.firstWhereOrNull((element) => element.player.id == playerOutId); if (outPlayer == null) { return null; @@ -699,8 +683,8 @@ class ScoreBoardViewNotifier extends StateNotifier { void setOrSwitchStriker({String? batsManId}) { final striker = batsManId ?? state.batsMans - ?.where((element) => element.player.id != state.strikerId) - .firstOrNull + ?.firstWhereOrNull( + (element) => element.player.id != state.strikerId) ?.player .id; state = state.copyWith( @@ -778,8 +762,7 @@ class ScoreBoardViewNotifier extends StateNotifier { .map((e) => e.id); final battingSquad = state.match?.teams - .where((element) => element.team.id == teamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == teamId) ?.squad .where( (element) => !element.performance.any( @@ -794,9 +777,8 @@ class ScoreBoardViewNotifier extends StateNotifier { int injuredCount = 0; battingSquad?.forEach((element) { - final currentPerformance = element.performance - .where((element) => element.inning_id == state.currentInning?.id) - .firstOrNull; + final currentPerformance = element.performance.firstWhereOrNull( + (element) => element.inning_id == state.currentInning?.id); if (currentPerformance?.status == PlayerStatus.yetToPlay) { yetToPlayCount += 1; @@ -842,17 +824,13 @@ class ScoreBoardViewNotifier extends StateNotifier { ? state.otherInning?.team_id : state.currentInning?.team_id; - final secondPlayingTeam = state.match?.teams - .where( - (element) => element.team.id == secondPlayingTeamId, - ) - .firstOrNull; + final secondPlayingTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id == secondPlayingTeamId, + ); - final firstPlayingTeam = state.match?.teams - .where( - (element) => element.team.id != secondPlayingTeamId, - ) - .firstOrNull; + final firstPlayingTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id != secondPlayingTeamId, + ); if (firstPlayingTeam == null || secondPlayingTeam == null) { return false; } @@ -886,17 +864,13 @@ class ScoreBoardViewNotifier extends StateNotifier { if (state.currentInning?.index == 4 && _isAllOut()) { final secondPlayingTeamId = state.currentInning?.team_id; - final secondPlayingTeam = state.match?.teams - .where( - (element) => element.team.id == secondPlayingTeamId, - ) - .firstOrNull; + final secondPlayingTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id == secondPlayingTeamId, + ); - final firstPlayingTeam = state.match?.teams - .where( - (element) => element.team.id != secondPlayingTeamId, - ) - .firstOrNull; + final firstPlayingTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id != secondPlayingTeamId, + ); if (firstPlayingTeam == null || secondPlayingTeam == null) { return false; } @@ -960,16 +934,12 @@ class ScoreBoardViewNotifier extends StateNotifier { final updatedPlayers = _getUndoPlayerStatus(lastBall); state = state.copyWith(isMatchUpdated: false); - final otherBattingInning = state.allInnings - .where((element) => - element.team_id == battingTeamId && - element.id != battingTeamInningId) - .firstOrNull; - final otherBowlingInning = state.allInnings - .where((element) => - element.team_id == bowlingTeamId && - element.id != bowlingTeamInningId) - .firstOrNull; + final otherBattingInning = state.allInnings.firstWhereOrNull((element) => + element.team_id == battingTeamId && + element.id != battingTeamInningId); + final otherBowlingInning = state.allInnings.firstWhereOrNull((element) => + element.team_id == bowlingTeamId && + element.id != bowlingTeamInningId); await _ballScoreService.deleteBallAndUpdateTeamDetails( ballId: lastBall.id, @@ -999,22 +969,19 @@ class ScoreBoardViewNotifier extends StateNotifier { return null; } final battingTeam = state.match?.teams - .where((element) => element.team.id == state.currentInning?.team_id) - .firstOrNull + .firstWhereOrNull( + (element) => element.team.id == state.currentInning?.team_id) ?.squad; - final outPlayer = battingTeam - ?.where((element) => element.player.id == lastBall.player_out_id) - .firstOrNull; + final outPlayer = battingTeam?.firstWhereOrNull( + (element) => element.player.id == lastBall.player_out_id); if (outPlayer == null) { return null; } - final newBatsMan = state.batsMans - ?.where((element) => - element.player.id != lastBall.batsman_id && - element.player.id != lastBall.non_striker_id) - .firstOrNull; + final newBatsMan = state.batsMans?.firstWhereOrNull((element) => + element.player.id != lastBall.batsman_id && + element.player.id != lastBall.non_striker_id); final getOutPlayersPerformance = outPlayer.performance.updateWhere( where: (element) => element.inning_id == state.currentInning?.id, @@ -1254,13 +1221,12 @@ class ScoreBoardViewNotifier extends StateNotifier { state = state.copyWith(actionError: null, isActionInProgress: true); MatchPlayer? bowler; - var battingPlayer = currentPlayers - .where((element) => element.teamId == state.currentInning?.team_id) - .firstOrNull; + var battingPlayer = currentPlayers.firstWhereOrNull( + (element) => element.teamId == state.currentInning?.team_id); bowler = currentPlayers - .where((element) => element.teamId == state.otherInning?.team_id) - .firstOrNull + .firstWhereOrNull( + (element) => element.teamId == state.otherInning?.team_id) ?.players .firstOrNull; @@ -1272,8 +1238,8 @@ class ScoreBoardViewNotifier extends StateNotifier { final playerPerformance = statusUpdatedSquad .elementAt(index) .performance - .where((element) => element.inning_id == state.currentInning?.id) - .firstOrNull; + .firstWhereOrNull( + (element) => element.inning_id == state.currentInning?.id); if (playerPerformance?.index == null || playerPerformance?.index == 0 || @@ -1349,8 +1315,7 @@ class ScoreBoardViewNotifier extends StateNotifier { ? state.currentInning?.team_id : state.otherInning?.team_id; final teamName = state.match?.teams - .where((element) => element.team.id == teamId) - .firstOrNull + .firstWhereOrNull((element) => element.team.id == teamId) ?.team .name; return teamName; @@ -1377,9 +1342,7 @@ class ScoreBoardViewNotifier extends StateNotifier { "INVALID ID"; final teamPlayers = state.match?.teams .firstWhere((element) => element.team.id == teamId) - .squad - .where((element) => element.player.isActive) - .toList(); + .squad; if (type == PlayerSelectionType.bowler) { // remove substitute to select from bowlers @@ -1415,9 +1378,8 @@ class ScoreBoardViewNotifier extends StateNotifier { element.status == PlayerStatus.substitute, ) && _isPlayerEligibleForBatsman(element.performance - .where((element) => + .firstWhereOrNull((element) => element.inning_id == state.currentInning?.id) - .firstOrNull ?.status)) .toList() ?? []; @@ -1429,13 +1391,12 @@ class ScoreBoardViewNotifier extends StateNotifier { return []; } - final team = state.match?.teams - .where((element) => element.team.id != state.currentInning?.team_id) - .firstOrNull; + final team = state.match?.teams.firstWhereOrNull( + (element) => element.team.id != state.currentInning?.team_id); final teamSquadIds = team?.squad.map((e) => e.player.id).toList() ?? []; final teamPlayers = team?.team.players .where((element) => - element.user.isActive && !teamSquadIds.contains(element.id)) + !teamSquadIds.contains(element.id) && element.user.isActive) .map((e) => e.user) .toList(); return teamPlayers ?? []; @@ -1446,17 +1407,15 @@ class ScoreBoardViewNotifier extends StateNotifier { return []; } - final team = state.match?.teams - .where((element) => element.team.id != state.currentInning?.team_id) - .firstOrNull; + final team = state.match?.teams.firstWhereOrNull( + (element) => element.team.id != state.currentInning?.team_id); final teamSquadIds = team?.squad.map((e) => e.player.id).toList() ?? []; return teamSquadIds; } Future addSubstitute(UserModel player) async { - final fieldingTeam = state.match?.teams - .where((element) => element.team.id != state.currentInning?.team_id) - .firstOrNull; + final fieldingTeam = state.match?.teams.firstWhereOrNull( + (element) => element.team.id != state.currentInning?.team_id); if (fieldingTeam == null) { return; } @@ -1465,8 +1424,7 @@ class ScoreBoardViewNotifier extends StateNotifier { status: PlayerStatus.substitute); final matchPlayer = fieldingTeam.squad - .where((element) => element.player.id == player.id) - .firstOrNull ?? + .firstWhereOrNull((element) => element.player.id == player.id) ?? MatchPlayer(id: player.id, player: player); final playerPerformance = matchPlayer.performance.toList().updateWhere( @@ -1495,10 +1453,8 @@ class ScoreBoardViewNotifier extends StateNotifier { final teamId = state.otherInning?.team_id ?? "INVALID ID"; final teamPlayers = state.match?.teams - .firstWhere((element) => element.team.id == teamId) - .squad - .where((element) => element.player.isActive) - .toList(); + .firstWhereOrNull((element) => element.team.id == teamId) + ?.squad; return teamPlayers ?? []; } diff --git a/khelo/lib/ui/flow/settings/support/contact_support_view_model.dart b/khelo/lib/ui/flow/settings/support/contact_support_view_model.dart index 4c72f739..f7f526ae 100644 --- a/khelo/lib/ui/flow/settings/support/contact_support_view_model.dart +++ b/khelo/lib/ui/flow/settings/support/contact_support_view_model.dart @@ -47,9 +47,8 @@ class ContactSupportViewStateNotifier state = state.copyWith( actionError: null, enableSubmit: state.titleController.text.trim().isNotEmpty && - state.attachments - .where((element) => element.uploadStatus.isUploading) - .firstOrNull == + state.attachments.firstWhereOrNull( + (element) => element.uploadStatus.isUploading) == null, ); } diff --git a/khelo/lib/ui/flow/stats/my_stats_tab_screen.dart b/khelo/lib/ui/flow/stats/my_stats_tab_screen.dart deleted file mode 100644 index 003fabca..00000000 --- a/khelo/lib/ui/flow/stats/my_stats_tab_screen.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:khelo/components/app_page.dart'; -import 'package:khelo/domain/extensions/context_extensions.dart'; -import 'package:khelo/ui/flow/stats/my_stats_tab_view_model.dart'; -import 'package:khelo/ui/flow/stats/user_match/user_match_list_screen.dart'; -import 'package:khelo/ui/flow/stats/user_stat/user_stat_screen.dart'; -import 'package:style/button/action_button.dart'; -import 'package:style/button/tab_button.dart'; -import 'package:style/extensions/context_extensions.dart'; - -class MyStatsTabScreen extends ConsumerStatefulWidget { - const MyStatsTabScreen({super.key}); - - @override - ConsumerState createState() => _MyStatsTabScreenState(); -} - -class _MyStatsTabScreenState extends ConsumerState - with WidgetsBindingObserver { - final List _tabs = [ - const UserMatchListScreen(), - const UserStatScreen(), - ]; - - late PageController _controller; - - int get _selectedTab => _controller.hasClients - ? _controller.page?.round() ?? 0 - : _controller.initialPage; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - _controller = PageController( - initialPage: ref.read(myStatsTabStateProvider).selectedTab, - ); - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.detached) { - // deallocate resources - _controller.dispose(); - WidgetsBinding.instance.removeObserver(this); - } - } - - @override - Widget build(BuildContext context) { - final notifier = ref.watch(myStatsTabStateProvider.notifier); - return AppPage( - body: Builder( - builder: (context) { - return _content(context, notifier); - }, - ), - ); - } - - Widget _content(BuildContext context, MyStatsTabViewNotifier notifier) { - return SafeArea( - child: Column( - children: [ - _tabView(context), - Expanded( - child: PageView( - controller: _controller, - children: _tabs, - onPageChanged: (index) { - notifier.onTabChange(index); - setState(() {}); - }, - ), - ), - ], - ), - ); - } - - Widget _tabView(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - children: [ - TabButton( - context.l10n.common_matches_title, - selected: _selectedTab == 0, - onTap: () => _controller.jumpToPage(0), - ), - const SizedBox(width: 8), - TabButton( - context.l10n.tab_stats_title, - selected: _selectedTab == 1, - onTap: () => _controller.jumpToPage(1), - ), - - // Dummy add-icon to maintain consistency in tab placement for myCricket & Stats tab. - Visibility( - visible: false, - maintainState: true, - maintainAnimation: true, - maintainSize: true, - child: actionButton(context, - onPressed: null, - icon: Icon(Icons.add, color: context.colorScheme.primary)), - ), - ], - ), - ); - } -} diff --git a/khelo/lib/ui/flow/stats/my_stats_tab_view_model.dart b/khelo/lib/ui/flow/stats/my_stats_tab_view_model.dart deleted file mode 100644 index 35628d0b..00000000 --- a/khelo/lib/ui/flow/stats/my_stats_tab_view_model.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'my_stats_tab_view_model.freezed.dart'; - -final myStatsTabStateProvider = - StateNotifierProvider.autoDispose( - (ref) => MyStatsTabViewNotifier()); - -class MyStatsTabViewNotifier extends StateNotifier { - MyStatsTabViewNotifier() : super(const MyStatsTabState()); - - void onTabChange(int tab) { - if (state.selectedTab != tab) { - state = state.copyWith(selectedTab: tab); - } - } -} - -@freezed -class MyStatsTabState with _$MyStatsTabState { - const factory MyStatsTabState({ - @Default(0) int selectedTab, - }) = _MyStatsTabState; -} diff --git a/khelo/lib/ui/flow/stats/my_stats_tab_view_model.freezed.dart b/khelo/lib/ui/flow/stats/my_stats_tab_view_model.freezed.dart deleted file mode 100644 index af98bfc3..00000000 --- a/khelo/lib/ui/flow/stats/my_stats_tab_view_model.freezed.dart +++ /dev/null @@ -1,147 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'my_stats_tab_view_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$MyStatsTabState { - int get selectedTab => throw _privateConstructorUsedError; - - /// Create a copy of MyStatsTabState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $MyStatsTabStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $MyStatsTabStateCopyWith<$Res> { - factory $MyStatsTabStateCopyWith( - MyStatsTabState value, $Res Function(MyStatsTabState) then) = - _$MyStatsTabStateCopyWithImpl<$Res, MyStatsTabState>; - @useResult - $Res call({int selectedTab}); -} - -/// @nodoc -class _$MyStatsTabStateCopyWithImpl<$Res, $Val extends MyStatsTabState> - implements $MyStatsTabStateCopyWith<$Res> { - _$MyStatsTabStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of MyStatsTabState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? selectedTab = null, - }) { - return _then(_value.copyWith( - selectedTab: null == selectedTab - ? _value.selectedTab - : selectedTab // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$MyStatsTabStateImplCopyWith<$Res> - implements $MyStatsTabStateCopyWith<$Res> { - factory _$$MyStatsTabStateImplCopyWith(_$MyStatsTabStateImpl value, - $Res Function(_$MyStatsTabStateImpl) then) = - __$$MyStatsTabStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({int selectedTab}); -} - -/// @nodoc -class __$$MyStatsTabStateImplCopyWithImpl<$Res> - extends _$MyStatsTabStateCopyWithImpl<$Res, _$MyStatsTabStateImpl> - implements _$$MyStatsTabStateImplCopyWith<$Res> { - __$$MyStatsTabStateImplCopyWithImpl( - _$MyStatsTabStateImpl _value, $Res Function(_$MyStatsTabStateImpl) _then) - : super(_value, _then); - - /// Create a copy of MyStatsTabState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? selectedTab = null, - }) { - return _then(_$MyStatsTabStateImpl( - selectedTab: null == selectedTab - ? _value.selectedTab - : selectedTab // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc - -class _$MyStatsTabStateImpl implements _MyStatsTabState { - const _$MyStatsTabStateImpl({this.selectedTab = 0}); - - @override - @JsonKey() - final int selectedTab; - - @override - String toString() { - return 'MyStatsTabState(selectedTab: $selectedTab)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$MyStatsTabStateImpl && - (identical(other.selectedTab, selectedTab) || - other.selectedTab == selectedTab)); - } - - @override - int get hashCode => Object.hash(runtimeType, selectedTab); - - /// Create a copy of MyStatsTabState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$MyStatsTabStateImplCopyWith<_$MyStatsTabStateImpl> get copyWith => - __$$MyStatsTabStateImplCopyWithImpl<_$MyStatsTabStateImpl>( - this, _$identity); -} - -abstract class _MyStatsTabState implements MyStatsTabState { - const factory _MyStatsTabState({final int selectedTab}) = - _$MyStatsTabStateImpl; - - @override - int get selectedTab; - - /// Create a copy of MyStatsTabState - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$MyStatsTabStateImplCopyWith<_$MyStatsTabStateImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart b/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart deleted file mode 100644 index 4644c3f9..00000000 --- a/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:khelo/components/empty_screen.dart'; -import 'package:khelo/components/error_screen.dart'; -import 'package:khelo/components/match_detail_cell.dart'; -import 'package:khelo/domain/extensions/context_extensions.dart'; -import 'package:khelo/ui/app_route.dart'; - -import 'package:khelo/ui/flow/stats/user_match/user_match_list_view_model.dart'; -import 'package:style/extensions/context_extensions.dart'; -import 'package:style/indicator/progress_indicator.dart'; - -class UserMatchListScreen extends ConsumerStatefulWidget { - const UserMatchListScreen({super.key}); - - @override - ConsumerState createState() => _UserMatchListScreenState(); -} - -class _UserMatchListScreenState extends ConsumerState - with WidgetsBindingObserver { - late UserMatchListViewNotifier notifier; - - @override - void initState() { - WidgetsBinding.instance.addObserver(this); - notifier = ref.read(userMatchListStateProvider.notifier); - super.initState(); - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.detached) { - // deallocate resources - notifier.dispose(); - WidgetsBinding.instance.removeObserver(this); - } - } - - @override - Widget build(BuildContext context) { - return Builder(builder: (context) { - return _body(context); - }); - } - - Widget _body(BuildContext context) { - final state = ref.watch(userMatchListStateProvider); - if (state.loading) { - return const Center(child: AppProgressIndicator()); - } - - if (state.error != null) { - return ErrorScreen( - error: state.error, - onRetryTap: notifier.onResume, - ); - } - - return (state.matches.isNotEmpty) - ? ListView.separated( - padding: const EdgeInsets.all(16) + context.mediaQueryPadding, - itemCount: state.matches.length, - itemBuilder: (context, index) => MatchDetailCell( - match: state.matches.elementAt(index), - showStatusTag: false, - onTap: () => AppRoute.matchDetailTab( - matchId: - state.matches.elementAt(index).id) - .push(context), - ), - separatorBuilder: (context, index) => const SizedBox(height: 16), - ) - : EmptyScreen( - title: context.l10n.match_list_no_match_here_title, - description: - context.l10n.team_detail_empty_matches_description_text, - buttonTitle: context.l10n.add_match_screen_title, - onTap: () => AppRoute.addMatch().push(context), - ); - } -} diff --git a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart b/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart deleted file mode 100644 index 37a43ea6..00000000 --- a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'dart:async'; -import 'package:data/api/match/match_model.dart'; -import 'package:data/service/match/match_service.dart'; -import 'package:data/storage/app_preferences.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'user_match_list_view_model.freezed.dart'; - -final userMatchListStateProvider = - StateNotifierProvider((ref) { - final notifier = UserMatchListViewNotifier(ref.read(matchServiceProvider)); - ref.listen(hasUserSession, (_, next) => notifier._onUserSessionUpdate(next)); - return notifier; -}); - -class UserMatchListViewNotifier extends StateNotifier { - final MatchService _matchService; - late StreamSubscription _matchesStreamSubscription; - - UserMatchListViewNotifier(this._matchService) - : super(const UserMatchListState()) { - _loadUserMatches(); - } - - void _onUserSessionUpdate(bool hasSession) { - if (!hasSession) { - _cancelStreamSubscription(); - } - } - - Future _loadUserMatches() async { - state = state.copyWith(loading: true); - try { - _matchesStreamSubscription = - _matchService.streamCurrentUserPlayedMatches().listen((matches) { - state = state.copyWith(matches: matches, loading: false, error: null); - }, onError: (e) { - state = state.copyWith(error: e, loading: false); - debugPrint( - "UserMatchListViewNotifier: error while loading user matches -> $e"); - }); - } catch (e) { - state = state.copyWith(error: e, loading: false); - debugPrint( - "UserMatchListViewNotifier: error while loading user matches -> $e"); - } - } - - _cancelStreamSubscription() async { - await _matchesStreamSubscription.cancel(); - } - - onResume() { - _cancelStreamSubscription(); - _loadUserMatches(); - } - - @override - Future dispose() async { - _cancelStreamSubscription(); - super.dispose(); - } -} - -@freezed -class UserMatchListState with _$UserMatchListState { - const factory UserMatchListState({ - Object? error, - @Default(false) bool loading, - @Default([]) List matches, - }) = _UserMatchListState; -} diff --git a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart b/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart deleted file mode 100644 index 02ddf3d6..00000000 --- a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart +++ /dev/null @@ -1,188 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'user_match_list_view_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$UserMatchListState { - Object? get error => throw _privateConstructorUsedError; - bool get loading => throw _privateConstructorUsedError; - List get matches => throw _privateConstructorUsedError; - - /// Create a copy of UserMatchListState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $UserMatchListStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $UserMatchListStateCopyWith<$Res> { - factory $UserMatchListStateCopyWith( - UserMatchListState value, $Res Function(UserMatchListState) then) = - _$UserMatchListStateCopyWithImpl<$Res, UserMatchListState>; - @useResult - $Res call({Object? error, bool loading, List matches}); -} - -/// @nodoc -class _$UserMatchListStateCopyWithImpl<$Res, $Val extends UserMatchListState> - implements $UserMatchListStateCopyWith<$Res> { - _$UserMatchListStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of UserMatchListState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? error = freezed, - Object? loading = null, - Object? matches = null, - }) { - return _then(_value.copyWith( - error: freezed == error ? _value.error : error, - loading: null == loading - ? _value.loading - : loading // ignore: cast_nullable_to_non_nullable - as bool, - matches: null == matches - ? _value.matches - : matches // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$UserMatchListStateImplCopyWith<$Res> - implements $UserMatchListStateCopyWith<$Res> { - factory _$$UserMatchListStateImplCopyWith(_$UserMatchListStateImpl value, - $Res Function(_$UserMatchListStateImpl) then) = - __$$UserMatchListStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({Object? error, bool loading, List matches}); -} - -/// @nodoc -class __$$UserMatchListStateImplCopyWithImpl<$Res> - extends _$UserMatchListStateCopyWithImpl<$Res, _$UserMatchListStateImpl> - implements _$$UserMatchListStateImplCopyWith<$Res> { - __$$UserMatchListStateImplCopyWithImpl(_$UserMatchListStateImpl _value, - $Res Function(_$UserMatchListStateImpl) _then) - : super(_value, _then); - - /// Create a copy of UserMatchListState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? error = freezed, - Object? loading = null, - Object? matches = null, - }) { - return _then(_$UserMatchListStateImpl( - error: freezed == error ? _value.error : error, - loading: null == loading - ? _value.loading - : loading // ignore: cast_nullable_to_non_nullable - as bool, - matches: null == matches - ? _value._matches - : matches // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc - -class _$UserMatchListStateImpl implements _UserMatchListState { - const _$UserMatchListStateImpl( - {this.error, - this.loading = false, - final List matches = const []}) - : _matches = matches; - - @override - final Object? error; - @override - @JsonKey() - final bool loading; - final List _matches; - @override - @JsonKey() - List get matches { - if (_matches is EqualUnmodifiableListView) return _matches; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_matches); - } - - @override - String toString() { - return 'UserMatchListState(error: $error, loading: $loading, matches: $matches)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$UserMatchListStateImpl && - const DeepCollectionEquality().equals(other.error, error) && - (identical(other.loading, loading) || other.loading == loading) && - const DeepCollectionEquality().equals(other._matches, _matches)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(error), - loading, - const DeepCollectionEquality().hash(_matches)); - - /// Create a copy of UserMatchListState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$UserMatchListStateImplCopyWith<_$UserMatchListStateImpl> get copyWith => - __$$UserMatchListStateImplCopyWithImpl<_$UserMatchListStateImpl>( - this, _$identity); -} - -abstract class _UserMatchListState implements UserMatchListState { - const factory _UserMatchListState( - {final Object? error, - final bool loading, - final List matches}) = _$UserMatchListStateImpl; - - @override - Object? get error; - @override - bool get loading; - @override - List get matches; - - /// Create a copy of UserMatchListState - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$UserMatchListStateImplCopyWith<_$UserMatchListStateImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart b/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart index 85fad617..34bc7a4e 100644 --- a/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart +++ b/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart @@ -1,14 +1,18 @@ -import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/app_page.dart'; import 'package:khelo/components/error_screen.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/ui/flow/stats/user_stat/user_stat_view_model.dart'; +import 'package:khelo/ui/flow/team/user_detail/component/user_detail_fielding_content.dart'; +import 'package:style/button/tab_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; -import 'package:style/text/app_text_style.dart'; + +import '../../team/user_detail/component/user_detail_batting_content.dart'; +import '../../team/user_detail/component/user_detail_bowling_content.dart'; class UserStatScreen extends ConsumerStatefulWidget { const UserStatScreen({super.key}); @@ -19,12 +23,21 @@ class UserStatScreen extends ConsumerStatefulWidget { class _UserStatScreenState extends ConsumerState with WidgetsBindingObserver { + late PageController _controller; + + int get _selectedTab => _controller.hasClients + ? _controller.page?.round() ?? 0 + : _controller.initialPage; + late UserStatViewNotifier notifier; @override void initState() { WidgetsBinding.instance.addObserver(this); notifier = ref.read(userStatViewStateProvider.notifier); + _controller = PageController( + initialPage: ref.read(userStatViewStateProvider).selectedTab, + ); super.initState(); } @@ -39,215 +52,97 @@ class _UserStatScreenState extends ConsumerState @override Widget build(BuildContext context) { - return Builder(builder: (context) => _body(context)); + return AppPage( + body: Builder(builder: (context) => _body(context)), + ); } Widget _body(BuildContext context) { final state = ref.watch(userStatViewStateProvider); - - if (state.loading) return const Center(child: AppProgressIndicator()); - + if (state.loading) { + return const Center(child: AppProgressIndicator()); + } if (state.error != null) { return ErrorScreen( error: state.error, - onRetryTap: notifier.onResume, + onRetryTap: notifier.loadData, ); } - if (state.userStat == null) return const SizedBox(); - return ListView( - padding: const EdgeInsets.all(16) + context.mediaQueryPadding, - children: [ - if (state.userStat?.battingStat != null) ...[ - _sectionTitle( - context, context.l10n.my_stat_stats_batting_statics_title), - _battingStats(context, state.userStat?.battingStat), - ], - const SizedBox(height: 40), - if (state.userStat?.bowlingStat != null) ...[ - _sectionTitle( - context, context.l10n.my_stat_stats_bowling_statics_title), - _bowlingStats(context, state.userStat?.bowlingStat), - ], - const SizedBox(height: 40), - if (state.userStat?.fieldingStat != null) ...[ - _sectionTitle( - context, context.l10n.my_stat_stats_fielding_statics_title), - _fieldingStats(context, state.userStat?.fieldingStat), - ], - ], - ); - } - - Widget _battingStats(BuildContext context, BattingStat? battingStat) { - if (battingStat == null) return const SizedBox(); - - return Container( - padding: const EdgeInsets.all(16), - margin: const EdgeInsets.only(top: 16), - decoration: BoxDecoration( - border: Border.all(color: context.colorScheme.outline), - borderRadius: BorderRadius.circular(16)), + return Padding( + padding: context.mediaQueryPadding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _mainStatisticView( - context, - title: context.l10n.my_stat_stats_run_scored_title, - count: battingStat.runScored.toString(), - ), - const SizedBox(height: 16), - IntrinsicHeight( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _subStatisticView(context, - title: context.l10n.common_batting_average_title, - count: battingStat.average.toStringAsFixed(2)), - _subStatisticView(context, - title: context.l10n.my_stat_stats_strike_rate_title, - count: '${battingStat.strikeRate.toStringAsFixed(2)}%'), - _subStatisticView(context, - title: context.l10n.my_stat_stats_ball_faced_title, - count: battingStat.ballFaced.toString()), - ], - ), - ), + _tabView(context), + _content(context, state), ], ), ); } - Widget _bowlingStats(BuildContext context, BowlingStat? bowlingStat) { - if (bowlingStat == null) return const SizedBox(); - - return Container( - padding: const EdgeInsets.all(16), - margin: const EdgeInsets.only(top: 16), - decoration: BoxDecoration( - border: Border.all(color: context.colorScheme.outline), - borderRadius: BorderRadius.circular(16)), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _mainStatisticView( - context, - title: context.l10n.common_wicket_taken_title, - count: bowlingStat.wicketTaken.toString(), - ), - const SizedBox(height: 16), - IntrinsicHeight( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _subStatisticView(context, - title: context.l10n.common_bowling_average_title, - count: bowlingStat.average.toStringAsFixed(2)), - _subStatisticView(context, - title: context.l10n.my_stat_stats_strike_rate_title, - count: '${bowlingStat.strikeRate.toStringAsFixed(2)}%'), - _subStatisticView(context, - title: context.l10n.my_stat_stats_economy_rate_title, - count: bowlingStat.economyRate.toStringAsFixed(2)), - ], + Widget _tabView(BuildContext context) { + final tabs = [ + context.l10n.user_detail_batting_title, + context.l10n.user_detail_bowling_title, + context.l10n.user_detail_fielding_title + ]; + + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + tabs.length, + (index) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: TabButton( + tabs[index], + onTap: () { + _controller.jumpToPage(index); + }, + selected: index == _selectedTab, ), ), - ], + ), ), ); } - Widget _fieldingStats(BuildContext context, FieldingStat? fieldingStat) { - if (fieldingStat == null) return const SizedBox(); - - return Container( - padding: const EdgeInsets.all(16), - margin: const EdgeInsets.only(top: 16), - decoration: BoxDecoration( - border: Border.all(color: context.colorScheme.outline), - borderRadius: BorderRadius.circular(16)), - child: IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _subStatisticView(context, - title: context.l10n.my_stat_stats_catches_title, - count: fieldingStat.catches.toString()), - _subStatisticView(context, - title: context.l10n.common_run_out_title, - count: fieldingStat.runOut.toString()), - _subStatisticView(context, - title: context.l10n.my_stat_stats_stumping_title, - count: fieldingStat.stumping.toString()), - ], - ), + Widget _content(BuildContext context, UserStatViewState state) { + return Expanded( + child: PageView( + controller: _controller, + onPageChanged: notifier.onTabChange, + children: [ + UserDetailBattingContent( + testMatchesCount: state.testMatchesCount, + otherMatchesCount: state.otherMatchesCount, + testStats: state.testStats.battingStat, + otherStats: state.otherStats.battingStat, + ), + UserDetailBowlingContent( + testMatchesCount: state.testMatchesCount, + otherMatchesCount: state.otherMatchesCount, + testStats: state.testStats.bowlingStat, + otherStats: state.otherStats.bowlingStat, + ), + UserDetailFieldingContent( + testMatchesCount: state.testMatchesCount, + otherMatchesCount: state.otherMatchesCount, + testStats: state.testStats.fieldingStat, + otherStats: state.otherStats.fieldingStat, + ), + ], ), ); } - Widget _sectionTitle(BuildContext context, String title) => Text(title, - style: AppTextStyle.header4 - .copyWith(color: context.colorScheme.textPrimary)); - - Widget _mainStatisticView( - BuildContext context, { - required String title, - required String count, - }) { - return Container( - width: context.mediaQuerySize.width, - padding: const EdgeInsets.symmetric(vertical: 16), - decoration: BoxDecoration( - color: context.colorScheme.containerLow, - borderRadius: BorderRadius.circular(16)), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - count, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary), - ), - Text( - title, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textSecondary), - ), - ], - )); - } - - Widget _subStatisticView( - BuildContext context, { - required String title, - required String count, - }) { - return Expanded( - child: Container( - padding: const EdgeInsets.all(12), - margin: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - color: context.colorScheme.containerLow, - borderRadius: BorderRadius.circular(16)), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - count, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary), - ), - const SizedBox(height: 4), - Text( - title, - textAlign: TextAlign.center, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textSecondary), - ), - ], - )), - ); + @override + void dispose() { + _controller.dispose(); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); } } diff --git a/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.dart b/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.dart index 1396577a..f4d8323a 100644 --- a/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.dart +++ b/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'package:data/api/ball_score/ball_score_model.dart'; +import 'package:data/api/match/match_model.dart'; import 'package:data/service/ball_score/ball_score_service.dart'; +import 'package:data/service/match/match_service.dart'; import 'package:data/storage/app_preferences.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -11,6 +13,7 @@ part 'user_stat_view_model.freezed.dart'; final userStatViewStateProvider = StateNotifierProvider((ref) { final notifier = UserStatViewNotifier( + ref.read(matchServiceProvider), ref.read(ballScoreServiceProvider), ref.read(currentUserPod)?.id, ); @@ -21,180 +24,86 @@ final userStatViewStateProvider = }); class UserStatViewNotifier extends StateNotifier { + final MatchService _matchService; final BallScoreService _ballScoreService; - late StreamSubscription _ballScoreStreamSubscription; + StreamSubscription? _subscription; - UserStatViewNotifier(this._ballScoreService, String? userId) + UserStatViewNotifier( + this._matchService, this._ballScoreService, String? userId) : super(UserStatViewState(currentUserId: userId)) { - _getUserRelatedBalls(); + loadData(); } void setUserId(String? userId) { if (userId == null) { - _cancelStreamSubscription(); + _subscription?.cancel(); } state = state.copyWith(currentUserId: userId); } - Future _getUserRelatedBalls() async { + void loadData() { + if (state.currentUserId == null) { + return; + } + _subscription?.cancel(); state = state.copyWith(loading: true); + + _subscription = _matchService + .streamUserMatches(state.currentUserId!) + .listen((matches) async { + final (testMatchCount, testStat, otherMatchCount, otherStats) = + await loadMatchData(matches); + state = state.copyWith( + testStats: testStat, + otherStats: otherStats, + testMatchesCount: testMatchCount, + otherMatchesCount: otherMatchCount, + loading: false, + ); + }, onError: (e) { + state = state.copyWith(loading: false, error: e); + debugPrint("UserDetailViewNotifier: error while loading data -> $e"); + }); + } + + Future<(int, UserStat, int, UserStat)> loadMatchData( + List matches) async { try { - _ballScoreStreamSubscription = - _ballScoreService.streamCurrentUserRelatedBalls().listen((ballScores) { - final userStat = _calculateUserStats(ballScores); - state = state.copyWith(userStat: userStat, loading: false, error: null); - }, onError: (e) { - state = state.copyWith(error: e, loading: false); - debugPrint( - "UserStatViewNotifier: error while getting user related balls -> $e"); - }); + final testMatches = matches + .where((element) => element.match_type == MatchType.testMatch) + .map((e) => e.id); + final otherMatches = matches + .where((element) => element.match_type != MatchType.testMatch) + .map((e) => e.id); + final ballScore = await _ballScoreService + .getBallScoresByMatchIds(matches.map((e) => e.id).toList()); + + final testStats = ballScore + .where((element) => testMatches.contains(element.match_id)) + .toList() + .calculateUserStats(state.currentUserId ?? ''); + final otherStats = ballScore + .where((element) => otherMatches.contains(element.match_id)) + .toList() + .calculateUserStats(state.currentUserId ?? ''); + + return (testMatches.length, testStats, otherMatches.length, otherStats); } catch (e) { - state = state.copyWith(error: e, loading: false); debugPrint( - "UserStatViewNotifier: error while getting user related balls -> $e"); + "UserDetailViewNotifier: error while loading match data -> $e"); + rethrow; } } - UserStat _calculateUserStats(List ballList) { - final runScored = ballList - .where((element) => element.batsman_id == state.currentUserId) - .fold(0, (sum, element) => sum + element.runs_scored); - final batingStat = - _calculateBatingStats(ballList: ballList, runScored: runScored); - - final bowlingStat = _calculateBowlingStats(ballList); - - final fieldingStat = _calculateFieldingStats(ballList); - - return UserStat( - battingStat: batingStat, - bowlingStat: bowlingStat, - fieldingStat: fieldingStat, - ); - } - - BattingStat _calculateBatingStats({ - required List ballList, - required int runScored, - }) { - final dismissal = ballList - .where((element) => element.player_out_id == state.currentUserId) - .length; - - final ballFaced = ballList - .where((element) => - element.batsman_id == state.currentUserId && - (element.extras_type == null || - element.extras_type == ExtrasType.legBye || - element.extras_type == ExtrasType.bye)) - .length; - - final average = dismissal == 0 ? 0.0 : runScored / dismissal; - - final strikeRate = ballFaced == 0 ? 0.0 : (runScored / ballFaced) * 100.0; - - return BattingStat( - average: average, - strikeRate: strikeRate, - ballFaced: ballFaced, - runScored: runScored, - ); - } - - BowlingStat _calculateBowlingStats(List ballList) { - final deliveries = - ballList.where((element) => element.bowler_id == state.currentUserId); - - final wicketTaken = deliveries - .where((element) => - element.wicket_type != null && - (element.wicket_type == WicketType.retired || - element.wicket_type == WicketType.retiredHurt || - element.wicket_type == WicketType.timedOut)) - .length; - - final bowledBallCount = deliveries - .where((element) => - element.wicket_type != WicketType.retired && - element.wicket_type != WicketType.retiredHurt && - element.wicket_type != WicketType.timedOut && - element.extras_type != ExtrasType.penaltyRun) - .length; - - final bowledBallCountForEconomyRate = deliveries - .where((element) => - (element.extras_type == null || - element.extras_type == ExtrasType.legBye || - element.extras_type == ExtrasType.bye) && - element.wicket_type != WicketType.retired && - element.wicket_type != WicketType.retiredHurt && - element.wicket_type != WicketType.timedOut && - element.extras_type != ExtrasType.penaltyRun) - .length; - - final runsConceded = deliveries - .where((element) => element.extras_type != ExtrasType.penaltyRun) - .fold( - 0, - (sum, element) => - sum + element.runs_scored + (element.extras_awarded ?? 0)); - - final average = wicketTaken == 0 ? 0.0 : runsConceded / wicketTaken; - - final strikeRate = wicketTaken == 0 ? 0.0 : bowledBallCount / wicketTaken; - - final economyRate = bowledBallCountForEconomyRate == 0 - ? 0.0 - : (runsConceded / bowledBallCountForEconomyRate) * 6; - - return BowlingStat( - average: average, - strikeRate: strikeRate, - wicketTaken: wicketTaken, - economyRate: economyRate, - ); - } - - FieldingStat _calculateFieldingStats(List ballList) { - final catches = ballList - .where((element) => - element.wicket_taker_id == state.currentUserId && - (element.wicket_type == WicketType.caught || - element.wicket_type == WicketType.caughtBehind || - element.wicket_type == WicketType.caughtAndBowled)) - .length; - - final runOut = ballList - .where((element) => - element.wicket_taker_id == state.currentUserId && - element.wicket_type == WicketType.runOut) - .length; - - final stumping = ballList - .where((element) => - element.wicket_taker_id == state.currentUserId && - element.wicket_type == WicketType.stumped) - .length; - - return FieldingStat( - catches: catches, - runOut: runOut, - stumping: stumping, - ); - } - - _cancelStreamSubscription() { - _ballScoreStreamSubscription.cancel(); - } - - onResume() { - _cancelStreamSubscription(); - _getUserRelatedBalls(); + void onTabChange(int tab) { + if (state.selectedTab != tab) { + state = state.copyWith(selectedTab: tab); + } } @override void dispose() { - _cancelStreamSubscription(); + _subscription?.cancel(); super.dispose(); } } @@ -204,7 +113,11 @@ class UserStatViewState with _$UserStatViewState { const factory UserStatViewState({ Object? error, String? currentUserId, - UserStat? userStat, + @Default(0) int selectedTab, + @Default(0) int testMatchesCount, + @Default(0) int otherMatchesCount, + @Default(UserStat()) UserStat testStats, + @Default(UserStat()) UserStat otherStats, @Default(false) bool loading, }) = _UserStatViewState; } diff --git a/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.freezed.dart b/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.freezed.dart index 4ce77f05..ac0ea110 100644 --- a/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.freezed.dart +++ b/khelo/lib/ui/flow/stats/user_stat/user_stat_view_model.freezed.dart @@ -18,7 +18,11 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$UserStatViewState { Object? get error => throw _privateConstructorUsedError; String? get currentUserId => throw _privateConstructorUsedError; - UserStat? get userStat => throw _privateConstructorUsedError; + int get selectedTab => throw _privateConstructorUsedError; + int get testMatchesCount => throw _privateConstructorUsedError; + int get otherMatchesCount => throw _privateConstructorUsedError; + UserStat get testStats => throw _privateConstructorUsedError; + UserStat get otherStats => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; /// Create a copy of UserStatViewState @@ -35,9 +39,17 @@ abstract class $UserStatViewStateCopyWith<$Res> { _$UserStatViewStateCopyWithImpl<$Res, UserStatViewState>; @useResult $Res call( - {Object? error, String? currentUserId, UserStat? userStat, bool loading}); + {Object? error, + String? currentUserId, + int selectedTab, + int testMatchesCount, + int otherMatchesCount, + UserStat testStats, + UserStat otherStats, + bool loading}); - $UserStatCopyWith<$Res>? get userStat; + $UserStatCopyWith<$Res> get testStats; + $UserStatCopyWith<$Res> get otherStats; } /// @nodoc @@ -57,7 +69,11 @@ class _$UserStatViewStateCopyWithImpl<$Res, $Val extends UserStatViewState> $Res call({ Object? error = freezed, Object? currentUserId = freezed, - Object? userStat = freezed, + Object? selectedTab = null, + Object? testMatchesCount = null, + Object? otherMatchesCount = null, + Object? testStats = null, + Object? otherStats = null, Object? loading = null, }) { return _then(_value.copyWith( @@ -66,10 +82,26 @@ class _$UserStatViewStateCopyWithImpl<$Res, $Val extends UserStatViewState> ? _value.currentUserId : currentUserId // ignore: cast_nullable_to_non_nullable as String?, - userStat: freezed == userStat - ? _value.userStat - : userStat // ignore: cast_nullable_to_non_nullable - as UserStat?, + selectedTab: null == selectedTab + ? _value.selectedTab + : selectedTab // ignore: cast_nullable_to_non_nullable + as int, + testMatchesCount: null == testMatchesCount + ? _value.testMatchesCount + : testMatchesCount // ignore: cast_nullable_to_non_nullable + as int, + otherMatchesCount: null == otherMatchesCount + ? _value.otherMatchesCount + : otherMatchesCount // ignore: cast_nullable_to_non_nullable + as int, + testStats: null == testStats + ? _value.testStats + : testStats // ignore: cast_nullable_to_non_nullable + as UserStat, + otherStats: null == otherStats + ? _value.otherStats + : otherStats // ignore: cast_nullable_to_non_nullable + as UserStat, loading: null == loading ? _value.loading : loading // ignore: cast_nullable_to_non_nullable @@ -81,13 +113,19 @@ class _$UserStatViewStateCopyWithImpl<$Res, $Val extends UserStatViewState> /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') - $UserStatCopyWith<$Res>? get userStat { - if (_value.userStat == null) { - return null; - } + $UserStatCopyWith<$Res> get testStats { + return $UserStatCopyWith<$Res>(_value.testStats, (value) { + return _then(_value.copyWith(testStats: value) as $Val); + }); + } - return $UserStatCopyWith<$Res>(_value.userStat!, (value) { - return _then(_value.copyWith(userStat: value) as $Val); + /// Create a copy of UserStatViewState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UserStatCopyWith<$Res> get otherStats { + return $UserStatCopyWith<$Res>(_value.otherStats, (value) { + return _then(_value.copyWith(otherStats: value) as $Val); }); } } @@ -101,10 +139,19 @@ abstract class _$$UserStatViewStateImplCopyWith<$Res> @override @useResult $Res call( - {Object? error, String? currentUserId, UserStat? userStat, bool loading}); + {Object? error, + String? currentUserId, + int selectedTab, + int testMatchesCount, + int otherMatchesCount, + UserStat testStats, + UserStat otherStats, + bool loading}); @override - $UserStatCopyWith<$Res>? get userStat; + $UserStatCopyWith<$Res> get testStats; + @override + $UserStatCopyWith<$Res> get otherStats; } /// @nodoc @@ -122,7 +169,11 @@ class __$$UserStatViewStateImplCopyWithImpl<$Res> $Res call({ Object? error = freezed, Object? currentUserId = freezed, - Object? userStat = freezed, + Object? selectedTab = null, + Object? testMatchesCount = null, + Object? otherMatchesCount = null, + Object? testStats = null, + Object? otherStats = null, Object? loading = null, }) { return _then(_$UserStatViewStateImpl( @@ -131,10 +182,26 @@ class __$$UserStatViewStateImplCopyWithImpl<$Res> ? _value.currentUserId : currentUserId // ignore: cast_nullable_to_non_nullable as String?, - userStat: freezed == userStat - ? _value.userStat - : userStat // ignore: cast_nullable_to_non_nullable - as UserStat?, + selectedTab: null == selectedTab + ? _value.selectedTab + : selectedTab // ignore: cast_nullable_to_non_nullable + as int, + testMatchesCount: null == testMatchesCount + ? _value.testMatchesCount + : testMatchesCount // ignore: cast_nullable_to_non_nullable + as int, + otherMatchesCount: null == otherMatchesCount + ? _value.otherMatchesCount + : otherMatchesCount // ignore: cast_nullable_to_non_nullable + as int, + testStats: null == testStats + ? _value.testStats + : testStats // ignore: cast_nullable_to_non_nullable + as UserStat, + otherStats: null == otherStats + ? _value.otherStats + : otherStats // ignore: cast_nullable_to_non_nullable + as UserStat, loading: null == loading ? _value.loading : loading // ignore: cast_nullable_to_non_nullable @@ -147,21 +214,41 @@ class __$$UserStatViewStateImplCopyWithImpl<$Res> class _$UserStatViewStateImpl implements _UserStatViewState { const _$UserStatViewStateImpl( - {this.error, this.currentUserId, this.userStat, this.loading = false}); + {this.error, + this.currentUserId, + this.selectedTab = 0, + this.testMatchesCount = 0, + this.otherMatchesCount = 0, + this.testStats = const UserStat(), + this.otherStats = const UserStat(), + this.loading = false}); @override final Object? error; @override final String? currentUserId; @override - final UserStat? userStat; + @JsonKey() + final int selectedTab; + @override + @JsonKey() + final int testMatchesCount; + @override + @JsonKey() + final int otherMatchesCount; + @override + @JsonKey() + final UserStat testStats; + @override + @JsonKey() + final UserStat otherStats; @override @JsonKey() final bool loading; @override String toString() { - return 'UserStatViewState(error: $error, currentUserId: $currentUserId, userStat: $userStat, loading: $loading)'; + return 'UserStatViewState(error: $error, currentUserId: $currentUserId, selectedTab: $selectedTab, testMatchesCount: $testMatchesCount, otherMatchesCount: $otherMatchesCount, testStats: $testStats, otherStats: $otherStats, loading: $loading)'; } @override @@ -172,8 +259,16 @@ class _$UserStatViewStateImpl implements _UserStatViewState { const DeepCollectionEquality().equals(other.error, error) && (identical(other.currentUserId, currentUserId) || other.currentUserId == currentUserId) && - (identical(other.userStat, userStat) || - other.userStat == userStat) && + (identical(other.selectedTab, selectedTab) || + other.selectedTab == selectedTab) && + (identical(other.testMatchesCount, testMatchesCount) || + other.testMatchesCount == testMatchesCount) && + (identical(other.otherMatchesCount, otherMatchesCount) || + other.otherMatchesCount == otherMatchesCount) && + (identical(other.testStats, testStats) || + other.testStats == testStats) && + (identical(other.otherStats, otherStats) || + other.otherStats == otherStats) && (identical(other.loading, loading) || other.loading == loading)); } @@ -182,7 +277,11 @@ class _$UserStatViewStateImpl implements _UserStatViewState { runtimeType, const DeepCollectionEquality().hash(error), currentUserId, - userStat, + selectedTab, + testMatchesCount, + otherMatchesCount, + testStats, + otherStats, loading); /// Create a copy of UserStatViewState @@ -199,7 +298,11 @@ abstract class _UserStatViewState implements UserStatViewState { const factory _UserStatViewState( {final Object? error, final String? currentUserId, - final UserStat? userStat, + final int selectedTab, + final int testMatchesCount, + final int otherMatchesCount, + final UserStat testStats, + final UserStat otherStats, final bool loading}) = _$UserStatViewStateImpl; @override @@ -207,7 +310,15 @@ abstract class _UserStatViewState implements UserStatViewState { @override String? get currentUserId; @override - UserStat? get userStat; + int get selectedTab; + @override + int get testMatchesCount; + @override + int get otherMatchesCount; + @override + UserStat get testStats; + @override + UserStat get otherStats; @override bool get loading; diff --git a/khelo/lib/ui/flow/team/add_team/add_team_screen.dart b/khelo/lib/ui/flow/team/add_team/add_team_screen.dart index 52795616..acc4b5e7 100644 --- a/khelo/lib/ui/flow/team/add_team/add_team_screen.dart +++ b/khelo/lib/ui/flow/team/add_team/add_team_screen.dart @@ -96,7 +96,7 @@ class _AddTeamScreenState extends ConsumerState { return Stack( children: [ ListView( - padding: const EdgeInsets.all(16) + BottomStickyOverlay.padding, + padding: context.mediaQueryPadding + const EdgeInsets.all(16) + BottomStickyOverlay.padding, children: [ ProfileImageAvatar( size: profileViewHeight, diff --git a/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart b/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart index fbeeabad..b3ff5670 100644 --- a/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart +++ b/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart @@ -142,6 +142,7 @@ class _AddTeamMemberScreenState extends ConsumerState { isShowButton: false, ) : ListView.separated( + padding: const EdgeInsets.only(top: 8, bottom: 70), itemCount: state.searchedUsers.length, itemBuilder: (context, index) { UserModel user = state.searchedUsers[index]; diff --git a/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_screen.dart b/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_screen.dart index d6812d53..50f57b94 100644 --- a/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_screen.dart +++ b/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_screen.dart @@ -125,6 +125,7 @@ class _ContactSelectionScreenState return ListView.separated( itemCount: state.contacts.length, + padding: const EdgeInsets.symmetric(vertical: 16), separatorBuilder: (context, index) => Divider( color: context.colorScheme.outline, height: 32, diff --git a/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_view_model.dart b/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_view_model.dart index d58f4161..2b553898 100644 --- a/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_view_model.dart +++ b/khelo/lib/ui/flow/team/add_team_member/contact_selection/contact_selection_view_model.dart @@ -1,4 +1,5 @@ import 'package:canopas_country_picker/canopas_country_picker.dart'; +import 'package:collection/collection.dart'; import 'package:data/api/user/user_models.dart'; import 'package:data/service/device/device_service.dart'; import 'package:data/service/user/user_service.dart'; @@ -118,8 +119,7 @@ class ContactSelectionViewNotifier phoneNumber = keepDigitAndPlusAtStart(phoneNumber); final matchedCountryCode = CountryCode.allCodes - .where((element) => phoneNumber.startsWith(element.dialCode)) - .firstOrNull + .firstWhereOrNull((element) => phoneNumber.startsWith(element.dialCode)) ?.dialCode; code = matchedCountryCode ?? state.deviceCountryCode; final trimFrom = matchedCountryCode != null ? code?.length : 1; diff --git a/khelo/lib/ui/flow/team/detail/components/primer_progress_bar.dart b/khelo/lib/ui/flow/team/detail/components/primer_progress_bar.dart index 4d832750..780b2669 100644 --- a/khelo/lib/ui/flow/team/detail/components/primer_progress_bar.dart +++ b/khelo/lib/ui/flow/team/detail/components/primer_progress_bar.dart @@ -40,25 +40,27 @@ class PrimerProgressBar extends StatelessWidget { ], ), const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _legendItem( - context, - color: context.colorScheme.positive, - text: context.l10n.team_detail_won_title(status.win), - ), - _legendItem( - context, - color: context.colorScheme.secondary, - text: context.l10n.team_detail_tie_title(status.tie), - ), - _legendItem( - context, - color: context.colorScheme.alert, - text: context.l10n.team_detail_lost_title(status.lost), - ), - ], + MediaQuery.withNoTextScaling( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _legendItem( + context, + color: context.colorScheme.positive, + text: context.l10n.team_detail_won_title(status.win), + ), + _legendItem( + context, + color: context.colorScheme.secondary, + text: context.l10n.team_detail_tie_title(status.tie), + ), + _legendItem( + context, + color: context.colorScheme.alert, + text: context.l10n.team_detail_lost_title(status.lost), + ), + ], + ), ) ], ); diff --git a/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart b/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart index a8905fca..fff765de 100644 --- a/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart +++ b/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart @@ -77,10 +77,13 @@ class TeamDetailMemberContent extends ConsumerWidget { ), ), const SizedBox(width: 10), - Text( - context.l10n.team_detail_add_members_title, - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.primary), + Flexible( + child: Text( + context.l10n.team_detail_add_members_title, + overflow: TextOverflow.ellipsis, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.primary), + ), ) ], ), diff --git a/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart b/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart index c7fde5e6..a3892de3 100644 --- a/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart +++ b/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart @@ -62,6 +62,7 @@ class TeamDetailStatContent extends ConsumerWidget { children: [ Text( context.l10n.team_detail_match_title(playedMatches), + textAlign: TextAlign.center, style: AppTextStyle.header2 .copyWith(color: context.colorScheme.textPrimary), ), @@ -117,10 +118,12 @@ class TeamDetailStatContent extends ConsumerWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), + Expanded( + child: Text( + title, + style: AppTextStyle.body1 + .copyWith(color: context.colorScheme.textPrimary), + ), ), Text( count, diff --git a/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_screen.dart b/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_screen.dart index 05e6e50a..e196be0c 100644 --- a/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_screen.dart +++ b/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_screen.dart @@ -42,6 +42,7 @@ class _MakeAdminScreenState extends ConsumerState { @override Widget build(BuildContext context) { _observePop(); + _observeShowSelectionError(); _observeActionError(); final state = ref.watch(makeTeamAdminStateProvider); return AppPage( @@ -75,6 +76,17 @@ class _MakeAdminScreenState extends ConsumerState { ); } + void _observeShowSelectionError() { + ref.listen( + makeTeamAdminStateProvider.select((value) => value.showSelectionError), + (previous, next) { + if (next) { + showErrorSnackBar( + context: context, error: context.l10n.make_admin_selection_error); + } + }); + } + void _observeActionError() { ref.listen(makeTeamAdminStateProvider.select((value) => value.actionError), (previous, next) { @@ -93,46 +105,49 @@ class _MakeAdminScreenState extends ConsumerState { (element) => element.id == widget.team.created_by, orElse: () => const TeamPlayer(id: '')); - return ListView.separated( - padding: - const EdgeInsets.symmetric(vertical: 16) + context.mediaQueryPadding, - itemCount: members.length + 1, - itemBuilder: (context, index) { - if (index == 0) { - return (owner.id.isNotEmpty) - ? UserDetailCell( - user: owner.user, - showPhoneNumber: false, - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - onTap: () => UserDetailSheet.show(context, owner.user), - trailing: Text( - context.l10n.team_detail_make_admin_owner_title, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.primary)), - ) - : const SizedBox(); - } + return Padding( + padding: context.mediaQueryPadding, + child: ListView.separated( + padding: const EdgeInsets.symmetric(vertical: 16), + itemCount: members.length + 1, + itemBuilder: (context, index) { + if (index == 0) { + return (owner.id.isNotEmpty) + ? UserDetailCell( + user: owner.user, + showPhoneNumber: false, + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + onTap: () => UserDetailSheet.show(context, owner.user), + trailing: Text( + context.l10n.team_detail_make_admin_owner_title, + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.primary)), + ) + : const SizedBox(); + } - final player = members[index - 1]; - return UserDetailCell( - user: player.user, - showPhoneNumber: false, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - onTap: () => UserDetailSheet.show(context, player.user), - trailing: RoundedCheckBox( - isSelected: state.selectedPlayers - .any((element) => element.user == player.user), - onTap: (_) => notifier.selectAdmin(player), - )); - }, - separatorBuilder: (context, index) => (index == 0 && owner.id.isNotEmpty) - ? Divider( - height: 24, - thickness: 1, - color: context.colorScheme.outline, - ) - : const SizedBox(height: 16), + final player = members[index - 1]; + return UserDetailCell( + user: player.user, + showPhoneNumber: false, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + onTap: () => UserDetailSheet.show(context, player.user), + trailing: RoundedCheckBox( + isSelected: state.selectedPlayers + .any((element) => element.user == player.user), + onTap: (_) => notifier.selectAdmin(player), + )); + }, + separatorBuilder: (context, index) => + (index == 0 && owner.id.isNotEmpty) + ? Divider( + height: 24, + thickness: 1, + color: context.colorScheme.outline, + ) + : const SizedBox(height: 16), + ), ); } } diff --git a/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.dart b/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.dart index 8f2c1a6d..6d6fe3d2 100644 --- a/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.dart +++ b/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.dart @@ -27,7 +27,15 @@ class MakeTeamAdminViewNotifier extends StateNotifier { } void selectAdmin(TeamPlayer player) { + state = state.copyWith(showSelectionError: false); final admins = state.selectedPlayers.toList(); + + // Do not allow to select Deactivated user but allow to deselect them. + if (!player.user.isActive && !admins.contains(player)) { + state = state.copyWith(showSelectionError: true); + return; + } + (admins.contains(player)) ? admins.remove(player) : admins.add(player); state = state.copyWith(selectedPlayers: admins, isButtonEnabled: true); } @@ -57,6 +65,7 @@ class MakeTeamAdminState with _$MakeTeamAdminState { Object? actionError, @Default(false) bool pop, @Default(false) bool isButtonEnabled, + @Default(false) bool showSelectionError, @Default([]) List selectedPlayers, }) = _MakeTeamAdminState; } diff --git a/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.freezed.dart b/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.freezed.dart index c32ea1a8..d47a1a9d 100644 --- a/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.freezed.dart +++ b/khelo/lib/ui/flow/team/detail/make_admin/make_team_admin_view_model.freezed.dart @@ -19,6 +19,7 @@ mixin _$MakeTeamAdminState { Object? get actionError => throw _privateConstructorUsedError; bool get pop => throw _privateConstructorUsedError; bool get isButtonEnabled => throw _privateConstructorUsedError; + bool get showSelectionError => throw _privateConstructorUsedError; List get selectedPlayers => throw _privateConstructorUsedError; /// Create a copy of MakeTeamAdminState @@ -38,6 +39,7 @@ abstract class $MakeTeamAdminStateCopyWith<$Res> { {Object? actionError, bool pop, bool isButtonEnabled, + bool showSelectionError, List selectedPlayers}); } @@ -59,6 +61,7 @@ class _$MakeTeamAdminStateCopyWithImpl<$Res, $Val extends MakeTeamAdminState> Object? actionError = freezed, Object? pop = null, Object? isButtonEnabled = null, + Object? showSelectionError = null, Object? selectedPlayers = null, }) { return _then(_value.copyWith( @@ -71,6 +74,10 @@ class _$MakeTeamAdminStateCopyWithImpl<$Res, $Val extends MakeTeamAdminState> ? _value.isButtonEnabled : isButtonEnabled // ignore: cast_nullable_to_non_nullable as bool, + showSelectionError: null == showSelectionError + ? _value.showSelectionError + : showSelectionError // ignore: cast_nullable_to_non_nullable + as bool, selectedPlayers: null == selectedPlayers ? _value.selectedPlayers : selectedPlayers // ignore: cast_nullable_to_non_nullable @@ -91,6 +98,7 @@ abstract class _$$MakeTeamAdminStateImplCopyWith<$Res> {Object? actionError, bool pop, bool isButtonEnabled, + bool showSelectionError, List selectedPlayers}); } @@ -110,6 +118,7 @@ class __$$MakeTeamAdminStateImplCopyWithImpl<$Res> Object? actionError = freezed, Object? pop = null, Object? isButtonEnabled = null, + Object? showSelectionError = null, Object? selectedPlayers = null, }) { return _then(_$MakeTeamAdminStateImpl( @@ -122,6 +131,10 @@ class __$$MakeTeamAdminStateImplCopyWithImpl<$Res> ? _value.isButtonEnabled : isButtonEnabled // ignore: cast_nullable_to_non_nullable as bool, + showSelectionError: null == showSelectionError + ? _value.showSelectionError + : showSelectionError // ignore: cast_nullable_to_non_nullable + as bool, selectedPlayers: null == selectedPlayers ? _value._selectedPlayers : selectedPlayers // ignore: cast_nullable_to_non_nullable @@ -137,6 +150,7 @@ class _$MakeTeamAdminStateImpl implements _MakeTeamAdminState { {this.actionError, this.pop = false, this.isButtonEnabled = false, + this.showSelectionError = false, final List selectedPlayers = const []}) : _selectedPlayers = selectedPlayers; @@ -148,6 +162,9 @@ class _$MakeTeamAdminStateImpl implements _MakeTeamAdminState { @override @JsonKey() final bool isButtonEnabled; + @override + @JsonKey() + final bool showSelectionError; final List _selectedPlayers; @override @JsonKey() @@ -159,7 +176,7 @@ class _$MakeTeamAdminStateImpl implements _MakeTeamAdminState { @override String toString() { - return 'MakeTeamAdminState(actionError: $actionError, pop: $pop, isButtonEnabled: $isButtonEnabled, selectedPlayers: $selectedPlayers)'; + return 'MakeTeamAdminState(actionError: $actionError, pop: $pop, isButtonEnabled: $isButtonEnabled, showSelectionError: $showSelectionError, selectedPlayers: $selectedPlayers)'; } @override @@ -172,6 +189,8 @@ class _$MakeTeamAdminStateImpl implements _MakeTeamAdminState { (identical(other.pop, pop) || other.pop == pop) && (identical(other.isButtonEnabled, isButtonEnabled) || other.isButtonEnabled == isButtonEnabled) && + (identical(other.showSelectionError, showSelectionError) || + other.showSelectionError == showSelectionError) && const DeepCollectionEquality() .equals(other._selectedPlayers, _selectedPlayers)); } @@ -182,6 +201,7 @@ class _$MakeTeamAdminStateImpl implements _MakeTeamAdminState { const DeepCollectionEquality().hash(actionError), pop, isButtonEnabled, + showSelectionError, const DeepCollectionEquality().hash(_selectedPlayers)); /// Create a copy of MakeTeamAdminState @@ -199,6 +219,7 @@ abstract class _MakeTeamAdminState implements MakeTeamAdminState { {final Object? actionError, final bool pop, final bool isButtonEnabled, + final bool showSelectionError, final List selectedPlayers}) = _$MakeTeamAdminStateImpl; @override @@ -208,6 +229,8 @@ abstract class _MakeTeamAdminState implements MakeTeamAdminState { @override bool get isButtonEnabled; @override + bool get showSelectionError; + @override List get selectedPlayers; /// Create a copy of MakeTeamAdminState diff --git a/khelo/lib/ui/flow/team/detail/team_detail_screen.dart b/khelo/lib/ui/flow/team/detail/team_detail_screen.dart index 894a7381..8b2baadb 100644 --- a/khelo/lib/ui/flow/team/detail/team_detail_screen.dart +++ b/khelo/lib/ui/flow/team/detail/team_detail_screen.dart @@ -33,12 +33,6 @@ class TeamDetailScreen extends ConsumerStatefulWidget { } class _TeamDetailScreenState extends ConsumerState { - final List _tabs = [ - const TeamDetailMatchContent(), - const TeamDetailMemberContent(), - const TeamDetailStatContent(), - ]; - late PageController _controller; late TeamDetailViewNotifier notifier; @@ -131,6 +125,7 @@ class _TeamDetailScreenState extends ConsumerState { return Padding( padding: context.mediaQueryPadding, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 16), @@ -142,28 +137,30 @@ class _TeamDetailScreenState extends ConsumerState { } Widget _tabView(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 9), + final tabs = [ + context.l10n.team_detail_match_tab_title, + context.l10n.team_detail_member_tab_title, + context.l10n.team_detail_stat_tab_title + ]; + + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9), + scrollDirection: Axis.horizontal, child: Row( - children: [ - TabButton( - context.l10n.team_detail_match_tab_title, - selected: _selectedTab == 0, - onTap: () => _controller.jumpToPage(0), - ), - const SizedBox(width: 8), - TabButton( - context.l10n.team_detail_member_tab_title, - selected: _selectedTab == 1, - onTap: () => _controller.jumpToPage(1), - ), - const SizedBox(width: 8), - TabButton( - context.l10n.team_detail_stat_tab_title, - selected: _selectedTab == 2, - onTap: () => _controller.jumpToPage(2), + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + tabs.length, + (index) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: TabButton( + tabs[index], + onTap: () { + _controller.jumpToPage(index); + }, + selected: index == _selectedTab, + ), ), - ], + ), ), ); } @@ -172,11 +169,12 @@ class _TeamDetailScreenState extends ConsumerState { return Expanded( child: PageView( controller: _controller, - children: _tabs, - onPageChanged: (index) { - notifier.onTabChange(index); - setState(() {}); - }, + onPageChanged: notifier.onTabChange, + children: const [ + TeamDetailMatchContent(), + TeamDetailMemberContent(), + TeamDetailStatContent(), + ], ), ); } diff --git a/khelo/lib/ui/flow/team/search_team/search_team_screen.dart b/khelo/lib/ui/flow/team/search_team/search_team_screen.dart index 018ad3db..873f1810 100644 --- a/khelo/lib/ui/flow/team/search_team/search_team_screen.dart +++ b/khelo/lib/ui/flow/team/search_team/search_team_screen.dart @@ -102,13 +102,16 @@ class _SearchTeamScreenState extends ConsumerState { BuildContext context, SearchTeamState state, ) { - return SearchTextField( - controller: state.searchController, - onChange: notifier.onSearchChanged, - hintText: context.l10n.search_team_search_placeholder_title, - suffixIcon: state.searchInProgress - ? const UnconstrainedBox(child: AppProgressIndicator(radius: 8)) - : const SizedBox(), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SearchTextField( + controller: state.searchController, + onChange: notifier.onSearchChanged, + hintText: context.l10n.search_team_search_placeholder_title, + suffixIcon: state.searchInProgress + ? const UnconstrainedBox(child: AppProgressIndicator(radius: 8)) + : const SizedBox(), + ), ); } @@ -128,12 +131,14 @@ class _SearchTeamScreenState extends ConsumerState { } return ListView( + padding: const EdgeInsets.symmetric(vertical: 16), children: [ - for (final team in state.searchResults) ...[ - _teamProfileCell(context, state, team), - Divider(color: context.colorScheme.outline), + for (int index = 0; index < state.searchResults.length; index++) ...[ + _teamProfileCell(context, state, state.searchResults[index]), + if (index != state.searchResults.length - 1) + Divider(color: context.colorScheme.outline), ], - const SizedBox(height: 24), + const SizedBox(height: 16), Text( context.l10n.search_team_your_teams_title, style: AppTextStyle.header2 diff --git a/khelo/lib/ui/flow/team/team_list_screen.dart b/khelo/lib/ui/flow/team/team_list_screen.dart index 3d5cbcb9..f0af239b 100644 --- a/khelo/lib/ui/flow/team/team_list_screen.dart +++ b/khelo/lib/ui/flow/team/team_list_screen.dart @@ -80,7 +80,7 @@ class _TeamListScreenState extends ConsumerState return (state.filteredTeams.isNotEmpty) ? ListView.separated( itemCount: state.filteredTeams.length, - padding: const EdgeInsets.symmetric(horizontal: 16) + + padding: const EdgeInsets.fromLTRB(16, 16, 16, 8) + context.mediaQueryPadding, separatorBuilder: (context, index) => Divider(color: context.colorScheme.outline), diff --git a/khelo/lib/ui/flow/team/user_detail/component/user_detail_batting_content.dart b/khelo/lib/ui/flow/team/user_detail/component/user_detail_batting_content.dart index 8642abdd..9d30e8fc 100644 --- a/khelo/lib/ui/flow/team/user_detail/component/user_detail_batting_content.dart +++ b/khelo/lib/ui/flow/team/user_detail/component/user_detail_batting_content.dart @@ -1,18 +1,27 @@ +import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/ui/flow/team/user_detail/component/user_detail_bowling_content.dart'; -import 'package:khelo/ui/flow/team/user_detail/user_detail_view_model.dart'; class UserDetailBattingContent extends ConsumerWidget { - const UserDetailBattingContent({super.key}); + final int testMatchesCount; + final int otherMatchesCount; + final BattingStat? testStats; + final BattingStat? otherStats; + + const UserDetailBattingContent({ + super.key, + this.testMatchesCount = 0, + this.otherMatchesCount = 0, + this.testStats, + this.otherStats, + }); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.read(userDetailStateProvider); - final testStats = state.testStats.battingStat; - final otherStats = state.otherStats.battingStat; return ListView( + padding: const EdgeInsets.only(bottom: 40), children: [ statsDataRow( context, @@ -27,8 +36,8 @@ class UserDetailBattingContent extends ConsumerWidget { context, showDivider: false, title: context.l10n.user_detail_matches_title, - subtitle1: state.testMatchesCount.toString(), - subtitle2: state.otherMatchesCount.toString(), + subtitle1: testMatchesCount.toString(), + subtitle2: otherMatchesCount.toString(), ), statsDataRow( context, diff --git a/khelo/lib/ui/flow/team/user_detail/component/user_detail_bowling_content.dart b/khelo/lib/ui/flow/team/user_detail/component/user_detail_bowling_content.dart index 30d057f8..598e7178 100644 --- a/khelo/lib/ui/flow/team/user_detail/component/user_detail_bowling_content.dart +++ b/khelo/lib/ui/flow/team/user_detail/component/user_detail_bowling_content.dart @@ -1,20 +1,28 @@ +import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; -import 'package:khelo/ui/flow/team/user_detail/user_detail_view_model.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; class UserDetailBowlingContent extends ConsumerWidget { - const UserDetailBowlingContent({super.key}); + final int testMatchesCount; + final int otherMatchesCount; + final BowlingStat? testStats; + final BowlingStat? otherStats; + + const UserDetailBowlingContent({ + super.key, + this.testMatchesCount = 0, + this.otherMatchesCount = 0, + this.testStats, + this.otherStats, + }); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.read(userDetailStateProvider); - final testStats = state.testStats.bowlingStat; - final otherStats = state.otherStats.bowlingStat; - return ListView( + padding: const EdgeInsets.only(bottom: 40), children: [ statsDataRow( context, @@ -29,8 +37,8 @@ class UserDetailBowlingContent extends ConsumerWidget { context, showDivider: false, title: context.l10n.user_detail_matches_title, - subtitle1: state.testMatchesCount.toString(), - subtitle2: state.otherMatchesCount.toString(), + subtitle1: testMatchesCount.toString(), + subtitle2: otherMatchesCount.toString(), ), statsDataRow( context, diff --git a/khelo/lib/ui/flow/team/user_detail/component/user_detail_fielding_content.dart b/khelo/lib/ui/flow/team/user_detail/component/user_detail_fielding_content.dart new file mode 100644 index 00000000..fd3af84d --- /dev/null +++ b/khelo/lib/ui/flow/team/user_detail/component/user_detail_fielding_content.dart @@ -0,0 +1,63 @@ +import 'package:data/api/ball_score/ball_score_model.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/ui/flow/team/user_detail/component/user_detail_bowling_content.dart'; + +class UserDetailFieldingContent extends ConsumerWidget { + final int testMatchesCount; + final int otherMatchesCount; + final FieldingStat? testStats; + final FieldingStat? otherStats; + + const UserDetailFieldingContent({ + super.key, + this.testMatchesCount = 0, + this.otherMatchesCount = 0, + this.testStats, + this.otherStats, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ListView( + padding: const EdgeInsets.only(bottom: 40), + children: [ + statsDataRow( + context, + isHeader: true, + showDivider: false, + title: context.l10n.user_detail_fielding_title, + subtitle1: context.l10n.user_detail_test_title, + subtitle2: context.l10n.common_other_title, + ), + const SizedBox(height: 16), + statsDataRow( + context, + showDivider: false, + title: context.l10n.user_detail_matches_title, + subtitle1: testMatchesCount.toString(), + subtitle2: otherMatchesCount.toString(), + ), + statsDataRow( + context, + title: context.l10n.user_detail_catches_title, + subtitle1: testStats?.catches.toString(), + subtitle2: otherStats?.catches.toString(), + ), + statsDataRow( + context, + title: context.l10n.user_detail_run_out_title, + subtitle1: testStats?.runOut.toString(), + subtitle2: otherStats?.runOut.toString(), + ), + statsDataRow( + context, + title: context.l10n.user_detail_stumping_title, + subtitle1: testStats?.stumping.toString(), + subtitle2: otherStats?.stumping.toString(), + ), + ], + ); + } +} \ No newline at end of file diff --git a/khelo/lib/ui/flow/team/user_detail/component/user_detail_info_content.dart b/khelo/lib/ui/flow/team/user_detail/component/user_detail_info_content.dart index 994d4b36..17cf6544 100644 --- a/khelo/lib/ui/flow/team/user_detail/component/user_detail_info_content.dart +++ b/khelo/lib/ui/flow/team/user_detail/component/user_detail_info_content.dart @@ -1,39 +1,38 @@ +import 'package:data/api/user/user_models.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/domain/formatter/date_formatter.dart'; -import 'package:khelo/ui/flow/team/user_detail/user_detail_view_model.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; class UserDetailInfoContent extends ConsumerWidget { - const UserDetailInfoContent({super.key}); + final UserModel? user; + final String teams; + + const UserDetailInfoContent({super.key, this.user, this.teams = ''}); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.read(userDetailStateProvider); - return ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), children: [ _title(context, context.l10n.user_detail_personal_information_title), const SizedBox(height: 8), - _infoRowView( - context, - context.l10n.user_detail_joining_date_title, - state.user?.created_at?.format(context, DateFormatType.shortDate)), + _infoRowView(context, context.l10n.user_detail_joining_date_title, + user?.created_at?.format(context, DateFormatType.shortDate)), _infoRowView(context, context.l10n.common_gender_title, - state.user?.gender?.getString(context)), + user?.gender?.getString(context)), _infoRowView( - context, context.l10n.common_location_title, state.user?.location), + context, context.l10n.common_location_title, user?.location), _infoRowView(context, context.l10n.user_detail_role_title, - state.user?.player_role?.getString(context)), + user?.player_role?.getString(context)), _infoRowView(context, context.l10n.common_batting_style_title, - state.user?.batting_style?.getString(context)), + user?.batting_style?.getString(context)), _infoRowView(context, context.l10n.common_bowling_style_title, - state.user?.bowling_style?.getString(context)), - _teamParticipationView(context, state), + user?.bowling_style?.getString(context)), + _teamParticipationView(context), ], ); } @@ -74,11 +73,7 @@ class UserDetailInfoContent extends ConsumerWidget { ); } - Widget _teamParticipationView( - BuildContext context, - UserDetailViewState state, - ) { - final teams = state.teams.map((e) => e.name).join(", "); + Widget _teamParticipationView(BuildContext context) { if (teams.isEmpty) { return const SizedBox(); } diff --git a/khelo/lib/ui/flow/team/user_detail/user_detail_screen.dart b/khelo/lib/ui/flow/team/user_detail/user_detail_screen.dart index cec67da3..c19f2dc6 100644 --- a/khelo/lib/ui/flow/team/user_detail/user_detail_screen.dart +++ b/khelo/lib/ui/flow/team/user_detail/user_detail_screen.dart @@ -34,12 +34,6 @@ class UserDetailScreen extends ConsumerStatefulWidget { } class _UserDetailScreenState extends ConsumerState { - final List _tabs = [ - const UserDetailInfoContent(), - const UserDetailBattingContent(), - const UserDetailBowlingContent(), - ]; - late PageController _controller; int get _selectedTab => _controller.hasClients @@ -133,51 +127,74 @@ class _UserDetailScreenState extends ConsumerState { padding: context.mediaQueryPadding, child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ _tabView(context), - _content(context), + _content(context, state), ], ), ); } Widget _tabView(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), + final tabs = [ + context.l10n.user_detail_info_title, + context.l10n.user_detail_batting_title, + context.l10n.user_detail_bowling_title + ]; + + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), + scrollDirection: Axis.horizontal, child: Row( - children: [ - TabButton( - context.l10n.user_detail_info_title, - selected: _selectedTab == 0, - onTap: () => _controller.jumpToPage(0), - ), - const SizedBox(width: 8), - TabButton( - context.l10n.user_detail_batting_title, - selected: _selectedTab == 1, - onTap: () => _controller.jumpToPage(1), + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + tabs.length, + (index) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: TabButton( + tabs[index], + onTap: () { + _controller.jumpToPage(index); + }, + selected: index == _selectedTab, + ), ), - const SizedBox(width: 8), - TabButton( - context.l10n.user_detail_bowling_title, - selected: _selectedTab == 2, - onTap: () => _controller.jumpToPage(2), - ), - ], + ), ), ); } - Widget _content(BuildContext context) { + Widget _content(BuildContext context, UserDetailViewState state) { return Expanded( child: PageView( controller: _controller, - children: _tabs, - onPageChanged: (index) { - notifier.onTabChange(index); - setState(() {}); - }, + onPageChanged: notifier.onTabChange, + children: [ + UserDetailInfoContent( + teams: state.teams.map((e) => e.name).join(", "), + user: state.user, + ), + UserDetailBattingContent( + testMatchesCount: state.testMatchesCount, + otherMatchesCount: state.otherMatchesCount, + testStats: state.testStats.battingStat, + otherStats: state.otherStats.battingStat, + ), + UserDetailBowlingContent( + testMatchesCount: state.testMatchesCount, + otherMatchesCount: state.otherMatchesCount, + testStats: state.testStats.bowlingStat, + otherStats: state.otherStats.bowlingStat, + ), + ], ), ); } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } }