feat: Refactor code structure and optimize performance across multiple modules

This commit is contained in:
Achintha Isuru
2025-11-17 23:29:28 -05:00
parent 831570f2e0
commit a64cbd9edf
1508 changed files with 105319 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/common/validators/email_validator.dart';
part 'sign_in_event.dart';
part 'sign_in_state.dart';
class SignInBloc extends Bloc<SignInEvent, SignInState> {
String? code;
SignInBloc() : super(const SignInState()) {
on<SwitchObscurePasswordEvent>(_onSwitchObscurePassword);
on<SignInEventSignIn>(_onSignIn);
on<ResetPassNextStepEvent>(_onResetPassNextStep);
on<ResetPassBackEvent>(_onResetPassBack);
on<SignInEventEmailChanged>(_onEmailChanged);
on<SignInEventPassChanged>(_onPassChanged);
on<SignInEventOnResetVerified>(_onResetVerified);
on<ConfirmNewPassEvent>(_onConfirmNewPass);
}
void _onSwitchObscurePassword(SwitchObscurePasswordEvent event, emit) {
emit(state.copyWith(obscurePassword: !state.obscurePassword));
}
void _onSignIn(SignInEventSignIn event, emit) async {
emit(state.copyWith(inLoading: true));
var validationResult = EmailValidator.validate(state.email);
if (validationResult != null) {
emit(state.copyWith(
emailValidationError: validationResult, inLoading: false));
return;
}
if(event.password.length<8){
emit(state.copyWith(
passValidationError: 'Password must be at least 8 characters long.', inLoading: false));
return;
}
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: state.email, password: event.password);
emit(state.copyWith(authSuccess: true, inLoading: false));
} catch (e) {
emit(state.copyWith(
emailValidationError: 'Failed to sign in.', inLoading: false));
}
}
void _onResetPassNextStep(ResetPassNextStepEvent event, emit) async {
if (state.resetPassStep == ResetPassStep.email) {
var validationResult = EmailValidator.validate(state.email);
if (validationResult != null) {
emit(state.copyWith(inputValidationError: validationResult));
return;
}
try {
await FirebaseAuth.instance.sendPasswordResetEmail(email: state.email);
} catch (e) {
emit(state.copyWith(
inputValidationError: 'Failed to send password reset email.'));
return;
}
}
emit(state.copyWith(
resetPassStep: ResetPassStep.values[state.resetPassStep.index + 1],
inputValidationError: '',
obscurePassword: true));
}
void _onResetVerified(SignInEventOnResetVerified event, emit) async {
var email = await FirebaseAuth.instance.verifyPasswordResetCode(event.code);
try {
code = event.code;
emit(state.copyWith(
resetPassStep: ResetPassStep.email,
email: email,
obscurePassword: true));
return;
} catch (e) {
debugPrint(e.toString());
}
}
void _onConfirmNewPass(ConfirmNewPassEvent event, emit) async {
if (event.password != event.passwordDuplicate) {
emit(state.copyWith(inputValidationError: 'Passwords do not match.'));
return;
}
try {
await FirebaseAuth.instance
.confirmPasswordReset(code: code!, newPassword: event.password);
emit(state.copyWith(
resetPassStep: ResetPassStep.success,
email: '',
obscurePassword: true));
} catch (e) {
emit(state.copyWith(inputValidationError: 'Failed to reset password.'));
return;
}
}
void _onResetPassBack(ResetPassBackEvent event, emit) {
emit(state.copyWith(
resetPassStep: ResetPassStep.email, email: '', obscurePassword: true));
}
void _onEmailChanged(SignInEventEmailChanged event, emit) {
emit(state.copyWith(email: event.email, inputValidationError: '', emailValidationError: ''));
}
void _onPassChanged(SignInEventPassChanged event, emit) {
emit(state.copyWith(passValidationError: ''));
}
}

View File

