fix: add ignore_for_file to data connect Repos and modify CI to avoid analyzing deleted files

This commit is contained in:
2026-02-20 19:51:44 +05:30
parent 24835f127e
commit 474be43448
259 changed files with 1810 additions and 1714 deletions

View File

@@ -6,13 +6,13 @@ import 'package:krow_core/core.dart';
import '../../domain/repositories/place_repository.dart';
class PlaceRepositoryImpl implements PlaceRepository {
final http.Client _client;
PlaceRepositoryImpl({http.Client? client}) : _client = client ?? http.Client();
final http.Client _client;
@override
Future<List<String>> searchCities(String query) async {
if (query.isEmpty) return [];
if (query.isEmpty) return <String>[];
final Uri uri = Uri.https(
'maps.googleapis.com',
@@ -39,7 +39,7 @@ class PlaceRepositoryImpl implements PlaceRepository {
} else {
// Handle other statuses (OVER_QUERY_LIMIT, REQUEST_DENIED, etc.)
// Returning empty list for now to avoid crashing UI, ideally log this.
return [];
return <String>[];
}
} else {
throw Exception('Network Error: ${response.statusCode}');

View File

@@ -5,9 +5,9 @@ import 'package:firebase_auth/firebase_auth.dart' as auth;
import '../../domain/repositories/profile_setup_repository.dart';
class ProfileSetupRepositoryImpl implements ProfileSetupRepository {
final DataConnectService _service;
ProfileSetupRepositoryImpl() : _service = DataConnectService.instance;
final DataConnectService _service;
@override
Future<void> submitProfile({

View File

@@ -4,13 +4,13 @@ import 'package:krow_core/core.dart';
///
/// Encapsulates the phone number needed to initiate the sign-in process.
class SignInWithPhoneArguments extends UseCaseArgument {
/// The phone number to be used for sign-in or sign-up.
final String phoneNumber;
/// Creates a [SignInWithPhoneArguments] instance.
///
/// The [phoneNumber] is required.
const SignInWithPhoneArguments({required this.phoneNumber});
/// The phone number to be used for sign-in or sign-up.
final String phoneNumber;
@override
List<Object> get props => <Object>[phoneNumber];

View File

@@ -6,14 +6,6 @@ import '../ui_entities/auth_mode.dart';
/// Encapsulates the verification ID and the SMS code needed to verify
/// a phone number during the authentication process.
class VerifyOtpArguments extends UseCaseArgument {
/// The unique identifier received after requesting an OTP.
final String verificationId;
/// The one-time password (OTP) sent to the user's phone.
final String smsCode;
/// The authentication mode (login or signup).
final AuthMode mode;
/// Creates a [VerifyOtpArguments] instance.
///
@@ -23,6 +15,14 @@ class VerifyOtpArguments extends UseCaseArgument {
required this.smsCode,
required this.mode,
});
/// The unique identifier received after requesting an OTP.
final String verificationId;
/// The one-time password (OTP) sent to the user's phone.
final String smsCode;
/// The authentication mode (login or signup).
final AuthMode mode;
@override
List<Object> get props => <Object>[verificationId, smsCode, mode];

View File

@@ -1,4 +1,3 @@
import 'package:krow_domain/krow_domain.dart';
abstract class ProfileSetupRepository {
Future<void> submitProfile({

View File

@@ -1,9 +1,9 @@
import '../repositories/place_repository.dart';
class SearchCitiesUseCase {
final PlaceRepository _repository;
SearchCitiesUseCase(this._repository);
final PlaceRepository _repository;
Future<List<String>> call(String query) {
return _repository.searchCities(query);

View File

@@ -7,12 +7,12 @@ import '../repositories/auth_repository_interface.dart';
/// This use case delegates the sign-in logic to the [AuthRepositoryInterface].
class SignInWithPhoneUseCase
implements UseCase<SignInWithPhoneArguments, String?> {
final AuthRepositoryInterface _repository;
/// Creates a [SignInWithPhoneUseCase].
///
/// Requires an [AuthRepositoryInterface] to interact with the authentication data source.
SignInWithPhoneUseCase(this._repository);
final AuthRepositoryInterface _repository;
@override
Future<String?> call(SignInWithPhoneArguments arguments) {

View File

@@ -1,9 +1,9 @@
import '../repositories/profile_setup_repository.dart';
class SubmitProfileSetup {
final ProfileSetupRepository repository;
SubmitProfileSetup(this.repository);
final ProfileSetupRepository repository;
Future<void> call({
required String fullName,

View File

@@ -7,12 +7,12 @@ import '../repositories/auth_repository_interface.dart';
///
/// This use case delegates the OTP verification logic to the [AuthRepositoryInterface].
class VerifyOtpUseCase implements UseCase<VerifyOtpArguments, User?> {
final AuthRepositoryInterface _repository;
/// Creates a [VerifyOtpUseCase].
///
/// Requires an [AuthRepositoryInterface] to interact with the authentication data source.
VerifyOtpUseCase(this._repository);
final AuthRepositoryInterface _repository;
@override
Future<User?> call(VerifyOtpArguments arguments) {

View File

@@ -14,16 +14,6 @@ import 'auth_state.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState>
with BlocErrorHandler<AuthState>
implements Disposable {
/// The use case for signing in with a phone number.
final SignInWithPhoneUseCase _signInUseCase;
/// The use case for verifying an OTP.
final VerifyOtpUseCase _verifyOtpUseCase;
int _requestToken = 0;
DateTime? _lastCodeRequestAt;
DateTime? _cooldownUntil;
static const Duration _resendCooldown = Duration(seconds: 31);
Timer? _cooldownTimer;
/// Creates an [AuthBloc].
AuthBloc({
@@ -40,6 +30,16 @@ class AuthBloc extends Bloc<AuthEvent, AuthState>
on<AuthResetRequested>(_onResetRequested);
on<AuthCooldownTicked>(_onCooldownTicked);
}
/// The use case for signing in with a phone number.
final SignInWithPhoneUseCase _signInUseCase;
/// The use case for verifying an OTP.
final VerifyOtpUseCase _verifyOtpUseCase;
int _requestToken = 0;
DateTime? _lastCodeRequestAt;
DateTime? _cooldownUntil;
static const Duration _resendCooldown = Duration(seconds: 31);
Timer? _cooldownTimer;
/// Clears any authentication error from the state.
void _onErrorCleared(AuthErrorCleared event, Emitter<AuthState> emit) {
@@ -111,7 +111,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState>
);
await handleError(
emit: emit,
emit: emit.call,
action: () async {
final String? verificationId = await _signInUseCase(
SignInWithPhoneArguments(
@@ -193,7 +193,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState>
) async {
emit(state.copyWith(status: AuthStatus.loading));
await handleError(
emit: emit,
emit: emit.call,
action: () async {
final User? user = await _verifyOtpUseCase(
VerifyOtpArguments(

View File

@@ -10,14 +10,14 @@ abstract class AuthEvent extends Equatable {
/// Event for requesting a sign-in with a phone number.
class AuthSignInRequested extends AuthEvent {
const AuthSignInRequested({this.phoneNumber, required this.mode});
/// The phone number provided by the user.
final String? phoneNumber;
/// The authentication mode (login or signup).
final AuthMode mode;
const AuthSignInRequested({this.phoneNumber, required this.mode});
@override
List<Object?> get props => <Object?>[phoneNumber, mode];
}
@@ -27,6 +27,12 @@ class AuthSignInRequested extends AuthEvent {
/// This event is dispatched after the user has received an OTP and
/// submits it for verification.
class AuthOtpSubmitted extends AuthEvent {
const AuthOtpSubmitted({
required this.verificationId,
required this.smsCode,
required this.mode,
});
/// The verification ID received after the phone number submission.
final String verificationId;
@@ -36,12 +42,6 @@ class AuthOtpSubmitted extends AuthEvent {
/// The authentication mode (login or signup).
final AuthMode mode;
const AuthOtpSubmitted({
required this.verificationId,
required this.smsCode,
required this.mode,
});
@override
List<Object?> get props => <Object?>[verificationId, smsCode, mode];
}
@@ -51,10 +51,10 @@ class AuthErrorCleared extends AuthEvent {}
/// Event for resetting the authentication flow back to initial.
class AuthResetRequested extends AuthEvent {
/// The authentication mode (login or signup).
final AuthMode mode;
const AuthResetRequested({required this.mode});
/// The authentication mode (login or signup).
final AuthMode mode;
@override
List<Object?> get props => <Object?>[mode];
@@ -62,9 +62,9 @@ class AuthResetRequested extends AuthEvent {
/// Event for ticking down the resend cooldown.
class AuthCooldownTicked extends AuthEvent {
final int secondsRemaining;
const AuthCooldownTicked(this.secondsRemaining);
final int secondsRemaining;
@override
List<Object?> get props => <Object?>[secondsRemaining];
@@ -72,10 +72,10 @@ class AuthCooldownTicked extends AuthEvent {
/// Event for updating the current draft OTP in the state.
class AuthOtpUpdated extends AuthEvent {
/// The current draft OTP.
final String otp;
const AuthOtpUpdated(this.otp);
/// The current draft OTP.
final String otp;
@override
List<Object?> get props => <Object?>[otp];
@@ -83,10 +83,10 @@ class AuthOtpUpdated extends AuthEvent {
/// Event for updating the current draft phone number in the state.
class AuthPhoneUpdated extends AuthEvent {
/// The current draft phone number.
final String phoneNumber;
const AuthPhoneUpdated(this.phoneNumber);
/// The current draft phone number.
final String phoneNumber;
@override
List<Object?> get props => <Object?>[phoneNumber];

View File

@@ -22,6 +22,17 @@ enum AuthStatus {
/// A unified state class for the authentication process.
class AuthState extends Equatable {
const AuthState({
this.status = AuthStatus.initial,
this.verificationId,
this.mode = AuthMode.login,
this.otp = '',
this.phoneNumber = '',
this.errorMessage,
this.cooldownSecondsRemaining = 0,
this.user,
});
/// The current status of the authentication flow.
final AuthStatus status;
@@ -46,17 +57,6 @@ class AuthState extends Equatable {
/// The authenticated user's data (available when status is [AuthStatus.authenticated]).
final User? user;
const AuthState({
this.status = AuthStatus.initial,
this.verificationId,
this.mode = AuthMode.login,
this.otp = '',
this.phoneNumber = '',
this.errorMessage,
this.cooldownSecondsRemaining = 0,
this.user,
});
@override
List<Object?> get props => <Object?>[
status,

View File

@@ -89,7 +89,7 @@ class ProfileSetupBloc extends Bloc<ProfileSetupEvent, ProfileSetupState>
emit(state.copyWith(status: ProfileSetupStatus.loading));
await handleError(
emit: emit,
emit: emit.call,
action: () async {
await _submitProfileSetup(
fullName: state.fullName,
@@ -114,18 +114,18 @@ class ProfileSetupBloc extends Bloc<ProfileSetupEvent, ProfileSetupState>
Emitter<ProfileSetupState> emit,
) async {
if (event.query.isEmpty) {
emit(state.copyWith(locationSuggestions: []));
emit(state.copyWith(locationSuggestions: <String>[]));
return;
}
// For search, we might want to handle errors silently or distinctively
// Using simple try-catch here as it's a search-as-you-type feature where error dialogs are intrusive
try {
final results = await _searchCities(event.query);
final List<String> results = await _searchCities(event.query);
emit(state.copyWith(locationSuggestions: results));
} catch (e) {
// Quietly fail or clear
emit(state.copyWith(locationSuggestions: []));
emit(state.copyWith(locationSuggestions: <String>[]));
}
}
@@ -133,7 +133,7 @@ class ProfileSetupBloc extends Bloc<ProfileSetupEvent, ProfileSetupState>
ProfileSetupClearLocationSuggestions event,
Emitter<ProfileSetupState> emit,
) {
emit(state.copyWith(locationSuggestions: []));
emit(state.copyWith(locationSuggestions: <String>[]));
}
}

View File

@@ -10,11 +10,11 @@ abstract class ProfileSetupEvent extends Equatable {
/// Event triggered when the full name changes.
class ProfileSetupFullNameChanged extends ProfileSetupEvent {
/// The new full name value.
final String fullName;
/// Creates a [ProfileSetupFullNameChanged] event.
const ProfileSetupFullNameChanged(this.fullName);
/// The new full name value.
final String fullName;
@override
List<Object?> get props => <Object?>[fullName];
@@ -22,11 +22,11 @@ class ProfileSetupFullNameChanged extends ProfileSetupEvent {
/// Event triggered when the bio changes.
class ProfileSetupBioChanged extends ProfileSetupEvent {
/// The new bio value.
final String bio;
/// Creates a [ProfileSetupBioChanged] event.
const ProfileSetupBioChanged(this.bio);
/// The new bio value.
final String bio;
@override
List<Object?> get props => <Object?>[bio];
@@ -34,11 +34,11 @@ class ProfileSetupBioChanged extends ProfileSetupEvent {
/// Event triggered when the preferred locations change.
class ProfileSetupLocationsChanged extends ProfileSetupEvent {
/// The new list of locations.
final List<String> locations;
/// Creates a [ProfileSetupLocationsChanged] event.
const ProfileSetupLocationsChanged(this.locations);
/// The new list of locations.
final List<String> locations;
@override
List<Object?> get props => <Object?>[locations];
@@ -46,11 +46,11 @@ class ProfileSetupLocationsChanged extends ProfileSetupEvent {
/// Event triggered when the max distance changes.
class ProfileSetupDistanceChanged extends ProfileSetupEvent {
/// The new max distance value in miles.
final double distance;
/// Creates a [ProfileSetupDistanceChanged] event.
const ProfileSetupDistanceChanged(this.distance);
/// The new max distance value in miles.
final double distance;
@override
List<Object?> get props => <Object?>[distance];
@@ -58,11 +58,11 @@ class ProfileSetupDistanceChanged extends ProfileSetupEvent {
/// Event triggered when the skills change.
class ProfileSetupSkillsChanged extends ProfileSetupEvent {
/// The new list of selected skills.
final List<String> skills;
/// Creates a [ProfileSetupSkillsChanged] event.
const ProfileSetupSkillsChanged(this.skills);
/// The new list of selected skills.
final List<String> skills;
@override
List<Object?> get props => <Object?>[skills];
@@ -70,11 +70,11 @@ class ProfileSetupSkillsChanged extends ProfileSetupEvent {
/// Event triggered when the industries change.
class ProfileSetupIndustriesChanged extends ProfileSetupEvent {
/// The new list of selected industries.
final List<String> industries;
/// Creates a [ProfileSetupIndustriesChanged] event.
const ProfileSetupIndustriesChanged(this.industries);
/// The new list of selected industries.
final List<String> industries;
@override
List<Object?> get props => <Object?>[industries];
@@ -82,11 +82,11 @@ class ProfileSetupIndustriesChanged extends ProfileSetupEvent {
/// Event triggered when the location query changes.
class ProfileSetupLocationQueryChanged extends ProfileSetupEvent {
/// The search query.
final String query;
/// Creates a [ProfileSetupLocationQueryChanged] event.
const ProfileSetupLocationQueryChanged(this.query);
/// The search query.
final String query;
@override
List<Object?> get props => <Object?>[query];

View File

@@ -5,6 +5,19 @@ enum ProfileSetupStatus { initial, loading, success, failure }
/// State for the ProfileSetupBloc.
class ProfileSetupState extends Equatable {
/// Creates a [ProfileSetupState] instance.
const ProfileSetupState({
this.fullName = '',
this.bio = '',
this.preferredLocations = const <String>[],
this.maxDistanceMiles = 25,
this.skills = const <String>[],
this.industries = const <String>[],
this.status = ProfileSetupStatus.initial,
this.errorMessage,
this.locationSuggestions = const <String>[],
});
/// The user's full name.
final String fullName;
@@ -32,19 +45,6 @@ class ProfileSetupState extends Equatable {
/// List of location suggestions from the API.
final List<String> locationSuggestions;
/// Creates a [ProfileSetupState] instance.
const ProfileSetupState({
this.fullName = '',
this.bio = '',
this.preferredLocations = const <String>[],
this.maxDistanceMiles = 25,
this.skills = const <String>[],
this.industries = const <String>[],
this.status = ProfileSetupStatus.initial,
this.errorMessage,
this.locationSuggestions = const <String>[],
});
/// Creates a copy of the current state with updated values.
ProfileSetupState copyWith({
String? fullName,

View File

@@ -17,11 +17,11 @@ import '../widgets/phone_verification_page/phone_input.dart';
/// This page coordinates the authentication flow by switching between
/// [PhoneInput] and [OtpVerification] based on the current [AuthState].
class PhoneVerificationPage extends StatefulWidget {
/// The authentication mode (login or signup).
final AuthMode mode;
/// Creates a [PhoneVerificationPage].
const PhoneVerificationPage({super.key, required this.mode});
/// The authentication mode (login or signup).
final AuthMode mode;
@override
State<PhoneVerificationPage> createState() => _PhoneVerificationPageState();

View File

@@ -157,9 +157,9 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
),
),
child: isCreatingProfile
? ElevatedButton(
? const ElevatedButton(
onPressed: null,
child: const SizedBox(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(strokeWidth: 2),

View File

@@ -3,17 +3,17 @@ import 'package:flutter/material.dart';
/// A widget for displaying a section title and subtitle
class SectionTitleSubtitle extends StatelessWidget {
/// The title of the section
final String title;
/// The subtitle of the section
final String subtitle;
const SectionTitleSubtitle({
super.key,
required this.title,
required this.subtitle,
});
/// The title of the section
final String title;
/// The subtitle of the section
final String subtitle;
@override
Widget build(BuildContext context) {

View File

@@ -3,14 +3,14 @@ import 'package:design_system/design_system.dart';
import 'package:core_localization/core_localization.dart';
class GetStartedActions extends StatelessWidget {
final VoidCallback onSignUpPressed;
final VoidCallback onLoginPressed;
const GetStartedActions({
super.key,
required this.onSignUpPressed,
required this.onLoginPressed,
});
final VoidCallback onSignUpPressed;
final VoidCallback onLoginPressed;
@override
Widget build(BuildContext context) {

View File

@@ -15,7 +15,7 @@ class _GetStartedBackgroundState extends State<GetStartedBackground> {
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
children: <Widget>[
const SizedBox(height: UiConstants.space8),
// Logo
Image.asset(
@@ -35,7 +35,7 @@ class _GetStartedBackgroundState extends State<GetStartedBackground> {
child: ClipOval(
child: Stack(
fit: StackFit.expand,
children: [
children: <Widget>[
// Layer 1: The Fallback Logo (Always visible until image loads)
Padding(
padding: const EdgeInsets.all(UiConstants.space12),
@@ -47,7 +47,7 @@ class _GetStartedBackgroundState extends State<GetStartedBackground> {
Image.network(
'https://images.unsplash.com/photo-1577219491135-ce391730fb2c?w=400&h=400&fit=crop&crop=faces',
fit: BoxFit.cover,
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) return child;
// Only animate opacity if we have a frame
return AnimatedOpacity(
@@ -56,12 +56,12 @@ class _GetStartedBackgroundState extends State<GetStartedBackground> {
child: child,
);
},
loadingBuilder: (context, child, loadingProgress) {
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
// While loading, show nothing (transparent) so layer 1 shows
if (loadingProgress == null) return child;
return const SizedBox.shrink();
},
errorBuilder: (context, error, stackTrace) {
errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) {
// On error, show nothing (transparent) so layer 1 shows
// Also schedule a state update to prevent retries if needed
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -83,7 +83,7 @@ class _GetStartedBackgroundState extends State<GetStartedBackground> {
// Pagination dots (Visual only)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
children: <Widget>[
Container(
width: UiConstants.space6,
height: UiConstants.space2,

View File

@@ -8,6 +8,15 @@ import 'otp_verification/otp_verification_header.dart';
/// A widget that displays the OTP verification UI.
class OtpVerification extends StatelessWidget {
/// Creates an [OtpVerification].
const OtpVerification({
super.key,
required this.state,
required this.onOtpSubmitted,
required this.onResend,
required this.onContinue,
});
/// The current state of the authentication process.
final AuthState state;
@@ -20,15 +29,6 @@ class OtpVerification extends StatelessWidget {
/// Callback for the "Continue" action.
final VoidCallback onContinue;
/// Creates an [OtpVerification].
const OtpVerification({
super.key,
required this.state,
required this.onOtpSubmitted,
required this.onResend,
required this.onContinue,
});
@override
Widget build(BuildContext context) {
return Column(

View File

@@ -11,11 +11,6 @@ import '../../../blocs/auth_bloc.dart';
/// This widget handles its own internal [TextEditingController]s and focus nodes.
/// It dispatches [AuthOtpUpdated] to the [AuthBloc] on every change.
class OtpInputField extends StatefulWidget {
/// Callback for when the OTP code is fully entered (6 digits).
final ValueChanged<String> onCompleted;
/// The error message to display, if any.
final String error;
/// Creates an [OtpInputField].
const OtpInputField({
@@ -23,6 +18,11 @@ class OtpInputField extends StatefulWidget {
required this.onCompleted,
required this.error,
});
/// Callback for when the OTP code is fully entered (6 digits).
final ValueChanged<String> onCompleted;
/// The error message to display, if any.
final String error;
@override
State<OtpInputField> createState() => _OtpInputFieldState();

View File

@@ -4,11 +4,6 @@ import 'package:flutter/material.dart';
/// A widget that handles the OTP resend logic and countdown timer.
class OtpResendSection extends StatefulWidget {
/// Callback for when the resend link is pressed.
final VoidCallback onResend;
/// Whether an error is currently displayed. (Used for layout tweaks in the original code)
final bool hasError;
/// Creates an [OtpResendSection].
const OtpResendSection({
@@ -16,6 +11,11 @@ class OtpResendSection extends StatefulWidget {
required this.onResend,
this.hasError = false,
});
/// Callback for when the resend link is pressed.
final VoidCallback onResend;
/// Whether an error is currently displayed. (Used for layout tweaks in the original code)
final bool hasError;
@override
State<OtpResendSection> createState() => _OtpResendSectionState();

View File

@@ -6,14 +6,6 @@ import '../../common/auth_trouble_link.dart';
/// A widget that displays the primary action button and trouble link for OTP verification.
class OtpVerificationActions extends StatelessWidget {
/// Whether the verification process is currently loading.
final bool isLoading;
/// Whether the submit button should be enabled.
final bool canSubmit;
/// Callback for when the Continue button is pressed.
final VoidCallback? onContinue;
/// Creates an [OtpVerificationActions].
const OtpVerificationActions({
@@ -22,6 +14,14 @@ class OtpVerificationActions extends StatelessWidget {
required this.canSubmit,
this.onContinue,
});
/// Whether the verification process is currently loading.
final bool isLoading;
/// Whether the submit button should be enabled.
final bool canSubmit;
/// Callback for when the Continue button is pressed.
final VoidCallback? onContinue;
@override
Widget build(BuildContext context) {
@@ -36,9 +36,9 @@ class OtpVerificationActions extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
isLoading
? ElevatedButton(
? const ElevatedButton(
onPressed: null,
child: const SizedBox(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),

View File

@@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
/// A widget that displays the title and subtitle for the OTP Verification page.
class OtpVerificationHeader extends StatelessWidget {
/// The phone number to which the code was sent.
final String phoneNumber;
/// Creates an [OtpVerificationHeader].
const OtpVerificationHeader({super.key, required this.phoneNumber});
/// The phone number to which the code was sent.
final String phoneNumber;
@override
Widget build(BuildContext context) {

View File

@@ -5,11 +5,6 @@ import 'package:staff_authentication/src/presentation/widgets/common/auth_troubl
/// A widget that displays the primary action button and trouble link for Phone Input.
class PhoneInputActions extends StatelessWidget {
/// Whether the sign-in process is currently loading.
final bool isLoading;
/// Callback for when the Send Code button is pressed.
final VoidCallback? onSendCode;
/// Creates a [PhoneInputActions].
const PhoneInputActions({
@@ -17,6 +12,11 @@ class PhoneInputActions extends StatelessWidget {
required this.isLoading,
this.onSendCode,
});
/// Whether the sign-in process is currently loading.
final bool isLoading;
/// Callback for when the Send Code button is pressed.
final VoidCallback? onSendCode;
@override
Widget build(BuildContext context) {
@@ -29,9 +29,9 @@ class PhoneInputActions extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
isLoading
? UiButton.secondary(
? const UiButton.secondary(
onPressed: null,
child: const SizedBox(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),

View File

@@ -2,20 +2,11 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:core_localization/core_localization.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget that displays the phone number input field with country code.
///
/// This widget handles its own [TextEditingController] to manage input.
class PhoneInputFormField extends StatefulWidget {
/// The initial value for the phone number.
final String initialValue;
/// The error message to display, if any.
final String error;
/// Callback for when the text field value changes.
final ValueChanged<String> onChanged;
/// Creates a [PhoneInputFormField].
const PhoneInputFormField({
@@ -24,6 +15,14 @@ class PhoneInputFormField extends StatefulWidget {
required this.error,
required this.onChanged,
});
/// The initial value for the phone number.
final String initialValue;
/// The error message to display, if any.
final String error;
/// Callback for when the text field value changes.
final ValueChanged<String> onChanged;
@override
State<PhoneInputFormField> createState() => _PhoneInputFormFieldState();

View File

@@ -5,6 +5,15 @@ import 'package:staff_authentication/src/presentation/widgets/common/section_tit
/// A widget for setting up basic profile information (photo, name, bio).
class ProfileSetupBasicInfo extends StatelessWidget {
/// Creates a [ProfileSetupBasicInfo] widget.
const ProfileSetupBasicInfo({
super.key,
required this.fullName,
required this.bio,
required this.onFullNameChanged,
required this.onBioChanged,
});
/// The user's full name.
final String fullName;
@@ -17,15 +26,6 @@ class ProfileSetupBasicInfo extends StatelessWidget {
/// Callback for when the bio changes.
final ValueChanged<String> onBioChanged;
/// Creates a [ProfileSetupBasicInfo] widget.
const ProfileSetupBasicInfo({
super.key,
required this.fullName,
required this.bio,
required this.onFullNameChanged,
required this.onBioChanged,
});
@override
/// Builds the basic info step UI.
Widget build(BuildContext context) {

View File

@@ -6,6 +6,15 @@ import 'package:staff_authentication/src/presentation/widgets/common/section_tit
/// A widget for setting up skills and preferred industries.
class ProfileSetupExperience extends StatelessWidget {
/// Creates a [ProfileSetupExperience] widget.
const ProfileSetupExperience({
super.key,
required this.skills,
required this.industries,
required this.onSkillsChanged,
required this.onIndustriesChanged,
});
/// The list of selected skills.
final List<String> skills;
@@ -18,15 +27,6 @@ class ProfileSetupExperience extends StatelessWidget {
/// Callback for when industries change.
final ValueChanged<List<String>> onIndustriesChanged;
/// Creates a [ProfileSetupExperience] widget.
const ProfileSetupExperience({
super.key,
required this.skills,
required this.industries,
required this.onSkillsChanged,
required this.onIndustriesChanged,
});
/// Toggles a skill.
void _toggleSkill({required String skill}) {
final List<String> updatedList = List<String>.from(skills);

View File

@@ -4,14 +4,6 @@ import 'package:flutter/material.dart';
/// A header widget for the profile setup page showing back button and step count.
class ProfileSetupHeader extends StatelessWidget {
/// The current step index (0-based).
final int currentStep;
/// The total number of steps.
final int totalSteps;
/// Callback when the back button is tapped.
final VoidCallback? onBackTap;
/// Creates a [ProfileSetupHeader].
const ProfileSetupHeader({
@@ -20,6 +12,14 @@ class ProfileSetupHeader extends StatelessWidget {
required this.totalSteps,
this.onBackTap,
});
/// The current step index (0-based).
final int currentStep;
/// The total number of steps.
final int totalSteps;
/// Callback when the back button is tapped.
final VoidCallback? onBackTap;
@override
/// Builds the header UI.

View File

@@ -9,6 +9,15 @@ import 'package:staff_authentication/src/presentation/widgets/common/section_tit
/// A widget for setting up preferred work locations and distance.
class ProfileSetupLocation extends StatefulWidget {
/// Creates a [ProfileSetupLocation] widget.
const ProfileSetupLocation({
super.key,
required this.preferredLocations,
required this.maxDistanceMiles,
required this.onLocationsChanged,
required this.onDistanceChanged,
});
/// The list of preferred locations.
final List<String> preferredLocations;
@@ -21,15 +30,6 @@ class ProfileSetupLocation extends StatefulWidget {
/// Callback for when the max distance changes.
final ValueChanged<double> onDistanceChanged;
/// Creates a [ProfileSetupLocation] widget.
const ProfileSetupLocation({
super.key,
required this.preferredLocations,
required this.maxDistanceMiles,
required this.onLocationsChanged,
required this.onDistanceChanged,
});
@override
State<ProfileSetupLocation> createState() => _ProfileSetupLocationState();
}
@@ -97,9 +97,9 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
// Suggestions List
BlocBuilder<ProfileSetupBloc, ProfileSetupState>(
buildWhen: (previous, current) =>
buildWhen: (ProfileSetupState previous, ProfileSetupState current) =>
previous.locationSuggestions != current.locationSuggestions,
builder: (context, state) {
builder: (BuildContext context, ProfileSetupState state) {
if (state.locationSuggestions.isEmpty) {
return const SizedBox.shrink();
}
@@ -114,9 +114,9 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: state.locationSuggestions.length,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
final suggestion = state.locationSuggestions[index];
separatorBuilder: (BuildContext context, int index) => const Divider(height: 1),
itemBuilder: (BuildContext context, int index) {
final String suggestion = state.locationSuggestions[index];
return ListTile(
title: Text(suggestion, style: UiTypography.body2m),
leading: const Icon(UiIcons.mapPin, size: 16),