From ce60756368e6ad62db79300dce9238a482c85607 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Thu, 30 May 2024 12:33:20 +0530 Subject: [PATCH 1/2] Refactor add team member --- khelo/assets/locales/app_en.arb | 2 + .../components/user_detail_sheet.dart | 176 +++++---- .../add_team_member_screen.dart | 339 ++++++++---------- .../verify_add_team_member_dialog.dart | 46 ++- 4 files changed, 260 insertions(+), 303 deletions(-) diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 4865a420..77f7101b 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -83,9 +83,11 @@ "add_team_add_as_member_description_text": "Add me as a team member", "add_team_enter_team_name_placeholder_text": "Enter team name", "add_team_location_text": "Location", + "add_team_player_role_title": "Playing role", "add_team_players_text": "Players", "add_team_add_hint_text": "Added user will be shown here, tap on '+' button to add team member.", "add_team_member_screen_title": "Add team member", + "add_team_member_details_title": "Details", "add_team_member_search_placeholder_text": "Search member name", "add_team_member_verify_placeholder_text": "Enter the last five digits of the phone number of the selected player.", "add_team_member_verify_title": "Verify", diff --git a/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart b/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart index 66a64447..bd9c00fc 100644 --- a/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart +++ b/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart @@ -35,118 +35,116 @@ class UserDetailSheet extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: context.mediaQueryPadding + - const EdgeInsets.only(bottom: 24, left: 16, right: 16), - child: Wrap( - alignment: WrapAlignment.center, + padding: context.mediaQueryPadding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - _nameText(context), - Divider( - height: 32, - color: context.colorScheme.outline, - thickness: 2, + _memberProfile(context), + Divider(color: context.colorScheme.outline), + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + context.l10n.add_team_member_details_title, + style: AppTextStyle.header4.copyWith( + color: context.colorScheme.textPrimary, + ), + ), ), - _textDividerView( + _memberDetailCell( context, - title1: user.location.toString(), - title2: user.gender?.getString(context) ?? "", + label: context.l10n.edit_profile_gender_placeholder, + value: user.gender?.getString(context), ), - Divider( - height: 32, - color: context.colorScheme.outline, - thickness: 2, + _memberDetailCell( + context, + label: context.l10n.add_team_location_text, + value: user.location.toString(), + ), + _memberDetailCell( + context, + label: context.l10n.add_team_player_role_title, + value: user.player_role?.getString(context), + ), + _memberDetailCell( + context, + label: context.l10n.edit_profile_batting_style_placeholder, + value: user.batting_style?.getString(context), + ), + _memberDetailCell( + context, + label: context.l10n.edit_profile_bowling_style_placeholder, + value: user.bowling_style?.getString(context), ), - _textDividerView(context, - title1: user.player_role?.getString(context), - title2: user.batting_style?.getString(context), - title3: user.bowling_style?.getString(context)), ], ), ); } - Widget _nameText(BuildContext context) { - return Row( - children: [ - ImageAvatar( + Widget _memberProfile(BuildContext context) { + return Material( + type: MaterialType.transparency, + child: ListTile( + dense: true, + leading: ImageAvatar( initial: user.nameInitial, imageUrl: user.profile_img_url, - size: 70, + size: 40, ), - Expanded( - child: Column( - children: [ - Text( - user.name ?? context.l10n.common_anonymous_title, - style: AppTextStyle.subtitle1.copyWith( - color: context.colorScheme.textPrimary, fontSize: 20), - ), - Text( - user.phone.format(context, StringFormats.obscurePhoneNumber), - style: AppTextStyle.subtitle1.copyWith( - color: context.colorScheme.textPrimary, fontSize: 20), - ), - ], - ), + title: Text( + user.name ?? context.l10n.common_anonymous_title, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), ), - Text.rich( - textAlign: TextAlign.right, - TextSpan( - text: context.l10n.add_match_joined_on_title, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textSecondary), - children: [ - TextSpan( - text: - "\n${DateFormat.yMMMd().format(user.created_at ?? DateTime.now())}", - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textSecondary)) - ])), - ], + subtitle: Text( + user.phone.format(context, StringFormats.obscurePhoneNumber), + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textDisabled), + ), + trailing: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.l10n.add_match_joined_on_title, + style: AppTextStyle.caption + .copyWith(color: context.colorScheme.textDisabled), + ), + Text( + DateFormat.yMMMd().format(user.created_at ?? DateTime.now()), + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textPrimary), + ), + ], + ), + ), ); } - Widget _textDividerView( + Widget _memberDetailCell( BuildContext context, { - required String? title1, - required String? title2, - String? title3, + required String label, + required String? value, }) { - return IntrinsicHeight( + if (value == null) return const SizedBox(); + + return Padding( + padding: const EdgeInsets.all(16.0), child: Row( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (title1 != null) ...[ - Text( - title1, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ], - if (title2 != null) ...[ - VerticalDivider( - thickness: 2, - color: context.colorScheme.outline, - width: 24, + Text( + label, + style: AppTextStyle.body1.copyWith( + color: context.colorScheme.textDisabled, ), - Text( - title2, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary), - ), - ], - if (title3 != null) ...[ - VerticalDivider( - thickness: 2, - color: context.colorScheme.outline, - width: 24, - ), - Text( - title3, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary), + ), + Text( + value, + style: AppTextStyle.body2.copyWith( + color: context.colorScheme.textPrimary, ), - ], + ) ], ), ); 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 319276d4..726f6359 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 @@ -7,85 +7,75 @@ import 'package:khelo/components/app_page.dart'; import 'package:khelo/components/error_screen.dart'; import 'package:khelo/components/error_snackbar.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/enum_extensions.dart'; -import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:khelo/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart'; import 'package:khelo/ui/flow/team/add_team_member/add_team_member_view_model.dart'; import 'package:khelo/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart'; import 'package:style/animations/on_tap_scale.dart'; +import 'package:style/button/action_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; +import 'package:style/text/app_text_field.dart'; import 'package:style/text/app_text_style.dart'; -class AddTeamMemberScreen extends ConsumerWidget { +class AddTeamMemberScreen extends ConsumerStatefulWidget { final TeamModel team; const AddTeamMemberScreen({super.key, required this.team}); - void _observeActionError(BuildContext context, WidgetRef ref) { - ref.listen(addTeamMemberStateProvider.select((value) => value.actionError), - (previous, next) { - if (next != null) { - showErrorSnackBar(context: context, error: next); - } - }); - } + @override + ConsumerState createState() => + _AddTeamMemberScreenState(); +} - void _observeIsAdded( - BuildContext context, WidgetRef ref, AddTeamMemberState state) { - ref.listen(addTeamMemberStateProvider.select((value) => value.isAdded), - (previous, next) { - if (next && context.mounted) { - context.pop(state.selectedUsers); - } - }); +class _AddTeamMemberScreenState extends ConsumerState { + late AddTeamMemberViewNotifier notifier; + + @override + void initState() { + super.initState(); + notifier = ref.read(addTeamMemberStateProvider.notifier); } @override - Widget build(BuildContext context, WidgetRef ref) { - final notifier = ref.watch(addTeamMemberStateProvider.notifier); + Widget build(BuildContext context) { final state = ref.watch(addTeamMemberStateProvider); - _observeActionError(context, ref); - _observeIsAdded(context, ref, state); + _observeActionError(); + _observeIsAdded(state); return AppPage( title: context.l10n.add_team_member_screen_title, actions: [ Visibility( visible: state.selectedUsers.isNotEmpty, child: state.isAddInProgress - ? const AppProgressIndicator( - size: AppProgressIndicatorSize.small, - ) - : IconButton( - onPressed: () { - notifier.addPlayersToTeam(team.id); - }, + ? const AppProgressIndicator(size: AppProgressIndicatorSize.small) + : actionButton(context, + onPressed: () => notifier.addPlayersToTeam(widget.team.id), icon: Icon( Icons.check, color: context.colorScheme.primary, )), ) ], - body: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: SafeArea( + body: Builder(builder: (context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), child: Column( children: [ - _searchTextField(context, notifier, state), - _body(context, notifier, state), - _selectedPlayerList(context, notifier, state), + _searchField(context, state), + _content(context, state), + _selectedPlayerList(context, state), ], ), - ), - ), + ); + }), ); } - Widget _body( + Widget _content( BuildContext context, - AddTeamMemberViewNotifier notifier, AddTeamMemberState state, ) { if (state.error != null) { @@ -105,184 +95,141 @@ class AddTeamMemberScreen extends ConsumerWidget { ), ) : ListView.separated( - separatorBuilder: (context, index) { - return const SizedBox( - height: 16, - ); - }, + padding: context.mediaQueryPadding + + const EdgeInsets.symmetric(vertical: 16), itemCount: state.searchedUsers.length, itemBuilder: (context, index) { UserModel user = state.searchedUsers[index]; - return _userProfileCell(context, notifier, state, user); + return UserDetailCell( + user: user, + onTap: () => UserDetailSheet.show(context, user), + trailing: OnTapScale( + onTap: widget.team.players?.contains(user) != true && + !state.selectedUsers.contains(user) + ? () async { + if (user.phone != null) { + final res = await VerifyAddTeamMemberDialog.show( + context, + phoneNumber: user.phone! + .substring(user.phone!.length - 5)); + if (res != null && res && context.mounted) { + notifier.selectUser(user); + } + } + } + : null, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + borderRadius: BorderRadius.circular(20)), + child: Text( + widget.team.players?.contains(user) == true || + state.selectedUsers.contains(user) + ? context.l10n.add_team_member_added_text + : context.l10n.common_add_title.toUpperCase(), + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textDisabled), + ), + ), + ), + ); }, + separatorBuilder: (context, index) => const SizedBox(height: 16), ), ); } - Widget _searchTextField( + Widget _searchField( BuildContext context, - AddTeamMemberViewNotifier notifier, AddTeamMemberState state, ) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Material( - type: MaterialType.transparency, - child: TextField( - controller: state.searchController, - onChanged: (value) => notifier.onSearchChanged(), - decoration: InputDecoration( - hintText: context.l10n.add_team_member_search_placeholder_text, - contentPadding: const EdgeInsets.all(16), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide.none, - ), - filled: true, - fillColor: context.colorScheme.containerLowOnSurface, - hintStyle: TextStyle(color: context.colorScheme.textDisabled), - prefixIcon: Icon( - Icons.search, - color: context.colorScheme.textDisabled, - size: 24, - ), - ), - onTapOutside: (event) { - FocusManager.instance.primaryFocus?.unfocus(); - }, - style: AppTextStyle.body2.copyWith( - color: context.colorScheme.textPrimary, - ), - ), + return AppTextField( + controller: state.searchController, + borderRadius: BorderRadius.circular(30), + contentPadding: const EdgeInsets.all(16), + borderType: AppTextFieldBorderType.outline, + onChanged: (value) => notifier.onSearchChanged(), + backgroundColor: context.colorScheme.containerLowOnSurface, + hintText: context.l10n.add_team_member_search_placeholder_text, + style: AppTextStyle.body2.copyWith( + color: context.colorScheme.textPrimary, + ), + hintStyle: AppTextStyle.subtitle2.copyWith( + color: context.colorScheme.textDisabled, + ), + borderColor: BorderColor( + focusColor: Colors.transparent, + unFocusColor: Colors.transparent, + ), + onTapOutside: (event) { + FocusManager.instance.primaryFocus?.unfocus(); + }, + prefixIcon: Icon( + Icons.search, + color: context.colorScheme.textDisabled, + size: 24, ), ); } - Widget _userProfileCell( - BuildContext context, - AddTeamMemberViewNotifier notifier, - AddTeamMemberState state, - UserModel user, - ) { - return GestureDetector( - onTap: () => UserDetailSheet.show(context, user), - child: Row( - children: [ - ImageAvatar( - initial: user.nameInitial, - imageUrl: user.profile_img_url, - size: 50, - ), - const SizedBox( - width: 8, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - user.name ?? context.l10n.common_anonymous_title, - style: AppTextStyle.header4 - .copyWith(color: context.colorScheme.textPrimary), - ), - Text( - user.player_role != null - ? user.player_role!.getString(context) - : context.l10n.common_not_specified_title, - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textSecondary)), - if (user.phone != null) ...[ - const SizedBox( - height: 2, - ), - Text( - user.phone - .format(context, StringFormats.obscurePhoneNumber), - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textSecondary), - ), - ], - ], - ), - ), - OnTapScale( - onTap: team.players?.contains(user) != true && - !state.selectedUsers.contains(user) - ? () async { - if (user.phone != null) { - final res = await VerifyAddTeamMemberDialog.show( - context, - phoneNumber: - user.phone!.substring(user.phone!.length - 5)); - if (res != null && res && context.mounted) { - notifier.selectUser(user); - } - } - } - : null, - child: Container( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 6), - decoration: BoxDecoration( - color: context.colorScheme.containerLow, - borderRadius: BorderRadius.circular(20)), - child: Text(team.players?.contains(user) == true || - state.selectedUsers.contains(user) - ? context.l10n.add_team_member_added_text - : context.l10n.common_add_title.toUpperCase()), - ), - ), - ], - )); - } - Widget _selectedPlayerList( BuildContext context, - AddTeamMemberViewNotifier notifier, AddTeamMemberState state, ) { return SizedBox( height: 60, - child: Stack( - children: [ - ListView.separated( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.only(right: 100), - itemBuilder: (context, index) { - final user = state.selectedUsers[index]; - return SizedBox( - height: 60, - width: 65, - child: Stack( - children: [ - ImageAvatar( - initial: user.nameInitial, - imageUrl: user.profile_img_url, - size: 60, - ), - Align( - alignment: Alignment.topRight, - child: OnTapScale( - onTap: () { - notifier.unSelectUser(user); - }, - child: Icon( - Icons.cancel_rounded, - color: context.colorScheme.textPrimary, - )), - ), - ], - ), - ); - }, - separatorBuilder: (context, index) { - return const SizedBox( - width: 20, - ); - }, - itemCount: state.selectedUsers.length), - ], + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: state.selectedUsers.length, + itemBuilder: (context, index) { + final user = state.selectedUsers[index]; + return SizedBox( + height: 60, + width: 65, + child: Stack( + children: [ + ImageAvatar( + initial: user.nameInitial, + imageUrl: user.profile_img_url, + size: 60, + ), + Align( + alignment: Alignment.topRight, + child: OnTapScale( + onTap: () { + notifier.unSelectUser(user); + }, + child: Icon( + Icons.cancel_rounded, + color: context.colorScheme.textPrimary, + )), + ), + ], + ), + ); + }, + separatorBuilder: (context, index) => const SizedBox(width: 20), ), ); } + + void _observeActionError() { + ref.listen(addTeamMemberStateProvider.select((value) => value.actionError), + (previous, next) { + if (next != null) { + showErrorSnackBar(context: context, error: next); + } + }); + } + + void _observeIsAdded(AddTeamMemberState state) { + ref.listen(addTeamMemberStateProvider.select((value) => value.isAdded), + (previous, next) { + if (next && context.mounted) { + context.pop(state.selectedUsers); + } + }); + } } diff --git a/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart b/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart index 27cae57a..be1628d2 100644 --- a/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart +++ b/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_field.dart'; import 'package:style/text/app_text_style.dart'; class VerifyAddTeamMemberDialog extends StatefulWidget { @@ -49,29 +50,38 @@ class _VerifyAddTeamMemberDialogState extends State { children: [ Text( context.l10n.add_team_member_verify_placeholder_text, - style: AppTextStyle.header4.copyWith( + style: AppTextStyle.subtitle1.copyWith( color: context.colorScheme.textPrimary, fontSize: 20, ), ), - TextField( - maxLength: 5, - decoration: InputDecoration( - counterStyle: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textDisabled)), - textAlign: TextAlign.center, - style: AppTextStyle.subtitle1.copyWith( - color: context.colorScheme.textPrimary, - fontSize: 34, + const SizedBox(height: 16), + Flexible( + child: AppTextField( + maxLength: 5, + autoFocus: true, + keyboardType: TextInputType.phone, + borderType: AppTextFieldBorderType.outline, + borderColor: BorderColor( + focusColor: Colors.transparent, + unFocusColor: Colors.transparent, + ), + backgroundColor: context.colorScheme.containerLow, + borderRadius: BorderRadius.circular(12), + contentPadding: const EdgeInsets.symmetric(vertical: 16), + style: AppTextStyle.header1.copyWith( + color: context.colorScheme.textPrimary, + ), + textAlign: TextAlign.center, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), + ], + onChanged: (value) { + setState(() { + verificationNumber = value; + }); + }, ), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), - ], - onChanged: (value) { - setState(() { - verificationNumber = value; - }); - }, ), ], ), From 4151b6863c4f76eda0f24d4954f527729e32b602 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Thu, 30 May 2024 17:14:24 +0530 Subject: [PATCH 2/2] Mr changes --- khelo/lib/domain/formatter/date_formatter.dart | 4 +++- .../add_match/select_squad/components/user_detail_sheet.dart | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/khelo/lib/domain/formatter/date_formatter.dart b/khelo/lib/domain/formatter/date_formatter.dart index 80d99ffc..eb993d57 100644 --- a/khelo/lib/domain/formatter/date_formatter.dart +++ b/khelo/lib/domain/formatter/date_formatter.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; -enum DateFormatType { dateAndTime, date, time, shortDate } +enum DateFormatType { dateAndTime, date, time, shortDate, dayMonthYear } extension DateFormatter on DateTime { String format(BuildContext context, DateFormatType type) { @@ -20,6 +20,8 @@ extension DateFormatter on DateTime { .format(this); case DateFormatType.shortDate: return DateFormat('dd MMM yyyy').format(this); + case DateFormatType.dayMonthYear: + return DateFormat.yMMMd().format(this); } } } diff --git a/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart b/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart index bd9c00fc..a2465a46 100644 --- a/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart +++ b/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart @@ -1,8 +1,8 @@ import 'package:data/api/user/user_models.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; @@ -111,7 +111,8 @@ class UserDetailSheet extends StatelessWidget { .copyWith(color: context.colorScheme.textDisabled), ), Text( - DateFormat.yMMMd().format(user.created_at ?? DateTime.now()), + (user.created_at ?? DateTime.now()) + .format(context, DateFormatType.dayMonthYear), style: AppTextStyle.body2 .copyWith(color: context.colorScheme.textPrimary), ),