@@ -0,0 +1,38 @@
part of 'sign_in_bloc.dart';
@immutable
sealed class SignInEvent {}
class SignInEventSignIn extends SignInEvent {
final String password;
SignInEventSignIn(this.password);
}
class ResetPassNextStepEvent extends SignInEvent {}
class ResetPassBackEvent extends SignInEvent {}
class SwitchObscurePasswordEvent extends SignInEvent {}
class SignInEventEmailChanged extends SignInEvent {
final String email;
SignInEventEmailChanged(this.email);
}
class SignInEventPassChanged extends SignInEvent {
}
class SignInEventOnResetVerified extends SignInEvent {
final String code;
SignInEventOnResetVerified(this.code);
}
class ConfirmNewPassEvent extends SignInEvent {
final String password;
final String passwordDuplicate;
ConfirmNewPassEvent(this.password, this.passwordDuplicate);
}

View File

@@ -0,0 +1,48 @@
part of 'sign_in_bloc.dart';
enum ResetPassStep { email, link, password, success }
@immutable
class SignInState {
final bool inLoading;
final String inputValidationError;
final String email;
final ResetPassStep resetPassStep;
final bool obscurePassword;
final bool authSuccess;
final String emailValidationError;
final String passValidationError;
const SignInState({
this.inLoading = false,
this.inputValidationError = '',
this.emailValidationError = '',
this.passValidationError = '',
this.email = '',
this.resetPassStep = ResetPassStep.email,
this.obscurePassword = true,
this.authSuccess = false,
});
SignInState copyWith({
bool? inLoading,
String? inputValidationError,
String? email,
ResetPassStep? resetPassStep,
bool? obscurePassword,
bool? authSuccess,
String? emailValidationError,
String? passValidationError,
}) {
return SignInState(
inLoading: inLoading ?? this.inLoading,
inputValidationError: inputValidationError ?? this.inputValidationError,
email: email ?? this.email,
resetPassStep: resetPassStep ?? this.resetPassStep,
obscurePassword: obscurePassword ?? this.obscurePassword,
authSuccess: authSuccess ?? this.authSuccess,
emailValidationError: emailValidationError ?? this.emailValidationError,
passValidationError: passValidationError ?? this.passValidationError,
);
}
}

View File

@@ -0,0 +1,97 @@
import 'package:auto_route/annotations.dart';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/features/sign_in/domain/bloc/sign_in_bloc.dart';
import 'package:krow/features/sign_in/presentation/reset_pass_screen/widgets/enter_new_pass_form.dart';
import 'package:krow/features/sign_in/presentation/reset_pass_screen/widgets/reset_success_form.dart';
@RoutePage()
class EnterNewPassScreen extends StatefulWidget {
final String? code;
const EnterNewPassScreen({super.key, @QueryParam('oobCode') this.code});
@override
State<EnterNewPassScreen> createState() => _EnterNewPassScreenState();
}
class _EnterNewPassScreenState extends State<EnterNewPassScreen> {
@override
void initState() {
super.initState();
context
.read<SignInBloc>()
.add(SignInEventOnResetVerified(widget.code ?? ''));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KwAppBar(),
body: BlocBuilder<SignInBloc, SignInState>(
builder: (context, state) {
var form = _getForm(state);
return ScrollLayoutHelper(
upperWidget: form,
lowerWidget: Column(
children: [
KwButton.primary(
label: _buttonText(state),
onPressed: () {
context.read<SignInBloc>().add(ResetPassNextStepEvent());
},
),
const Gap(36),
RichText(
text: TextSpan(
text: 'Remember your password? ',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
children: [
TextSpan(
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.maybePop(context);
},
text: 'Log In',
style: AppTextStyles.bodyMediumSmb,
),
],
)),
const Gap(36),
],
),
);
},
),
);
}
String _buttonText(SignInState state) {
switch (state.resetPassStep) {
case ResetPassStep.password:
return 'Save New Password';
case ResetPassStep.success:
return 'Go to Login';
default:
return 'Next';
}
}
_getForm(SignInState state) {
if (state.resetPassStep == ResetPassStep.success) {
return const ResetSuccessForm();
} else {
return EnterNewPassForm(obscurePassword: state.obscurePassword);
}
}
}

View File

