Skip to content

feat(auth): add sign-up flow and reusable auth form widgets#14

Merged
CowboyGH merged 15 commits intodevelopfrom
feature/auth-sign-up
Mar 6, 2026
Merged

feat(auth): add sign-up flow and reusable auth form widgets#14
CowboyGH merged 15 commits intodevelopfrom
feature/auth-sign-up

Conversation

@CowboyGH
Copy link
Owner

@CowboyGH CowboyGH commented Mar 6, 2026

🚀 Summary

Implemented the /register vertical slice end-to-end (API client → repository → cubit → UI → router), aligned register contract handling, and reduced auth UI duplication via shared widgets.

✨ Changes

  • ✅ Added /register integration: RegisterRequestDto, RegisterResponseDto, AuthApiClient.register, AuthRepository.signUp, AuthRepositoryImpl.signUp
  • ✅ Added unit tests for AuthRepositoryImpl.signUp
  • ✅ Added SignUpCubit + unit tests
  • ✅ Added SignUpPage and SignUpPageBuilder, wired route and navigation from sign-in
  • ✅ Extracted shared auth UI widgets: AuthFlowShell, AuthTextField, AuthPasswordField, AuthSwitchSection
  • ✅ Updated UserDto.emailVerifiedAt to nullable to match /register payload
  • 🧹 Refactored sign-up consent block into local ConsentRow and improved consent validation message

🧪 Verification

  • flutter analyze
  • flutter test
  • Manual flow: SignIn → SignUp navigation, form validation, consent gating, successful sign-up returns to SignIn (with OTP sent to email), skip on SignUp enters guest flow

@CowboyGH CowboyGH self-assigned this Mar 6, 2026
@CowboyGH CowboyGH added area: network API, requests, and data parsing area: ui/ux Widgets, layout, animations, or design area: logic State management and business logic type: feature New feature or request labels Mar 6, 2026
@CowboyGH CowboyGH changed the title Feature/auth sign up feat(auth): add sign-up flow and reusable auth form widgets Mar 6, 2026
@CowboyGH
Copy link
Owner Author

CowboyGH commented Mar 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

📝 Walkthrough

Walkthrough

Adds a complete sign-up flow across layers: request/response DTOs (RegisterRequestDto, RegisterResponseDto), AuthApiClient.register, AuthRepository.signUp and AuthRepositoryImpl.signUp, SignUpCubit with SignUpState, SignUpPage and SignUpPageBuilder, shared UI widgets (AuthFlowShell, AuthTextField, AuthPasswordField, AuthSwitchSection), validators, router entry for /auth/sign-up, tests for repository and cubit, and makes UserDto.emailVerifiedAt nullable.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/UI
    participant Page as SignUpPage
    participant Cubit as SignUpCubit
    participant Repo as AuthRepositoryImpl
    participant API as AuthApiClient
    participant Net as Network/Dio

    User->>Page: Enter name, email, password
    User->>Page: Tap "Sign Up"
    Page->>Cubit: signUp(name, email, password)
    Cubit->>Cubit: emit(inProgress)
    Cubit->>Repo: signUp(name, email, password)
    Repo->>Repo: build RegisterRequestDto
    Repo->>API: register(request)
    API->>Net: POST /register
    Net-->>API: 200 RegisterResponseDto
    API-->>Repo: RegisterResponseDto
    Repo->>Repo: map to User entity
    Repo-->>Cubit: Result<User, AuthFailure>
    Cubit->>Cubit: emit(succeed(user)) / emit(failed(error))
    Page->>User: show snackbar & navigate on success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through code to wire the gate,
DTOs snug, the router straight,
Cubits hum and validators sing,
A sign-up carrot on a spring,
Hop, click—new users celebrate! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main additions: a complete sign-up flow and reusable authentication form widgets, which aligns with the substantial changes across DTOs, repositories, cubits, pages, and shared widgets.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/auth-sign-up

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (4)
CHANGELOG.md (1)

32-36: Consider grouping the new sign-up changelog bullets.

