Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
06db5ac
Add username unavailability indicator and validation to Edit Profile …
4555jan Oct 27, 2025
847dd64
fix:mistakenly made another variable for username checking in control…
4555jan Nov 8, 2025
8e79723
changed the minimum length to 7
4555jan Nov 11, 2025
be91e5a
kept the milliseconds to 800 like before
4555jan Nov 11, 2025
e9851b9
solved a localization error
4555jan Nov 11, 2025
4602600
Merge branch 'dev' into feat-username-validation
4555jan Nov 19, 2025
0a0ec5f
Fix username validation and add proper checking state
4555jan Nov 19, 2025
1c427f9
separate username validation error messages and removed unnecessary …
4555jan Nov 23, 2025
ec564d6
removed an unnecessary string
4555jan Nov 23, 2025
d024bd3
fix: added description for last four strings
4555jan Nov 23, 2025
9c49bb7
fix: disabled the save changes button when the username is invalid
4555jan Nov 23, 2025
a8926cd
fix: Remove username availability check and added try catch block
4555jan Nov 23, 2025
1f65dea
Merge branch 'dev' into feat-username-validation
4555jan Nov 25, 2025
0bf8294
Fix single try and catch block
4555jan Nov 25, 2025
9a70110
removed the snakebars and moved to the validator
4555jan Jan 4, 2026
31665f5
Fix fixed save profile button and icon when users
4555jan Jan 6, 2026
cbc7a09
fix: reduced unnecessary conditions
4555jan Jan 7, 2026
55aee19
fix: translations
4555jan Jan 7, 2026
86ef78d
fix: username validation showing red X if username is avilable
4555jan Jan 10, 2026
5f08373
fix: make sure that save button doesnt call api on current username a…
4555jan Jan 10, 2026
683c6c3
fix: checks were failing for usernameavilable so had to edit testfile…
4555jan Jan 10, 2026
1ea8357
fix: checks were failing
4555jan Jan 10, 2026
29392b1
fix: added test for both functions and circular controller added back
4555jan Jan 11, 2026
f44d70c
fix: added mock for another user
4555jan Jan 11, 2026
844fc6d
fix: rethrow showSuccessSnackbar
4555jan Jan 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/controllers/edit_profile_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class EditProfileController extends GetxController {

RxBool isLoading = false.obs;
Rx<bool> usernameAvailable = false.obs;
RxBool usernameAvailableChecking = false.obs;

bool removeImage = false;
bool showSuccessSnackbar = false;
Expand Down
79 changes: 66 additions & 13 deletions lib/views/screens/edit_profile_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:resonate/themes/theme_controller.dart';
import 'package:resonate/utils/debouncer.dart';
import 'package:resonate/utils/enums/log_type.dart';
import 'package:resonate/utils/ui_sizes.dart';
import 'package:resonate/views/widgets/loading_dialog.dart';
import 'package:resonate/views/widgets/snackbar.dart';
import 'package:resonate/l10n/app_localizations.dart';

import '../../controllers/auth_state_controller.dart';
Expand All @@ -22,6 +25,7 @@ class EditProfileScreen extends StatelessWidget {
final AuthStateController authStateController = Get.put(
AuthStateController(),
);
final debouncer = Debouncer(milliseconds: 800);

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -112,16 +116,24 @@ class EditProfileScreen extends StatelessWidget {
keyboardType: TextInputType.text,
autocorrect: false,
decoration: InputDecoration(
// hintText: "Name",
labelText: AppLocalizations.of(context)!.name,
prefixIcon: Icon(Icons.abc_rounded),
),
),
SizedBox(height: UiSizes.height_20),
Obx(
() => TextFormField(
maxLength: 36,
validator: (value) {
if (value!.length > 5) {
final validUsername = RegExp(
r'^[a-zA-Z0-9._-]+$',
).hasMatch(value.trim());
if (!validUsername) {
return AppLocalizations.of(
context,
)!.usernameInvalidOrTaken;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make a separate string for invalid username and taken username so user can have proper feedback

}
return null;
} else {
return AppLocalizations.of(
Expand All @@ -131,22 +143,68 @@ class EditProfileScreen extends StatelessWidget {
},
controller: controller.usernameController,
onChanged: (value) async {
Get.closeCurrentSnackbar();

if (value.length > 5) {
controller.usernameAvailable.value =
await controller.isUsernameAvailable(
value.trim(),
final validUsername = RegExp(
r'^[a-zA-Z0-9._-]+$',
).hasMatch(value.trim());

if (!validUsername || value.trim().length > 36) {
controller.usernameAvailable.value = false;
controller.usernameAvailableChecking.value =
false;
customSnackbar(
AppLocalizations.of(
context,
)!.usernameUnavailable,
"Username can only contain letters, numbers, dots, hyphens, and underscores (max 36 characters)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add localizations

LogType.error,
snackbarDuration: 1,
);
return;
}

controller.usernameAvailableChecking.value = true;

debouncer.run(() async {
controller.usernameAvailable.value =
await controller.isUsernameAvailable(
value.trim(),
);

controller.usernameAvailableChecking.value =
false;

if (!controller.usernameAvailable.value) {
customSnackbar(
AppLocalizations.of(
context,
)!.usernameUnavailable,
AppLocalizations.of(
context,
)!.usernameInvalidOrTaken,
LogType.error,
snackbarDuration: 1,
);
}
});
} else {
controller.usernameAvailable.value = false;
controller.usernameAvailableChecking.value = false;
}
},
keyboardType: TextInputType.text,
autocorrect: false,
decoration: InputDecoration(
// hintText: "Username",
labelText: AppLocalizations.of(context)!.username,
prefixIcon: const Icon(Icons.person),
suffixIcon: controller.usernameAvailable.value
suffixText: controller.usernameAvailableChecking.value
? AppLocalizations.of(context)!.checking
: null,
suffixIcon:
controller.usernameAvailable.value &&
!controller.usernameAvailableChecking.value
? const Icon(
Icons.verified_outlined,
color: Colors.green,
Expand Down Expand Up @@ -298,12 +356,9 @@ class EditProfileScreen extends StatelessWidget {
Column(
children: [
IconButton(
tooltip: AppLocalizations.of(
context,
)!.clickPictureCamera,
tooltip: AppLocalizations.of(context)!.clickPictureCamera,
onPressed: () {
Navigator.pop(context);
// Display Loading Dialog
loadingDialog(context);
editProfileController.pickImageFromCamera();
},
Expand All @@ -322,8 +377,6 @@ class EditProfileScreen extends StatelessWidget {
tooltip: AppLocalizations.of(context)!.pickImageGallery,
onPressed: () {
Navigator.pop(context);

// Display Loading Dialog
loadingDialog(context);
editProfileController.pickImageFromGallery();
},
Expand Down Expand Up @@ -358,4 +411,4 @@ class EditProfileScreen extends StatelessWidget {
],
);
}
}
}