@@ -0,0 +1,92 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/application/routing/routes.gr.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/features/sign_in/domain/bloc/sign_in_bloc.dart';
import 'package:krow/features/sign_in/presentation/reset_pass_screen/widgets/reset_email_form.dart';
import 'package:krow/features/sign_in/presentation/reset_pass_screen/widgets/waiting_email_link_form.dart';
@RoutePage()
class ResetPassScreen extends StatelessWidget {
const ResetPassScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KwAppBar(),
body: BlocConsumer<SignInBloc, SignInState>(
listener: (context, state) {
if (state.resetPassStep == ResetPassStep.password) {
context.router.replace(EnterNewPassRoute());
}
},
builder: (context, state) {
var form = _getForm(state);
return ScrollLayoutHelper(
upperWidget: form,
lowerWidget: Column(
children: [
KwButton.primary(
disabled: state.resetPassStep != ResetPassStep.email,
label: _buttonText(state),
onPressed: () {
if (state.resetPassStep == ResetPassStep.email) {
context.read<SignInBloc>().add(ResetPassNextStepEvent());
}
},
),
const Gap(36),
RichText(
text: TextSpan(
text: 'Remember your password? ',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
children: [
TextSpan(
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.maybePop(context);
},
text: 'Log In',
style: AppTextStyles.bodyMediumSmb,
),
],
)),
const Gap(36),
],
),
);
},
),
);
}
String _buttonText(SignInState state) {
switch (state.resetPassStep) {
case ResetPassStep.email:
return 'Send Reset Link';
case ResetPassStep.link:
return 'Continue';
default:
return 'Next';
}
}
_getForm(SignInState state) {
switch (state.resetPassStep) {
case ResetPassStep.email:
return const ResetEmailForm();
case ResetPassStep.link:
return WaitingEmailLinkForm(email: state.email);
default:
return const SizedBox.shrink();
}
}
}

View File

@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/gen/assets.gen.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
import 'package:krow/features/sign_in/domain/bloc/sign_in_bloc.dart';
class EnterNewPassForm extends StatelessWidget {
final bool obscurePassword;
const EnterNewPassForm({super.key, required this.obscurePassword});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(36),
const Text(
'Reset Your Password',
style: AppTextStyles.headingH1,
),
const Gap(8),
Text(
'Create a new password for your account',
style:
AppTextStyles.bodyMediumReg.copyWith(color: AppColors.blackGray),
),
const Gap(24),
KwTextInput(
title: 'New Password',
hintText: 'Enter password here',
obscureText: obscurePassword,
maxLines: 1,
suffixIcon: GestureDetector(
onTap: () {
BlocProvider.of<SignInBloc>(context)
.add(SwitchObscurePasswordEvent());
},
child: Padding(
padding: const EdgeInsets.only(right: 16),
child: obscurePassword
? Assets.images.icons.eyeOff.svg()
: Assets.images.icons.eye.svg(),
),
),
controller: TextEditingController()),
const Gap(8),
KwTextInput(
title: 'Confirm Password',
hintText: 'Enter password here',
maxLines: 1,
obscureText: obscurePassword,
suffixIcon: GestureDetector(
onTap: () {
BlocProvider.of<SignInBloc>(context)
.add(SwitchObscurePasswordEvent());
},
child: Padding(
padding: const EdgeInsets.only(right: 16),
child: obscurePassword
? Assets.images.icons.eyeOff.svg()
: Assets.images.icons.eye.svg(),
),
),
controller: TextEditingController()),
const Gap(24),
],
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
import 'package:krow/features/sign_in/domain/bloc/sign_in_bloc.dart';
class ResetEmailForm extends StatefulWidget {
const ResetEmailForm({super.key});
@override
State<ResetEmailForm> createState() => _ResetEmailFormState();
}
class _ResetEmailFormState extends State<ResetEmailForm> {
var textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(36),
const Text(
'Forgot Password?',
style: AppTextStyles.headingH1,
),
const Gap(8),
Text(
'Enter the email address associated with your account',
style:
AppTextStyles.bodyMediumReg.copyWith(color: AppColors.blackGray),
),
const Gap(24),
BlocBuilder<SignInBloc, SignInState>(
builder: (context, state) {
return KwTextInput(
title: 'Email',
hintText: 'Enter your email',
keyboardType: TextInputType.emailAddress,
onChanged: (str) {
context.read<SignInBloc>().add(SignInEventEmailChanged(str));
},
helperText: state.inputValidationError,
showError: state.inputValidationError.isNotEmpty,
controller: textEditingController);
},
),
const Gap(24),
],
);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
class ResetSuccessForm extends StatelessWidget {
const ResetSuccessForm({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(36),
const Text(
'Password Reset Successful!',
style: AppTextStyles.headingH1,
),
const Gap(8),
Text(
'Your password has been updated. You can now log in with your new password',
style:
AppTextStyles.bodyMediumReg.copyWith(color: AppColors.blackGray),
),
const Gap(24),
],
);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
class WaitingEmailLinkForm extends StatelessWidget {
final String email;
const WaitingEmailLinkForm({super.key, required this.email});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(36),
const Text(
'Check Your Email',
style: AppTextStyles.headingH1,
),
const Gap(8),
RichText(
text: TextSpan(
text: 'We\'ve sent a password reset link to ',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
children: [
TextSpan(
text: email,
style: AppTextStyles.bodyMediumMed,
),
TextSpan(
text:
'. Please check your inbox and follow the instructions to reset your password',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
),
],
),
),
Center(
child: Padding(
padding: EdgeInsets.only(
//на око
top: (MediaQuery.of(context).size.height - 520) / 2), //на око
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'Didn\'t receive the email?\n',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
children: [
TextSpan(
text: 'Resend',
style: AppTextStyles.bodyMediumSmb
.copyWith(color: AppColors.primaryBlue),
recognizer: TapGestureRecognizer()..onTap = () {},
),
TextSpan(
text: ' or ',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
),
TextSpan(
text: 'Contact Support',
style: AppTextStyles.bodyMediumSmb
.copyWith(color: AppColors.primaryBlue),
recognizer: TapGestureRecognizer()..onTap = () {},
),
],
),
),
),
),
],
);
}
}