Five consecutive entries start with Added, which makes this subsection harder to scan. Collapsing them into one parent bullet with subitems would read cleaner.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 32 - 36, Combine the five consecutive "Added"
bullets into a single parent bullet that summarizes "Added sign-up/auth changes"
and list the specific artifacts as indented subitems; include subitems for the
API and DTOs (RegisterRequestDto, RegisterResponseDto, AuthApiClient.register),
data/domain changes (AuthRepository.signUp, AuthRepositoryImpl), presentation
(SignUpCubit, SignUpPageBuilder, SignUpPage), tests (AuthRepositoryImpl.signUp
tests, SignUpCubit tests), and shared UI widgets (AuthFlowShell, AuthTextField,
AuthPasswordField, AuthSwitchSection) so the changelog reads as one coherent
grouped entry with clear subentries.
lib/features/auth/presentation/widgets/auth_flow_shell.dart (1)

34-39: Consider using theme colors for dark mode support.

The hardcoded Colors.white background won't adapt to dark theme. If dark mode support is planned, consider using theme-aware colors.

 decoration: BoxDecoration(
-  color: Colors.white,
+  color: Theme.of(context).colorScheme.surface,
   borderRadius: BorderRadius.circular(24),
 ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/auth/presentation/widgets/auth_flow_shell.dart` around lines 34
- 39, The Container in AuthFlowShell uses a hardcoded Colors.white in its
BoxDecoration which won't adapt to dark mode; replace that with a theme-aware
color (e.g. Theme.of(context).colorScheme.surface or
Theme.of(context).cardColor) so the background follows light/dark themes, e.g.
update the BoxDecoration's color property to use
Theme.of(context).colorScheme.surface (or cardColor) and ensure the enclosing
build method has access to the BuildContext.
lib/features/auth/presentation/widgets/auth_password_field.dart (1)

54-63: Consider adding semantic label for accessibility.

The visibility toggle IconButton lacks a semantic label, which may impact screen reader users. Adding a tooltip provides both visual and accessibility benefits.

 suffixIcon: IconButton(
+  tooltip: _isPasswordVisible ? 'Hide password' : 'Show password',
   onPressed: widget.enabled
       ? () => setState(() {
           _isPasswordVisible = !_isPasswordVisible;
         })
       : null,
   icon: Icon(
     _isPasswordVisible ? Icons.visibility_rounded : Icons.visibility_off_rounded,
   ),
 ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/auth/presentation/widgets/auth_password_field.dart` around lines
54 - 63, The IconButton used as the password visibility toggle (in the
suffixIcon) is missing an accessibility label/tooltip; update the IconButton in
auth_password_field.dart (the widget that toggles _isPasswordVisible and uses
widget.enabled) to include a descriptive semantic label/tooltip (e.g., "Show
password"/"Hide password" based on _isPasswordVisible) so screen readers and
hover/tooltips provide context, ensuring the label changes when
_isPasswordVisible toggles and remains null/disabled when widget.enabled is
false.
test/features/auth/support/auth_dto_fixtures.dart (1)

20-27: Default emailVerifiedAt to null in shared fixtures.

The type is now nullable, but the common fixture still defaults to the old non-null shape. Making unverified the default broadens coverage for the new register contract and makes verified cases explicit.

🧪 Suggested change
-  String? emailVerifiedAt = 'emailVerifiedAt',
+  String? emailVerifiedAt,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/features/auth/support/auth_dto_fixtures.dart` around lines 20 - 27, The
shared fixture createUserDto currently defaults the nullable parameter
emailVerifiedAt to the string 'emailVerifiedAt'; change the default to null so
unverified users are the default in tests. Update the createUserDto signature
(the emailVerifiedAt parameter) to use emailVerifiedAt = null (keeping its
nullable type String?) and ensure any tests that relied on the old default
explicitly pass a verified timestamp where needed (search for createUserDto
calls to update those cases).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/auth/data/repositories/auth_repository_impl.dart`:
- Around line 47-51: In signUp (in auth_repository_impl.dart) don't assume
RegisterResponseDto is successful; check response.success and only return
Result.success(response.user.toEntity()) when true, otherwise return
Result.failure with an AuthFailure carrying response.message (map non-401
failures to a message-bearing AuthFailure and preserve UnauthorizedAuthFailure
behavior if applicable); update the signUp flow to convert
RegisterResponseDto.message -> user-facing AuthFailure and ensure Result.failure
is returned when response.success is false.

In `@lib/features/auth/presentation/pages/sign_up_page.dart`:
- Around line 39-45: Trim the incoming value before performing any validations
in _nameValidator: assign a local variable like trimmed = value?.trim() and use
that for the empty check and the length check (so surrounding whitespace doesn't
inflate length), and apply the same change to the other validator referenced at
lines 108-109 (e.g., _surnameValidator) to ensure consistency with _submit()
which trims input before sending.
- Around line 257-283: The RichText TextSpan children in SignUpPage are styled
like links but have no gesture recognizers or destinations; add
TapGestureRecognizer instances (importing/using TapGestureRecognizer from
gestures) for the two underlined TextSpans, wire them to Navigator.push to open
new routes, and manage their lifecycle by holding them as fields on the
SignUpPageState and disposing them in dispose(); also create simple target pages
(e.g., PrivacyPolicyPage and DataProcessingConsentPage classes/widgets) and
register/push them from the recognizers so tapping the underlined text navigates
to those pages.
- Around line 234-255: Replace the custom GestureDetector/Container checkbox
with Flutter's built-in Checkbox to restore keyboard and assistive-tech support:
in sign_up_page.dart remove the GestureDetector/Container block and use
Checkbox(value: isAgree, onChanged: enabled ? (v) => onTap?.call() /* or adapt
to accept ValueChanged<bool?> */ : null, activeColor:
Theme.of(context).colorScheme.primary, checkColor:
Theme.of(context).colorScheme.onPrimary) wrapped in a SizedBox(width: 48,
height: 48) (or Padding to ensure a 48×48 tap target) and add a semanticLabel
via Semantics(label: 'Agree to terms', child: ...) if needed; keep the existing
enabled/isAgree state usage and ensure the onTap callback signature is adapted
if necessary to accept the boolean from Checkbox.onChanged.
- Around line 137-141: The "Пропустить" TextButton is currently disabled in
non-debug builds due to the condition (!kDebugMode || isInProgress) which makes
the guest flow unreachable in production; update the topRightAction logic in
sign_up_page.dart to either remove the kDebugMode gate so the button is enabled
in production (i.e., use isInProgress alone to disable while loading and keep
onPressed calling context.read<AuthSessionCubit>().continueAsGuest()), or if
guest mode is truly dev-only, replace the disabling approach with visibility
logic so the TextButton is not rendered at all when !kDebugMode (wrap the widget
with a conditional or Visibility and keep isInProgress gating for enabled state)
and add a short comment explaining the chosen behavior.
- Around line 76-84: The sign-up validator (allowedCharsPattern, hasLetter,
hasDigit and max 64) in sign_up_page.dart is more restrictive than
sign_in_page.dart (which allows any character and max 128); confirm the backend
/register password policy and then update the sign_up_page.dart validators to
match it—either remove the allowedCharsPattern restriction to permit symbols (or
relax its regex), align the length limit to the backend (and to
sign_in_page.dart), and keep/adjust the hasLetter/hasDigit checks only if the
backend requires them so both sign-up and sign-in use the same rules.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Around line 32-36: Combine the five consecutive "Added" bullets into a single
parent bullet that summarizes "Added sign-up/auth changes" and list the specific
artifacts as indented subitems; include subitems for the API and DTOs
(RegisterRequestDto, RegisterResponseDto, AuthApiClient.register), data/domain
changes (AuthRepository.signUp, AuthRepositoryImpl), presentation (SignUpCubit,
SignUpPageBuilder, SignUpPage), tests (AuthRepositoryImpl.signUp tests,
SignUpCubit tests), and shared UI widgets (AuthFlowShell, AuthTextField,
AuthPasswordField, AuthSwitchSection) so the changelog reads as one coherent
grouped entry with clear subentries.

In `@lib/features/auth/presentation/widgets/auth_flow_shell.dart`:
- Around line 34-39: The Container in AuthFlowShell uses a hardcoded
Colors.white in its BoxDecoration which won't adapt to dark mode; replace that
with a theme-aware color (e.g. Theme.of(context).colorScheme.surface or
Theme.of(context).cardColor) so the background follows light/dark themes, e.g.
update the BoxDecoration's color property to use
Theme.of(context).colorScheme.surface (or cardColor) and ensure the enclosing
build method has access to the BuildContext.

In `@lib/features/auth/presentation/widgets/auth_password_field.dart`:
- Around line 54-63: The IconButton used as the password visibility toggle (in
the suffixIcon) is missing an accessibility label/tooltip; update the IconButton
in auth_password_field.dart (the widget that toggles _isPasswordVisible and uses
widget.enabled) to include a descriptive semantic label/tooltip (e.g., "Show
password"/"Hide password" based on _isPasswordVisible) so screen readers and
hover/tooltips provide context, ensuring the label changes when
_isPasswordVisible toggles and remains null/disabled when widget.enabled is
false.

In `@test/features/auth/support/auth_dto_fixtures.dart`:
- Around line 20-27: The shared fixture createUserDto currently defaults the
nullable parameter emailVerifiedAt to the string 'emailVerifiedAt'; change the
default to null so unverified users are the default in tests. Update the
createUserDto signature (the emailVerifiedAt parameter) to use emailVerifiedAt =
null (keeping its nullable type String?) and ensure any tests that relied on the
old default explicitly pass a verified timestamp where needed (search for
createUserDto calls to update those cases).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 95394020-4e3f-49bb-8c86-f102aad5f330

📥 Commits

Reviewing files that changed from the base of the PR and between 68ef1c9 and 32b6382.

📒 Files selected for processing (22)
  • CHANGELOG.md
  • lib/core/router/router.dart
  • lib/core/router/router_paths.dart
  • lib/features/auth/data/dto/register_request_dto.dart
  • lib/features/auth/data/dto/register_response_dto.dart
  • lib/features/auth/data/dto/user_dto.dart
  • lib/features/auth/data/remote/auth_api_client.dart
  • lib/features/auth/data/repositories/auth_repository_impl.dart
  • lib/features/auth/domain/repositories/auth_repository.dart
  • lib/features/auth/presentation/cubits/sign_up_cubit.dart
  • lib/features/auth/presentation/cubits/sign_up_state.dart
  • lib/features/auth/presentation/pages/sign_in_page.dart
  • lib/features/auth/presentation/pages/sign_up_page.dart
  • lib/features/auth/presentation/pages/sign_up_page_builder.dart
  • lib/features/auth/presentation/widgets/.gitkeep
  • lib/features/auth/presentation/widgets/auth_flow_shell.dart
  • lib/features/auth/presentation/widgets/auth_password_field.dart
  • lib/features/auth/presentation/widgets/auth_switch_section.dart
  • lib/features/auth/presentation/widgets/auth_text_field.dart
  • test/features/auth/data/repositories/auth_repository_impl_sign_up_test.dart
  • test/features/auth/presentation/cubits/sign_up_cubit_test.dart
  • test/features/auth/support/auth_dto_fixtures.dart

@gitguardian
Copy link

gitguardian bot commented Mar 6, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
- - Generic Password 7fe413a test/features/auth/presentation/validators/auth_validators_test.dart View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/auth/presentation/pages/sign_in_page.dart`:
- Around line 104-109: The disabled "Забыли пароль?" TextButton (the
Align/TextButton block with onPressed: null and Text('Забыли пароль?')) should
be hidden until the feature exists; either remove that widget entirely or
conditionally render it. Fix by replacing the block with nothing (e.g., omit it
or return SizedBox.shrink()) or wrap it behind a feature flag/boolean (e.g.,
isForgotPasswordEnabled) and only render Align/TextButton when true, or
implement a real handler by setting onPressed to navigate to the
ForgotPasswordPage via Navigator.push (or call the existing forgot-password
handler) if the flow is ready. Ensure changes are made in the SignInPage where
Align/TextButton currently lives.
- Around line 95-101: The sign-in form currently uses the sign-up password
validator AuthValidators.password which can reject legacy or otherwise-valid
backend passwords; replace it with a simpler sign-in validator (e.g.,
AuthValidators.signInPassword) that only enforces presence. Add a new static
method signInPassword in AuthValidators returning a non-null error only when the
value is null/empty, then update the AuthPasswordField instance (the widget
using _passwordController, enabled: !isInProgress, textInputAction:
TextInputAction.done, onFieldSubmitted: _submit) to use
AuthValidators.signInPassword instead of AuthValidators.password so client-side
validation won’t incorrectly block login attempts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bf93ead9-059d-45ab-b86a-f7f48bf3f32f

📥 Commits

Reviewing files that changed from the base of the PR and between 32b6382 and 7fe413a.

📒 Files selected for processing (4)
  • lib/features/auth/presentation/pages/sign_in_page.dart
  • lib/features/auth/presentation/pages/sign_up_page.dart
  • lib/features/auth/presentation/validators/auth_validators.dart
  • test/features/auth/presentation/validators/auth_validators_test.dart

@CowboyGH CowboyGH merged commit c107f8d into develop Mar 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: logic State management and business logic area: network API, requests, and data parsing area: ui/ux Widgets, layout, animations, or design type: feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant