Add explicit types and improve type safety across codebase

This commit adds explicit type annotations to variables, function parameters, and return types throughout the codebase, particularly in widget trees, Bloc logic, and repository implementations. The changes improve code readability, maintainability, and type safety, and align with Dart best practices. No business logic was changed.
This commit is contained in:
Achintha Isuru
2026-01-24 10:00:36 -05:00
parent ff93bfa4bd
commit f57f41c508
99 changed files with 495 additions and 485 deletions

View File

@@ -23,7 +23,7 @@ export 'package:core_localization/core_localization.dart';
/// A [Module] for the client authentication feature.
class ClientAuthenticationModule extends Module {
@override
List<Module> get imports => [DataConnectModule()];
List<Module> get imports => <Module>[DataConnectModule()];
@override
void binds(Injector i) {
@@ -59,7 +59,7 @@ class ClientAuthenticationModule extends Module {
}
@override
void routes(r) {
void routes(RouteManager r) {
r.child('/', child: (_) => const ClientGetStartedPage());
r.child('/client-sign-in', child: (_) => const ClientSignInPage());
r.child('/client-sign-up', child: (_) => const ClientSignUpPage());

View File

@@ -1,4 +1,5 @@
import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:firebase_data_connect/src/core/ref.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain;
import '../../domain/repositories/auth_repository_interface.dart';
@@ -24,12 +25,12 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
required String password,
}) async {
try {
final credential = await _firebaseAuth.signInWithEmailAndPassword(
final firebase.UserCredential credential = await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
final firebaseUser = credential.user;
final firebase.User? firebaseUser = credential.user;
if (firebaseUser == null) {
throw Exception('Sign-in failed, no Firebase user received.');
}
@@ -59,12 +60,12 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
required String password,
}) async {
try {
final credential = await _firebaseAuth.createUserWithEmailAndPassword(
final firebase.UserCredential credential = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
final firebaseUser = credential.user;
final firebase.User? firebaseUser = credential.user;
if (firebaseUser == null) {
throw Exception('Sign-up failed, Firebase user could not be created.');
}
@@ -72,20 +73,20 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
// Client-specific business logic:
// 1. Create a `Business` entity.
// 2. Create a `User` entity associated with the business.
final createBusinessResponse = await _dataConnect.createBusiness(
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables> createBusinessResponse = await _dataConnect.createBusiness(
businessName: companyName,
userId: firebaseUser.uid,
rateGroup: dc.BusinessRateGroup.STANDARD,
status: dc.BusinessStatus.PENDING,
).execute();
final businessData = createBusinessResponse.data?.business_insert;
final dc.CreateBusinessBusinessInsert? businessData = createBusinessResponse.data?.business_insert;
if (businessData == null) {
await firebaseUser.delete(); // Rollback if business creation fails
throw Exception('Business creation failed after Firebase user registration.');
}
final createUserResponse = await _dataConnect.createUser(
final OperationResult<dc.CreateUserData, dc.CreateUserVariables> createUserResponse = await _dataConnect.createUser(
id: firebaseUser.uid,
role: dc.UserBaseRole.USER,
)
@@ -93,7 +94,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
.userRole('BUSINESS')
.execute();
final newUserData = createUserResponse.data?.user_insert;
final dc.CreateUserUserInsert? newUserData = createUserResponse.data?.user_insert;
if (newUserData == null) {
await firebaseUser.delete(); // Rollback if user profile creation fails
// TO-DO: Also delete the created Business if this fails
@@ -137,27 +138,27 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
required String firebaseUserId,
required String? fallbackEmail,
}) async {
final response = await _dataConnect.getUserById(id: firebaseUserId).execute();
final user = response.data?.user;
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response = await _dataConnect.getUserById(id: firebaseUserId).execute();
final dc.GetUserByIdUser? user = response.data?.user;
if (user == null) {
throw Exception('Authenticated user profile not found in database.');
}
final email = user.email ?? fallbackEmail;
final String? email = user.email ?? fallbackEmail;
if (email == null || email.isEmpty) {
throw Exception('User email is missing in profile data.');
}
final domainUser = domain.User(
final domain.User domainUser = domain.User(
id: user.id,
email: email,
role: user.role.stringValue,
);
final businessResponse = await _dataConnect.getBusinessesByUserId(
final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> businessResponse = await _dataConnect.getBusinessesByUserId(
userId: firebaseUserId,
).execute();
final business = businessResponse.data.businesses.isNotEmpty
final dc.GetBusinessesByUserIdBusinesses? business = businessResponse.data.businesses.isNotEmpty
? businessResponse.data.businesses.first
: null;

View File

@@ -11,5 +11,5 @@ class SignInWithEmailArguments extends UseCaseArgument {
const SignInWithEmailArguments({required this.email, required this.password});
@override
List<Object?> get props => [email, password];
List<Object?> get props => <Object?>[email, password];
}

View File

@@ -8,5 +8,5 @@ class SignInWithSocialArguments extends UseCaseArgument {
const SignInWithSocialArguments({required this.provider});
@override
List<Object?> get props => [provider];
List<Object?> get props => <Object?>[provider];
}

View File

@@ -18,5 +18,5 @@ class SignUpWithEmailArguments extends UseCaseArgument {
});
@override
List<Object?> get props => [companyName, email, password];
List<Object?> get props => <Object?>[companyName, email, password];
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_domain/src/entities/users/user.dart';
import '../../domain/arguments/sign_in_with_email_arguments.dart';
import '../../domain/arguments/sign_in_with_social_arguments.dart';
import '../../domain/arguments/sign_up_with_email_arguments.dart';
@@ -50,7 +51,7 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
) async {
emit(state.copyWith(status: ClientAuthStatus.loading));
try {
final user = await _signInWithEmail(
final User user = await _signInWithEmail(
SignInWithEmailArguments(email: event.email, password: event.password),
);
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
@@ -71,7 +72,7 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
) async {
emit(state.copyWith(status: ClientAuthStatus.loading));
try {
final user = await _signUpWithEmail(
final User user = await _signUpWithEmail(
SignUpWithEmailArguments(
companyName: event.companyName,
email: event.email,
@@ -96,7 +97,7 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
) async {
emit(state.copyWith(status: ClientAuthStatus.loading));
try {
final user = await _signInWithSocial(
final User user = await _signInWithSocial(
SignInWithSocialArguments(provider: event.provider),
);
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));

View File

@@ -5,7 +5,7 @@ abstract class ClientAuthEvent extends Equatable {
const ClientAuthEvent();
@override
List<Object?> get props => [];
List<Object?> get props => <Object?>[];
}
/// Event dispatched when a user attempts to sign in with email and password.
@@ -16,7 +16,7 @@ class ClientSignInRequested extends ClientAuthEvent {
const ClientSignInRequested({required this.email, required this.password});
@override
List<Object?> get props => [email, password];
List<Object?> get props => <Object?>[email, password];
}
/// Event dispatched when a user attempts to create a new business account.
@@ -32,7 +32,7 @@ class ClientSignUpRequested extends ClientAuthEvent {
});
@override
List<Object?> get props => [companyName, email, password];
List<Object?> get props => <Object?>[companyName, email, password];
}
/// Event dispatched for third-party authentication (Google/Apple).
@@ -42,7 +42,7 @@ class ClientSocialSignInRequested extends ClientAuthEvent {
const ClientSocialSignInRequested({required this.provider});
@override
List<Object?> get props => [provider];
List<Object?> get props => <Object?>[provider];
}
/// Event dispatched when the user requests to terminate their session.

View File

@@ -50,5 +50,5 @@ class ClientAuthState extends Equatable {
}
@override
List<Object?> get props => [status, user, errorMessage];
List<Object?> get props => <Object?>[status, user, errorMessage];
}

View File

@@ -11,7 +11,7 @@ class ClientGetStartedPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
children: <Widget>[
// Background Illustration/Visuals from prototype
Positioned(
top: -100,
@@ -28,7 +28,7 @@ class ClientGetStartedPage extends StatelessWidget {
SafeArea(
child: Column(
children: [
children: <Widget>[
const SizedBox(height: UiConstants.space10),
// Logo
Center(
@@ -48,7 +48,7 @@ class ClientGetStartedPage extends StatelessWidget {
horizontal: UiConstants.space6,
),
child: Stack(
children: [
children: <Widget>[
// Representative cards from prototype
Positioned(
top: 20,
@@ -76,7 +76,7 @@ class ClientGetStartedPage extends StatelessWidget {
vertical: UiConstants.space10,
),
child: Column(
children: [
children: <Widget>[
Text(
t.client_authentication.get_started_page.title,
textAlign: TextAlign.center,
@@ -132,7 +132,7 @@ class _ShiftOrderCard extends StatelessWidget {
decoration: BoxDecoration(
color: UiColors.white,
borderRadius: UiConstants.radiusLg,
boxShadow: [
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.black.withOpacity(0.1),
blurRadius: 10,
@@ -143,9 +143,9 @@ class _ShiftOrderCard extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
children: <Widget>[
Row(
children: [
children: <Widget>[
Container(
padding: const EdgeInsets.all(UiConstants.space1),
decoration: BoxDecoration(
@@ -195,7 +195,7 @@ class _WorkerProfileCard extends StatelessWidget {
decoration: BoxDecoration(
color: UiColors.white,
borderRadius: UiConstants.radiusLg,
boxShadow: [
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.black.withOpacity(0.1),
blurRadius: 10,
@@ -204,7 +204,7 @@ class _WorkerProfileCard extends StatelessWidget {
],
),
child: Row(
children: [
children: <Widget>[
CircleAvatar(
radius: 16,
backgroundColor: UiColors.primary.withOpacity(0.1),
@@ -214,7 +214,7 @@ class _WorkerProfileCard extends StatelessWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
children: <Widget>[
Text('Alex Thompson', style: UiTypography.footnote1b),
Text(
'Professional Waiter • 4.9★',
@@ -236,7 +236,7 @@ class _CalendarCard extends StatelessWidget {
decoration: BoxDecoration(
color: UiColors.accent,
borderRadius: UiConstants.radiusMd,
boxShadow: [
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.black.withOpacity(0.1),
blurRadius: 10,

View File

@@ -35,13 +35,13 @@ class ClientSignInPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final i18n = t.client_authentication.sign_in_page;
final authBloc = Modular.get<ClientAuthBloc>();
final TranslationsClientAuthenticationSignInPageEn i18n = t.client_authentication.sign_in_page;
final ClientAuthBloc authBloc = Modular.get<ClientAuthBloc>();
return BlocProvider.value(
value: authBloc,
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
listener: (context, state) {
listener: (BuildContext context, ClientAuthState state) {
if (state.status == ClientAuthStatus.authenticated) {
Modular.to.navigateClientHome();
} else if (state.status == ClientAuthStatus.error) {
@@ -52,8 +52,8 @@ class ClientSignInPage extends StatelessWidget {
);
}
},
builder: (context, state) {
final isLoading = state.status == ClientAuthStatus.loading;
builder: (BuildContext context, ClientAuthState state) {
final bool isLoading = state.status == ClientAuthStatus.loading;
return Scaffold(
appBar: const UiAppBar(showBackButton: true),
@@ -69,14 +69,14 @@ class ClientSignInPage extends StatelessWidget {
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
children: <Widget>[
SectionTitle(title: i18n.title, subtitle: i18n.subtitle),
const SizedBox(height: UiConstants.space8),
// Sign In Form
ClientSignInForm(
isLoading: isLoading,
onSignIn: ({required email, required password}) =>
onSignIn: ({required String email, required String password}) =>
_handleSignIn(
context,
email: email,
@@ -99,7 +99,7 @@ class ClientSignInPage extends StatelessWidget {
// Sign Up Link
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
children: <Widget>[
Text(
i18n.no_account,
style: UiTypography.body2r.textSecondary,

View File

@@ -39,13 +39,13 @@ class ClientSignUpPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final i18n = t.client_authentication.sign_up_page;
final authBloc = Modular.get<ClientAuthBloc>();
final TranslationsClientAuthenticationSignUpPageEn i18n = t.client_authentication.sign_up_page;
final ClientAuthBloc authBloc = Modular.get<ClientAuthBloc>();
return BlocProvider.value(
value: authBloc,
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
listener: (context, state) {
listener: (BuildContext context, ClientAuthState state) {
if (state.status == ClientAuthStatus.authenticated) {
Modular.to.navigateClientHome();
} else if (state.status == ClientAuthStatus.error) {
@@ -56,8 +56,8 @@ class ClientSignUpPage extends StatelessWidget {
);
}
},
builder: (context, state) {
final isLoading = state.status == ClientAuthStatus.loading;
builder: (BuildContext context, ClientAuthState state) {
final bool isLoading = state.status == ClientAuthStatus.loading;
return Scaffold(
appBar: const UiAppBar(showBackButton: true),
@@ -73,7 +73,7 @@ class ClientSignUpPage extends StatelessWidget {
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
children: <Widget>[
SectionTitle(title: i18n.title, subtitle: i18n.subtitle),
const SizedBox(height: UiConstants.space8),
@@ -82,9 +82,9 @@ class ClientSignUpPage extends StatelessWidget {
isLoading: isLoading,
onSignUp:
({
required companyName,
required email,
required password,
required String companyName,
required String email,
required String password,
}) => _handleSignUp(
context,
companyName: companyName,
@@ -108,7 +108,7 @@ class ClientSignUpPage extends StatelessWidget {
// Sign In Link
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
children: <Widget>[
Text(
i18n.has_account,
style: UiTypography.body2r.textSecondary,

View File

@@ -26,8 +26,8 @@ class ClientSignInForm extends StatefulWidget {
}
class _ClientSignInFormState extends State<ClientSignInForm> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _obscurePassword = true;
@override
@@ -46,10 +46,10 @@ class _ClientSignInFormState extends State<ClientSignInForm> {
@override
Widget build(BuildContext context) {
final i18n = t.client_authentication.sign_in_page;
final TranslationsClientAuthenticationSignInPageEn i18n = t.client_authentication.sign_in_page;
return Column(
children: [
children: <Widget>[
// Email Field
UiTextField(
label: i18n.email_label,

View File

@@ -30,10 +30,10 @@ class ClientSignUpForm extends StatefulWidget {
}
class _ClientSignUpFormState extends State<ClientSignUpForm> {
final _companyController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
final TextEditingController _companyController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _confirmPasswordController = TextEditingController();
bool _obscurePassword = true;
@override
@@ -62,10 +62,10 @@ class _ClientSignUpFormState extends State<ClientSignUpForm> {
@override
Widget build(BuildContext context) {
final i18n = t.client_authentication.sign_up_page;
final TranslationsClientAuthenticationSignUpPageEn i18n = t.client_authentication.sign_up_page;
return Column(
children: [
children: <Widget>[
// Company Name Field
UiTextField(
label: i18n.company_label,

View File

@@ -15,7 +15,7 @@ class AuthDivider extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
children: <Widget>[
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space4),

View File

@@ -15,7 +15,7 @@ class SectionTitle extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
children: <Widget>[
Text(title, style: UiTypography.headline1m),
Text(subtitle, style: UiTypography.body2r.textSecondary),
],