View File

@@ -0,0 +1,19 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/features/sign_in/domain/bloc/sign_in_bloc.dart';
@RoutePage()
class SignInFlowScreen extends StatelessWidget {
const SignInFlowScreen({
super.key,
});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => SignInBloc(),
child: const AutoRouter(),
);
}
}

View File

@@ -0,0 +1,136 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/application/routing/routes.gr.dart';
import 'package:krow/core/presentation/gen/assets.gen.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
import 'package:krow/features/sign_in/domain/bloc/sign_in_bloc.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
@RoutePage()
class SignInScreen extends StatefulWidget {
const SignInScreen({super.key});
@override
State<SignInScreen> createState() => _SignInScreenState();
}
class _SignInScreenState extends State<SignInScreen> {
final emailController = TextEditingController();
final passController = TextEditingController();
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KwAppBar(),
body: BlocConsumer<SignInBloc, SignInState>(
listener: (context, state) {
if (state.authSuccess) {
context.router.replace(const SplashRoute());
}
},
builder: (context, state) {
return ModalProgressHUD(
inAsyncCall: state.inLoading,
child: ScrollLayoutHelper(
upperWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(36),
const Text(
'Welcome Back!',
style: AppTextStyles.headingH1,
),
const Gap(8),
Text(
'Log in to find work opportunities that match your skills',
style: AppTextStyles.bodyMediumReg
.copyWith(color: AppColors.blackGray),
),
const Gap(24),
KwTextInput(
title: 'Email',
hintText: 'Enter your email',
keyboardType: TextInputType.emailAddress,
helperText: state.emailValidationError,
showError: state.emailValidationError.isNotEmpty,
onChanged: (_) {
BlocProvider.of<SignInBloc>(context)
.add(SignInEventEmailChanged(emailController.text));
},
controller: emailController),
const Gap(8),
KwTextInput(
title: 'Password',
hintText: 'Enter password here',
obscureText: state.obscurePassword,
keyboardType: TextInputType.visiblePassword,
helperText: state.passValidationError,
showError: state.passValidationError.isNotEmpty,
maxLines: 1,
onChanged: (_) {
BlocProvider.of<SignInBloc>(context)
.add(SignInEventPassChanged());
},
suffixIcon: GestureDetector(
onTap: () {
BlocProvider.of<SignInBloc>(context)
.add(SwitchObscurePasswordEvent());
},
child: Padding(
padding: const EdgeInsets.only(right: 16),
child: state.obscurePassword
? Assets.images.icons.eyeOff.svg()
: Assets.images.icons.eye.svg(),
),
),
controller: passController),
// GestureDetector(
// onTap: () async {
// await context.router.push(const ResetPassRoute());
// BlocProvider.of<SignInBloc>(context)
// .add(ResetPassBackEvent());
// },
// child: const Padding(
// padding: EdgeInsets.only(left: 16, top: 12),
// child: Text(
// 'Forgot password',
// style: AppTextStyles.bodyTinyMed,
// ),
// ),
// ),
const Gap(24),
],
),
lowerWidget: Padding(
padding: const EdgeInsets.only(bottom: 36),
child: KwButton.primary(
disabled:
emailController.text.isEmpty || passController.text.isEmpty,
label: 'Continue',
onPressed: () {
BlocProvider.of<SignInBloc>(context).add(SignInEventSignIn(
passController.text,
));
},
),
),
),
);
},
),
);
}
}

