login impl

This commit is contained in:
efrilm 2025-09-18 08:17:24 +07:00
parent 1ca1a45126
commit cee78e179b
6 changed files with 96 additions and 26 deletions

View File

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -41,6 +43,10 @@ class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
final phoneNumberValid = state.phoneNumber.isNotEmpty; final phoneNumberValid = state.phoneNumber.isNotEmpty;
final passwordValid = state.password.isNotEmpty; final passwordValid = state.password.isNotEmpty;
log(
'phoneNumberValid: $phoneNumberValid, passwordValid: $passwordValid, phoneNumber: ${state.phoneNumber}, password: ${state.password}',
);
if (phoneNumberValid && passwordValid) { if (phoneNumberValid && passwordValid) {
failureOrLogin = await _authRepository.login( failureOrLogin = await _authRepository.login(
phoneNumber: state.phoneNumber, phoneNumber: state.phoneNumber,

View File

@ -27,6 +27,7 @@ class ApiClient {
ApiClient(this._dio, this._env) { ApiClient(this._dio, this._env) {
_dio.options.baseUrl = _env.baseUrl; _dio.options.baseUrl = _env.baseUrl;
_dio.options.connectTimeout = const Duration(seconds: 20); _dio.options.connectTimeout = const Duration(seconds: 20);
_dio.options.validateStatus = (status) => true;
_dio.interceptors.add(BadNetworkErrorInterceptor()); _dio.interceptors.add(BadNetworkErrorInterceptor());
_dio.interceptors.add(BadRequestErrorInterceptor()); _dio.interceptors.add(BadRequestErrorInterceptor());
_dio.interceptors.add(InternalServerErrorInterceptor()); _dio.interceptors.add(InternalServerErrorInterceptor());

View File

@ -3,6 +3,6 @@ class ApiPath {
static String register = '/api/v1/customer-auth/register/start'; static String register = '/api/v1/customer-auth/register/start';
static String verify = '/api/v1/customer-auth/register/verify-otp'; static String verify = '/api/v1/customer-auth/register/verify-otp';
static String setPassword = '/api/v1/customer-auth/register/set-password'; static String setPassword = '/api/v1/customer-auth/register/set-password';
static String login = '/api/v1/customer-auth/register/login'; static String login = '/api/v1/customer-auth/login';
static String resend = '/api/v1/customer-auth/register/resend-otp'; static String resend = '/api/v1/customer-auth/register/resend-otp';
} }

View File

@ -10,6 +10,7 @@ class AppPasswordTextFormField extends StatefulWidget {
this.prefixIcon, this.prefixIcon,
this.keyboardType, this.keyboardType,
this.onChanged, this.onChanged,
this.validator,
}); });
final String? hintText; final String? hintText;
@ -19,6 +20,7 @@ class AppPasswordTextFormField extends StatefulWidget {
final Widget? prefixIcon; final Widget? prefixIcon;
final TextInputType? keyboardType; final TextInputType? keyboardType;
final ValueChanged<String>? onChanged; final ValueChanged<String>? onChanged;
final String? Function(String?)? validator;
@override @override
State<AppPasswordTextFormField> createState() => State<AppPasswordTextFormField> createState() =>
@ -26,7 +28,7 @@ class AppPasswordTextFormField extends StatefulWidget {
} }
class _AppPasswordTextFormFieldState extends State<AppPasswordTextFormField> { class _AppPasswordTextFormFieldState extends State<AppPasswordTextFormField> {
bool isPasswordVisible = false; bool isPasswordVisible = true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
@ -48,6 +50,7 @@ class _AppPasswordTextFormFieldState extends State<AppPasswordTextFormField> {
color: AppColor.textPrimary, color: AppColor.textPrimary,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
validator: widget.validator,
decoration: InputDecoration( decoration: InputDecoration(
hintText: widget.hintText, hintText: widget.hintText,
prefixIcon: widget.prefixIcon, prefixIcon: widget.prefixIcon,

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../application/auth/check_phone_form/check_phone_form_bloc.dart'; import '../../../../application/auth/check_phone_form/check_phone_form_bloc.dart';
import '../../../../common/function/app_function.dart';
import '../../../../common/theme/theme.dart'; import '../../../../common/theme/theme.dart';
import '../../../../domain/auth/auth.dart'; import '../../../../domain/auth/auth.dart';
import '../../../../injection.dart'; import '../../../../injection.dart';
@ -35,7 +36,9 @@ class LoginPage extends StatelessWidget implements AutoRouteWrapper {
context.router.push(RegisterRoute()); context.router.push(RegisterRoute());
} else if (data.status.isPasswordRequired) { } else if (data.status.isPasswordRequired) {
context.router.push( context.router.push(
PasswordRoute(phoneNumber: data.phoneNumber), PasswordRoute(
phoneNumber: getNormalizePhone(state.phoneNumber),
),
); );
} }
}); });

View File

@ -1,21 +1,47 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../application/auth/login_form/login_form_bloc.dart';
import '../../../../injection.dart';
import '../../../components/button/button.dart'; import '../../../components/button/button.dart';
import '../../../components/field/field.dart'; import '../../../components/field/field.dart';
import '../../../components/toast/flushbar.dart';
import '../../../router/app_router.gr.dart'; import '../../../router/app_router.gr.dart';
@RoutePage() @RoutePage()
class PasswordPage extends StatelessWidget { class PasswordPage extends StatelessWidget implements AutoRouteWrapper {
final String phoneNumber; final String phoneNumber;
const PasswordPage({super.key, required this.phoneNumber}); const PasswordPage({super.key, required this.phoneNumber});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return BlocListener<LoginFormBloc, LoginFormState>(
listenWhen: (p, c) => p.failureOrLoginOption != c.failureOrLoginOption,
listener: (context, state) {
state.failureOrLoginOption.fold(
() => null,
(either) => either.fold(
(f) => AppFlushbar.showAuthFailureToast(context, f),
(data) {
AppFlushbar.showSuccess(context, data.message);
Future.delayed(Duration(milliseconds: 1000), () {
context.router.replaceAll([MainRoute()]);
});
},
),
);
},
child: Scaffold(
appBar: AppBar(title: const Text('Kata Sandi')), appBar: AppBar(title: const Text('Kata Sandi')),
body: Padding( body: BlocBuilder<LoginFormBloc, LoginFormState>(
builder: (context, state) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Form(
autovalidateMode: state.showErrorMessages
? AutovalidateMode.always
: AutovalidateMode.disabled,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -25,6 +51,20 @@ class PasswordPage extends StatelessWidget {
AppPasswordTextFormField( AppPasswordTextFormField(
title: 'Masukkan kata sandi', title: 'Masukkan kata sandi',
hintText: '********', hintText: '********',
onChanged: (value) => context.read<LoginFormBloc>().add(
LoginFormEvent.passwordChanged(value),
),
validator: (value) {
if (context
.read<LoginFormBloc>()
.state
.password
.isEmpty) {
return 'Masukkan kata sandi';
}
return null;
},
), ),
const SizedBox(height: 50), const SizedBox(height: 50),
@ -33,8 +73,13 @@ class PasswordPage extends StatelessWidget {
// Continue Button // Continue Button
AppElevatedButton( AppElevatedButton(
onPressed: () => context.router.push(const MainRoute()), onPressed: state.isSubmitting
? null
: () => context.read<LoginFormBloc>().add(
LoginFormEvent.submitted(),
),
title: 'Konfirmasi', title: 'Konfirmasi',
isLoading: state.isSubmitting,
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
@ -42,5 +87,17 @@ class PasswordPage extends StatelessWidget {
), ),
), ),
); );
},
),
),
);
} }
@override
Widget wrappedRoute(BuildContext context) => BlocProvider(
create: (context) =>
getIt<LoginFormBloc>()
..add(LoginFormEvent.phoneNumberChanged(phoneNumber)),
child: this,
);
} }