diff --git a/khelo/assets/images/ic_error.svg b/khelo/assets/images/ic_error.svg new file mode 100644 index 00000000..5abf543f --- /dev/null +++ b/khelo/assets/images/ic_error.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/khelo/assets/images/ic_no_internet.svg b/khelo/assets/images/ic_no_internet.svg new file mode 100644 index 00000000..2f6c4560 --- /dev/null +++ b/khelo/assets/images/ic_no_internet.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 6fc709d1..2b523e03 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -77,10 +77,10 @@ }, "error_something_went_wrong": "Something went wrong! Please try again later.", "error_no_internet": "No internet connection! Please check your network connectivity.", - "no_internet_error_title": "No internet connected!!", - "no_internet_error_description": "Please check your network connectivity and try again", + "no_internet_error_title": "No internet!", + "no_internet_error_description": "Network error. Please check connection and try again.", "something_went_wrong_error_title": "Something went wrong", - "something_went_wrong_error_description": "Your content is not loaded because of some unknown problem. We will fix it soon, Please try again later.", + "something_went_wrong_error_description": "This is all the information we have, but we won't rest until we resolve the issue!", "@_ONBOARDING": { }, @@ -121,7 +121,10 @@ "add_team_member_details_title": "Details", "add_team_member_search_placeholder_text": "Search member name", "add_team_member_verify_title": "Verify", - "add_team_member_search_hint_text": "Search the person name above to add them in your team.", + "add_team_member_empty_title": "Your cricket crew awaits!", + "add_team_member_empty_description_text": "The pitch is set, but you need your players. Find them with a quick search.", + "add_team_member_search_no_result_title": "Oops! No User Found", + "add_team_member_search_description_text": "It seems we don't have anyone by that name. Give another name a go!", "add_team_member_added_text": "ADDED", "add_team_member_verify_error_text": "Please enter correct digits", "add_team_member_verify_placeholder_text": "Enter the last {count} digits of the phone number of the selected player.", @@ -138,9 +141,11 @@ "team_list_all_teams_title": "All teams", "team_list_created_by_me_title": "Created by me", "team_list_me_as_member_title": "Me as member", - "team_list_empty_list_description": "Tap on '+' button to create your team.", + "team_list_no_teams_created_title": "No teams created", + "team_list_empty_list_description": "Tap on the “ + ” icon to create a team", - "match_list_no_match_yet_title": "No Matches Yet", + "match_list_no_match_here_title": "No matches here", + "match_list_empty_list_description": "Tap on the “ + ” icon to create a match", "match_list_overs_title": "{overs, plural, =0{{overs} overs} =1{{overs} over} other{{overs} overs}}", "@match_list_overs_title": { "description": "overs, plural, =0{{overs} overs} =1{{overs} over} other{{overs} overs}}", @@ -192,7 +197,8 @@ "add_match_invalid_squad_error": "Please select valid squad", "search_user_hint_title": "Search user name", - "search_user_empty_text": "Search the person name in above input area.", + "search_user_empty_title": "Time to gather your officials!", + "search_user_empty_description_text": "Search and assign officials for your next match. Get ready for cricket action!", "search_team_screen_title": "Search Team", "search_team_your_teams_title": "Your Teams", "search_team_selection_error_text": "The team must have at least 2 players to add it in a match.", @@ -229,12 +235,15 @@ "team_detail_screen_title": "Team Detail", "team_detail_member_tab_title": "Member", - "team_detail_empty_member_title": "No team member are added to the team.", + "team_detail_empty_matches_title": "No matches scheduled yet!", + "team_detail_empty_matches_description_text": "Get your cricket game on! Schedule your cricket match with ease!", + "team_detail_empty_member_title": "You haven't any member!", + "team_detail_empty_member_description_text": "Invite your friends to build your dream team and start tracking matches together.", + "team_detail_empty_stat_title": "No stats available yet!", + "team_detail_empty_stat_description_text": "The players are warming up! Stay tuned for exciting stats as the team progresses.", + "team_detail_add_member_title": "Add member", "team_detail_match_tab_title": "Match", - "team_detail_empty_match_title": "Team hasn't compete with opponent till now.", "team_detail_stat_tab_title": "Stat", - "team_detail_empty_stat_title": "Team's analysis will be shown here based on it's previous matches.", - "team_detail_add_member_title": "Add member", "team_detail_won_title": "Won({win})", "@team_detail_won_title": { "description": "Won({win})", @@ -303,6 +312,10 @@ "match_detail_match_info_tab_title": "Info", "match_detail_highlight_tab_title": "Highlight", "match_detail_overs_tab_title": "Overs", + "match_detail_match_not_started_error_title": "Match has not started yet.", + "match_detail_error_description_text": "The detailed will be available after the start of the match.", + "match_detail_highlight_empty_title": "No highlights currently available.", + "match_detail_highlight_empty_description_text": "No events recorded yet. Check back later for updates and highlights.", "match_info_match_title": "Match", "match_info_date_title": "Date", @@ -418,7 +431,6 @@ "match_commentary_back_to_attack_text": " is back to attack", "match_commentary_end_inning_text_part_1": " wraps up their innings, leaving ", "match_commentary_end_inning_text_part_2": " for victory.", - "match_commentary_empty_commentary_text": "Commentary will be shown here as soon as the match starts.", "match_commentary_inning_count_text": "Inning {count}", "@match_commentary_inning_count_text": { "description": "Inning {count}", @@ -510,8 +522,6 @@ "match_highlight_filter_all_text": "All", "match_highlight_filter_fours_text": "Fours", "match_highlight_filter_sixes_text": "Sixes", - "match_highlight_empty_highlight_text": "No highlights to show here", - "match_overs_empty_over_text": "Overs will be shown here as soon as the match starts", "@_SCOREBOARD": { }, diff --git a/khelo/lib/components/empty_screen.dart b/khelo/lib/components/empty_screen.dart new file mode 100644 index 00000000..f096016b --- /dev/null +++ b/khelo/lib/components/empty_screen.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:style/button/primary_button.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +class EmptyScreen extends StatelessWidget { + final String title; + final String description; + final bool isShowButton; + final String? buttonTitle; + final Function()? onTap; + + const EmptyScreen({ + super.key, + required this.title, + required this.description, + this.isShowButton = true, + this.buttonTitle, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: context.mediaQuerySize.height / 1.4), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + title, + style: AppTextStyle.header2 + .copyWith(color: context.colorScheme.textPrimary), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Text( + description, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textDisabled), + textAlign: TextAlign.center, + ), + if (isShowButton) ...[ + const SizedBox(height: 24), + PrimaryButton( + buttonTitle ?? '', + edgeInsets: + const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + expanded: false, + onPressed: onTap, + ), + ], + ], + ), + ), + ), + ); + } +} diff --git a/khelo/lib/components/error_screen.dart b/khelo/lib/components/error_screen.dart index 6a4a7052..7ad152f6 100644 --- a/khelo/lib/components/error_screen.dart +++ b/khelo/lib/components/error_screen.dart @@ -1,8 +1,11 @@ import 'package:data/errors/app_error.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:khelo/components/place_holder_screen.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; +import '../gen/assets.gen.dart'; + class ErrorScreen extends StatelessWidget { final Object? error; final VoidCallback onRetryTap; @@ -19,7 +22,11 @@ class ErrorScreen extends StatelessWidget { } Widget _noInternetConnectionScreen(BuildContext context) => PlaceHolderScreen( - image: const SizedBox(), + image: SvgPicture.asset( + Assets.images.icNoInternet, + height: 140, + width: 140, + ), title: context.l10n.no_internet_error_title, message: context.l10n.no_internet_error_description, onActionBtnTap: onRetryTap, @@ -27,7 +34,11 @@ class ErrorScreen extends StatelessWidget { ); Widget _errorScreen(BuildContext context) => PlaceHolderScreen( - image: const SizedBox(), + image: SvgPicture.asset( + Assets.images.icError, + height: 140, + width: 140, + ), title: context.l10n.something_went_wrong_error_title, message: context.l10n.something_went_wrong_error_description, onActionBtnTap: onRetryTap, diff --git a/khelo/lib/components/place_holder_screen.dart b/khelo/lib/components/place_holder_screen.dart index ba76815a..12202f6a 100644 --- a/khelo/lib/components/place_holder_screen.dart +++ b/khelo/lib/components/place_holder_screen.dart @@ -28,46 +28,48 @@ class PlaceHolderScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: padding + context.mediaQueryPadding, - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - image, - SizedBox( - height: 40, - width: context.mediaQuerySize.width, - ), - Text( - title, - style: AppTextStyle.header1.copyWith( - color: context.colorScheme.textPrimary, + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: context.mediaQuerySize.height / 1.3), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + image, + SizedBox( + height: 40, + width: context.mediaQuerySize.width, ), - ), - const SizedBox(height: 16), - Visibility( - visible: messageWidget != null, - replacement: Text( - message ?? '', - style: AppTextStyle.subtitle1.copyWith( - color: context.colorScheme.textSecondary, + Text( + title, + style: AppTextStyle.header1.copyWith( + color: context.colorScheme.textPrimary, ), - textAlign: TextAlign.center, ), - child: messageWidget ?? const SizedBox(), - ), - const SizedBox(height: 40), - if (actionBtnTitle != null) ...[ - PrimaryButton( - edgeInsets: - const EdgeInsets.symmetric(vertical: 16, horizontal: 32), - actionBtnTitle ?? '', - onPressed: onActionBtnTap, - expanded: false, + const SizedBox(height: 16), + Visibility( + visible: messageWidget != null, + replacement: Text( + message ?? '', + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textSecondary, + ), + textAlign: TextAlign.center, + ), + child: messageWidget ?? const SizedBox(), ), + const SizedBox(height: 40), + if (actionBtnTitle != null) ...[ + PrimaryButton( + edgeInsets: + const EdgeInsets.symmetric(vertical: 16, horizontal: 32), + actionBtnTitle ?? '', + onPressed: onActionBtnTap, + expanded: false, + ), + ], ], - ], + ), ), ); } diff --git a/khelo/lib/gen/assets.gen.dart b/khelo/lib/gen/assets.gen.dart index b171e6a3..a3a30052 100644 --- a/khelo/lib/gen/assets.gen.dart +++ b/khelo/lib/gen/assets.gen.dart @@ -49,6 +49,9 @@ class $AssetsImagesGen { /// File path: assets/images/ic_edit.svg String get icEdit => 'assets/images/ic_edit.svg'; + /// File path: assets/images/ic_error.svg + String get icError => 'assets/images/ic_error.svg'; + /// File path: assets/images/ic_gallery.svg String get icGallery => 'assets/images/ic_gallery.svg'; @@ -61,6 +64,9 @@ class $AssetsImagesGen { /// File path: assets/images/ic_location.svg String get icLocation => 'assets/images/ic_location.svg'; + /// File path: assets/images/ic_no_internet.svg + String get icNoInternet => 'assets/images/ic_no_internet.svg'; + /// File path: assets/images/ic_privacy_policy.svg String get icPrivacyPolicy => 'assets/images/ic_privacy_policy.svg'; @@ -130,10 +136,12 @@ class $AssetsImagesGen { icContactSupport, icCricket, icEdit, + icError, icGallery, icGroup, icHome, icLocation, + icNoInternet, icPrivacyPolicy, icProfile, icProfileThin, diff --git a/khelo/lib/ui/flow/home/home_screen.dart b/khelo/lib/ui/flow/home/home_screen.dart index 43cacfd9..79f370e9 100644 --- a/khelo/lib/ui/flow/home/home_screen.dart +++ b/khelo/lib/ui/flow/home/home_screen.dart @@ -38,7 +38,9 @@ class _HomeScreenState extends ConsumerState { return AppPage( title: context.l10n.common_matches_title, - body: _body(context, notifier, state), + body: Builder(builder: (context) { + return _body(context, notifier, 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 f1a5e8cd..a72529ff 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 @@ -3,6 +3,7 @@ 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/user_detail_cell.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; @@ -11,7 +12,6 @@ import 'package:khelo/ui/flow/matches/add_match/select_squad/components/user_det import 'package:khelo/ui/flow/team/add_team_member/components/verify_team_member_sheet.dart'; import 'package:style/button/secondary_button.dart'; import 'package:style/extensions/context_extensions.dart'; -import 'package:style/text/app_text_style.dart'; import 'package:style/text/search_text_field.dart'; class SearchUserBottomSheet extends ConsumerWidget { @@ -63,17 +63,14 @@ class SearchUserBottomSheet extends ConsumerWidget { } return state.searchedUsers.isEmpty - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Center( - child: Text( - context.l10n.search_user_empty_text, - textAlign: TextAlign.center, - style: AppTextStyle.body1.copyWith( - color: context.colorScheme.textPrimary, - ), - ), - ), + ? EmptyScreen( + title: (state.searchController.text.isNotEmpty) + ? context.l10n.add_team_member_search_no_result_title + : context.l10n.search_user_empty_title, + description: (state.searchController.text.isNotEmpty) + ? context.l10n.add_team_member_search_description_text + : context.l10n.search_user_empty_description_text, + isShowButton: false, ) : ListView.separated( separatorBuilder: (context, index) { diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_view_model.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_view_model.dart index 416c88af..6bd82ebe 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_view_model.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/search_user/search_user_view_model.dart @@ -24,7 +24,10 @@ class SearchUserViewNotifier extends StateNotifier { Future search(String searchKey) async { try { - state = state.copyWith(error: null); + if (searchKey.isEmpty) { + state = state.copyWith(searchedUsers: [], error: null); + return; + } final users = await _userService.searchUser(searchKey); state = state.copyWith(searchedUsers: users); } catch (e) { @@ -39,9 +42,7 @@ class SearchUserViewNotifier extends StateNotifier { } _debounce = Timer(const Duration(milliseconds: 500), () async { - if (state.searchController.text.isNotEmpty) { - search(state.searchController.text.trim()); - } + search(state.searchController.text.trim()); }); } 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 a05d89f0..91c36f7d 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 @@ -4,6 +4,7 @@ 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/empty_screen.dart'; import 'package:khelo/components/error_screen.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/ui/flow/matches/match_detail/components/commentary_ball_summary.dart'; @@ -38,24 +39,16 @@ class MatchDetailCommentaryView extends ConsumerWidget { ); } - if (state.overList.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - context.l10n.match_commentary_empty_commentary_text, - textAlign: TextAlign.center, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - ); - } - - return ListView( - padding: context.mediaQueryPadding, - children: _buildCommentaryList(context, state), - ); + return (state.overList.isNotEmpty) + ? ListView( + padding: context.mediaQueryPadding, + children: _buildCommentaryList(context, state), + ) + : EmptyScreen( + title: context.l10n.match_detail_match_not_started_error_title, + description: context.l10n.match_detail_error_description_text, + isShowButton: false, + ); } List _buildCommentaryList( 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 6c06ec92..ac2452ea 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 @@ -5,13 +5,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:khelo/components/action_bottom_sheet.dart'; +import 'package:khelo/components/empty_screen.dart'; import 'package:khelo/components/error_screen.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/ui/flow/matches/match_detail/components/commentary_ball_summary.dart'; import 'package:khelo/ui/flow/matches/match_detail/match_detail_tab_view_model.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; -import 'package:style/text/app_text_style.dart'; import 'package:style/widgets/adaptive_outlined_tile.dart'; import '../../../../../gen/assets.gen.dart'; @@ -126,29 +126,23 @@ class MatchDetailHighlightView extends ConsumerWidget { final highlight = state.filteredHighlight; if (highlight.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - context.l10n.match_highlight_empty_highlight_text, - textAlign: TextAlign.center, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), + return EmptyScreen( + title: context.l10n.match_detail_highlight_empty_title, + description: context.l10n.match_detail_highlight_empty_description_text, + isShowButton: false, + ); + } else { + return ListView.separated( + itemCount: highlight.length, + padding: const EdgeInsets.only(top: 24), + separatorBuilder: (context, index) => + Divider(color: context.colorScheme.outline, height: 32), + itemBuilder: (context, index) { + final overSummary = highlight[index]; + return Column(children: _buildHighlightList(context, overSummary)); + }, ); } - - return ListView.separated( - itemCount: highlight.length, - padding: const EdgeInsets.only(top: 24), - separatorBuilder: (context, index) => - Divider(color: context.colorScheme.outline, height: 32), - itemBuilder: (context, index) { - final overSummary = highlight[index]; - return Column(children: _buildHighlightList(context, overSummary)); - }, - ); } List _buildHighlightList( 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 9c94edbe..e9506546 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 @@ -10,6 +10,8 @@ import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_style.dart'; +import '../../../../../components/empty_screen.dart'; + class MatchDetailOversView extends ConsumerWidget { const MatchDetailOversView({super.key}); @@ -34,29 +36,21 @@ class MatchDetailOversView extends ConsumerWidget { ); } - if (state.overList.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - context.l10n.match_overs_empty_over_text, - textAlign: TextAlign.center, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - ); - } - - return ListView( - padding: context.mediaQueryPadding, - children: [ - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: FinalScoreView(), - ), - ..._buildOverList(context, state), - ], + return (state.overList.isNotEmpty) + ? ListView( + padding: context.mediaQueryPadding, + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: FinalScoreView(), + ), + ..._buildOverList(context, state), + ], + ) + : EmptyScreen( + title: context.l10n.match_detail_match_not_started_error_title, + description: context.l10n.match_detail_error_description_text, + isShowButton: false, ); } 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 08858e18..e2a94f15 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 @@ -13,6 +13,8 @@ import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_style.dart'; +import '../../../../../components/empty_screen.dart'; + class MatchDetailScorecardView extends ConsumerWidget { const MatchDetailScorecardView({super.key}); @@ -36,19 +38,20 @@ class MatchDetailScorecardView extends ConsumerWidget { ); } - if (state.overList.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - context.l10n.match_scorecard_empty_scorecard_text, - textAlign: TextAlign.center, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - ); - } + return (state.overList.isNotEmpty) + ? _content(context, notifier, state) + : EmptyScreen( + title: context.l10n.match_detail_match_not_started_error_title, + description: context.l10n.match_detail_error_description_text, + isShowButton: false, + ); + } + + Widget _content( + BuildContext context, + MatchDetailTabViewNotifier notifier, + MatchDetailTabState state, + ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/khelo/lib/ui/flow/matches/match_list_screen.dart b/khelo/lib/ui/flow/matches/match_list_screen.dart index e1398dbe..90312967 100644 --- a/khelo/lib/ui/flow/matches/match_list_screen.dart +++ b/khelo/lib/ui/flow/matches/match_list_screen.dart @@ -1,13 +1,13 @@ import 'package:data/api/match/match_model.dart'; 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:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; -import 'package:style/text/app_text_style.dart'; import 'match_list_view_model.dart'; @@ -60,47 +60,43 @@ class _MatchListScreenState extends ConsumerState ); } - if (state.matches != null && state.matches!.isNotEmpty) { - return ListView.separated( - padding: context.mediaQueryPadding + - const EdgeInsets.all(16) + - const EdgeInsets.only(bottom: 70), - itemCount: state.matches!.length, - separatorBuilder: (context, index) { - return const SizedBox(height: 16); - }, - itemBuilder: (context, index) { - final match = state.matches![index]; - return MatchDetailCell( - match: match, - showActionButtons: match.created_by == state.currentUserId, - onTap: () => - AppRoute.matchDetailTab(matchId: match.id ?? "").push(context), - onActionTap: () { - if (match.match_status == MatchStatus.yetToStart) { - AppRoute.addMatch(matchId: match.id).push(context); - } else { - if (match.toss_decision == null || - match.toss_winner_id == null) { - AppRoute.addTossDetail(matchId: match.id ?? "INVALID_ID") - .push(context); - } else { - AppRoute.scoreBoard(matchId: match.id ?? "INVALID_ID") - .push(context); - } - } + return (state.matches != null && state.matches!.isNotEmpty) + ? ListView.separated( + padding: context.mediaQueryPadding + + const EdgeInsets.all(16) + + const EdgeInsets.only(bottom: 70), + itemCount: state.matches!.length, + separatorBuilder: (context, index) { + return const SizedBox(height: 16); }, + itemBuilder: (context, index) { + final match = state.matches![index]; + return MatchDetailCell( + match: match, + showActionButtons: match.created_by == state.currentUserId, + onTap: () => AppRoute.matchDetailTab(matchId: match.id ?? "") + .push(context), + onActionTap: () { + if (match.match_status == MatchStatus.yetToStart) { + AppRoute.addMatch(matchId: match.id).push(context); + } else { + if (match.toss_decision == null || + match.toss_winner_id == null) { + AppRoute.addTossDetail(matchId: match.id ?? "INVALID_ID") + .push(context); + } else { + AppRoute.scoreBoard(matchId: match.id ?? "INVALID_ID") + .push(context); + } + } + }, + ); + }, + ) + : EmptyScreen( + title: context.l10n.match_list_no_match_here_title, + description: context.l10n.match_list_empty_list_description, + isShowButton: false, ); - }, - ); - } else { - return Center( - child: Text( - context.l10n.match_list_no_match_yet_title, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ); - } } } 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 61358e54..b24ddbf3 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 @@ -106,7 +106,8 @@ class _MyGameTabScreenState extends ConsumerState }, ), const Spacer(), - if (_selectedTab == 1) ...[ + if (_selectedTab == 1 && + ref.watch(teamListViewStateProvider).teams.isNotEmpty) ...[ actionButton(context, onPressed: () => ref .read(teamListViewStateProvider.notifier) 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 index 8fa9a705..be522c47 100644 --- 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 @@ -1,13 +1,14 @@ 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'; -import 'package:style/text/app_text_style.dart'; class UserMatchListScreen extends ConsumerStatefulWidget { const UserMatchListScreen({super.key}); @@ -56,27 +57,26 @@ class _UserMatchListScreenState extends ConsumerState ); } - if (state.matches.isNotEmpty) { - return 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 ?? "INVALID ID") - .push(context), - ), - separatorBuilder: (context, index) => const SizedBox(height: 16), - ); - } else { - return Center( - child: Text( - context.l10n.match_list_no_match_yet_title, - style: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ); - } + 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 ?? "INVALID 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/team/add_team_member/add_team_member_screen.dart b/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart index eea067b0..d3b2f4a9 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 @@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:khelo/components/app_page.dart'; +import 'package:khelo/components/empty_screen.dart'; import 'package:khelo/components/error_screen.dart'; import 'package:khelo/components/error_snackbar.dart'; import 'package:khelo/components/image_avatar.dart'; @@ -67,26 +68,11 @@ class _AddTeamMemberScreenState extends ConsumerState { ), ), ], - body: Builder(builder: (context) { - return Padding( - padding: context.mediaQueryPadding + - const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _searchField(context, state), - if (state.selectedUsers.isNotEmpty) ...[ - _selectedPlayerList(context, state), - ], - _content(context, state), - ], - ), - ); - }), + body: Builder(builder: (context) => _body(context, state)), ); } - Widget _content( + Widget _body( BuildContext context, AddTeamMemberState state, ) { @@ -96,15 +82,37 @@ class _AddTeamMemberScreenState extends ConsumerState { onRetryTap: notifier.onSearchChanged, ); } + + return Padding( + padding: + context.mediaQueryPadding + const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _searchField(context, state), + if (state.selectedUsers.isNotEmpty) ...[ + _selectedPlayerList(context, state), + ], + _searchedPlayerList(context, state), + ], + ), + ); + } + + Widget _searchedPlayerList( + BuildContext context, + AddTeamMemberState state, + ) { return Expanded( - child: state.searchedUsers.isEmpty - ? Center( - child: Text( - context.l10n.add_team_member_search_hint_text, - textAlign: TextAlign.center, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textDisabled), - ), + child: (state.searchedUsers.isEmpty) + ? EmptyScreen( + title: (state.searchController.text.isNotEmpty) + ? context.l10n.add_team_member_search_no_result_title + : context.l10n.add_team_member_empty_title, + description: (state.searchController.text.isNotEmpty) + ? context.l10n.add_team_member_search_description_text + : context.l10n.add_team_member_empty_description_text, + isShowButton: false, ) : ListView.separated( itemCount: state.searchedUsers.length, diff --git a/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart b/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart index c78ca0c1..905892f6 100644 --- a/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart +++ b/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart @@ -27,6 +27,10 @@ class AddTeamMemberViewNotifier extends StateNotifier { Future search(String searchKey) async { try { + if (searchKey.isEmpty) { + state = state.copyWith(searchedUsers: []); + return; + } final users = await _userService.searchUser(searchKey); state = state.copyWith(searchedUsers: users, error: null); } catch (e) { @@ -41,9 +45,7 @@ class AddTeamMemberViewNotifier extends StateNotifier { } _debounce = Timer(const Duration(milliseconds: 500), () async { - if (state.searchController.text.isNotEmpty) { - search(state.searchController.text.trim()); - } + search(state.searchController.text.trim()); }); } diff --git a/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart b/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart index 76fb4bb4..c8c8ae74 100644 --- a/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart +++ b/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart @@ -1,12 +1,11 @@ import 'package:data/api/match/match_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/empty_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/team/detail/team_detail_view_model.dart'; -import 'package:style/extensions/context_extensions.dart'; -import 'package:style/text/app_text_style.dart'; class TeamDetailMatchContent extends ConsumerWidget { const TeamDetailMatchContent({super.key}); @@ -45,17 +44,20 @@ class TeamDetailMatchContent extends ConsumerWidget { separatorBuilder: (context, index) => const SizedBox(height: 16), ); } else { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - context.l10n.team_detail_empty_match_title, - textAlign: TextAlign.center, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - ); + return EmptyScreen( + title: context.l10n.team_detail_empty_matches_title, + description: context.l10n.team_detail_empty_matches_description_text, + buttonTitle: context.l10n.add_match_screen_title, + onTap: () async { + bool? isUpdated = await AppRoute.addMatch( + defaultTeam: (state.team?.players?.length ?? 0) >= 2 + ? state.team + : null) + .push(context); + if (isUpdated == true && context.mounted) { + ref.read(teamDetailStateProvider.notifier).loadTeamById(); + } + }); } } } 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 e9e6062a..9f49c314 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 @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/empty_screen.dart'; import 'package:khelo/components/user_detail_cell.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/ui/app_route.dart'; @@ -39,16 +40,11 @@ class TeamDetailMemberContent extends ConsumerWidget { separatorBuilder: (context, index) => const SizedBox(height: 16), ); } else { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - context.l10n.team_detail_empty_member_title, - textAlign: TextAlign.center, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), + return EmptyScreen( + title: context.l10n.team_detail_empty_member_title, + description: context.l10n.team_detail_empty_member_description_text, + buttonTitle: context.l10n.team_list_add_members_title, + onTap: () => AppRoute.addTeamMember(team: state.team!).push(context), ); } } 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 b43ea37e..c7fde5e6 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 @@ -3,6 +3,7 @@ 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/empty_screen.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/ui/flow/team/detail/components/primer_progress_bar.dart'; import 'package:khelo/ui/flow/team/detail/team_detail_view_model.dart'; @@ -20,16 +21,10 @@ class TeamDetailStatContent extends ConsumerWidget { ?.where((element) => element.match_status == MatchStatus.finish) .isEmpty ?? true) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - context.l10n.team_detail_empty_stat_title, - textAlign: TextAlign.center, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), + return EmptyScreen( + title: context.l10n.team_detail_empty_stat_title, + description: context.l10n.team_detail_empty_stat_description_text, + isShowButton: false, ); } diff --git a/khelo/lib/ui/flow/team/detail/team_detail_view_model.dart b/khelo/lib/ui/flow/team/detail/team_detail_view_model.dart index f196e123..4a6dc0f1 100644 --- a/khelo/lib/ui/flow/team/detail/team_detail_view_model.dart +++ b/khelo/lib/ui/flow/team/detail/team_detail_view_model.dart @@ -67,8 +67,8 @@ class TeamDetailViewNotifier extends StateNotifier { } TeamStat _calculateTeamStat(List matches) { - if (matches.isEmpty) return const TeamStat(); final finishedMatches = _filterFinishedMatches(matches); + if (finishedMatches.isEmpty) return const TeamStat(); return TeamStat( played: finishedMatches.length, status: _teamMatchStatus(finishedMatches), diff --git a/khelo/lib/ui/flow/team/team_list_screen.dart b/khelo/lib/ui/flow/team/team_list_screen.dart index fcddae8f..85fba11f 100644 --- a/khelo/lib/ui/flow/team/team_list_screen.dart +++ b/khelo/lib/ui/flow/team/team_list_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:khelo/components/action_bottom_sheet.dart'; import 'package:khelo/components/app_page.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/domain/extensions/context_extensions.dart'; @@ -75,35 +76,27 @@ class _TeamListScreenState extends ConsumerState BuildContext context, TeamListViewState state, ) { - if (state.filteredTeams.isEmpty) { - return Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - context.l10n.team_list_empty_list_description, - textAlign: TextAlign.center, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - ); - } - - return ListView.separated( - itemCount: state.filteredTeams.length, - padding: const EdgeInsets.symmetric(horizontal: 16) + - context.mediaQueryPadding, - separatorBuilder: (context, index) => - Divider(color: context.colorScheme.outline), - itemBuilder: (context, index) { - final team = state.filteredTeams[index]; - return _teamListCell( - context, - team: team, - showMoreOptionButton: state.currentUserId == team.created_by, - ); - }, - ); + return (state.filteredTeams.isNotEmpty) + ? ListView.separated( + itemCount: state.filteredTeams.length, + padding: const EdgeInsets.symmetric(horizontal: 16) + + context.mediaQueryPadding, + separatorBuilder: (context, index) => + Divider(color: context.colorScheme.outline), + itemBuilder: (context, index) { + final team = state.filteredTeams[index]; + return _teamListCell( + context, + team: team, + showMoreOptionButton: state.currentUserId == team.created_by, + ); + }, + ) + : EmptyScreen( + title: context.l10n.team_list_no_teams_created_title, + description: context.l10n.team_list_empty_list_description, + isShowButton: false, + ); } Widget _teamListCell(