View File

@@ -0,0 +1,125 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/application/routing/routes.gr.dart';
import 'package:krow/core/presentation/gen/assets.gen.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
@RoutePage()
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Material(
type: MaterialType.transparency,
child: Stack(fit: StackFit.expand, children: [
_background(context),
Align(
alignment: Alignment.topCenter,
child: ClipPath(
clipper: ArcClipper(),
child: Container(
clipBehavior: Clip.none,
height: MediaQuery.of(context).size.height - 280,
decoration: const BoxDecoration(
//unnamed color from figma for this shape
color: Color(0xFF2246EA),
),
child: Align(
alignment: Alignment.topCenter,
child: Assets.images.bgPattern.svg(
width: MediaQuery.of(context).size.width,
alignment: Alignment.bottomCenter,
fit: BoxFit.fitWidth,
colorFilter: const ColorFilter.mode(
//unnamed color from figma for this image
Color(0xFF2D50EB),
BlendMode.srcIn,
),
),
),
),
),
),
SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Assets.images.logo.svg(),
const Gap(10),
Flexible(child: Assets.images.welcome.image()),
const Gap(24),
Text(
'Take Control of Your Shifts and Events',
textAlign: TextAlign.center,
style: AppTextStyles.headingH1.copyWith(
color: Colors.white,
),
),
const Gap(8),
Text(
'Streamline your operations with powerful tools to manage schedules, track performance, and keep your team on the same page—all in one place',
textAlign: TextAlign.center,
style: AppTextStyles.bodyMediumReg.copyWith(
color: Colors.white,
),
),
const Gap(24),
KwButton.accent(
label: 'Sign In',
onPressed: () {
context.router.push(const SignInRoute());
}),
// const Gap(12),
// KwButton.outlinedAccent(
// leftIcon: Assets.images.signInApple,
// label: 'Sign In with Apple',
// onPressed: () {},
// ),
// const Gap(12),
// KwButton.outlinedAccent(
// leftIcon: Assets.images.signInGoogle,
// label: 'Sign In with Google',
// onPressed: () {})
// .copyWith(originalIconsColors: true),
const Gap(32),
],
),
),
),
]),
);
}
Container _background(BuildContext context) {
return Container(
alignment: Alignment.topCenter,
height: MediaQuery.of(context).size.height,
color: AppColors.primaryBlue,
);
}
}
class ArcClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path();
path.lineTo(0, size.height - 120);
path.quadraticBezierTo(
size.width / 2,
size.height,
size.width,
size.height - 120,
);
path.lineTo(size.width, 0);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}