feat: login page

This commit is contained in:
efrilm
2025-08-12 17:13:02 +07:00
parent afc8476701
commit 318d22d7c9
18 changed files with 606 additions and 22 deletions
@@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import '../../../common/theme/theme.dart';
import '../spacer/spacer.dart';
part 'elevated_button.dart';
@@ -0,0 +1,67 @@
part of 'button.dart';
class AppElevatedButton extends StatelessWidget {
const AppElevatedButton({
super.key,
required this.text,
required this.isLoading,
required this.onPressed,
this.height = 50,
});
final String text;
final bool isLoading;
final Function()? onPressed;
final double height;
@override
Widget build(BuildContext context) {
return Container(
height: height,
decoration: BoxDecoration(
gradient: const LinearGradient(colors: AppColor.primaryGradient),
borderRadius: BorderRadius.circular(AppValue.radius),
boxShadow: [
BoxShadow(
color: AppColor.primaryWithOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 8),
),
],
),
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: isLoading
? Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Loading',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.white,
),
),
SpaceWidth(8),
SpinKitCircle(color: AppColor.white, size: 24.0),
],
)
: Text(
text,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.white,
),
),
),
);
}
}
@@ -0,0 +1,9 @@
import 'package:flutter/material.dart';
import 'package:line_icons/line_icon.dart';
import 'package:line_icons/line_icons.dart';
import '../../../common/theme/theme.dart';
import '../spacer/spacer.dart';
part 'password_text_form_field.dart';
part 'text_form_field.dart';
@@ -0,0 +1,69 @@
part of 'field.dart';
class AppPasswordTextFormField extends StatefulWidget {
const AppPasswordTextFormField({
super.key,
this.controller,
required this.title,
this.hintText,
required this.prefixIcon,
this.validator,
});
final TextEditingController? controller;
final String title;
final String? hintText;
final IconData prefixIcon;
final String? Function(String?)? validator;
@override
State<AppPasswordTextFormField> createState() =>
_AppPasswordTextFormFieldState();
}
class _AppPasswordTextFormFieldState extends State<AppPasswordTextFormField> {
bool _obscurePassword = true;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
const SpaceHeight(8),
TextFormField(
controller: widget.controller,
keyboardType: TextInputType.emailAddress,
cursorColor: AppColor.primary,
obscureText: _obscurePassword,
style: AppStyle.md.copyWith(color: AppColor.textPrimary),
decoration: InputDecoration(
hintText: widget.hintText,
prefixIcon: LineIcon(
widget.prefixIcon,
color: AppColor.textSecondary,
),
suffixIcon: IconButton(
padding: EdgeInsets.zero,
icon: Icon(
_obscurePassword ? LineIcons.eye : LineIcons.eyeSlash,
color: AppColor.textSecondary,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
),
validator: widget.validator,
),
],
);
}
}
@@ -0,0 +1,46 @@
part of 'field.dart';
class AppTextFormField extends StatelessWidget {
const AppTextFormField({
super.key,
this.controller,
required this.title,
this.hintText,
required this.prefixIcon,
this.validator,
});
final TextEditingController? controller;
final String title;
final String? hintText;
final IconData prefixIcon;
final String? Function(String?)? validator;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
const SpaceHeight(8),
TextFormField(
controller: controller,
keyboardType: TextInputType.emailAddress,
cursorColor: AppColor.primary,
style: AppStyle.md.copyWith(color: AppColor.textPrimary),
decoration: InputDecoration(
hintText: hintText,
prefixIcon: LineIcon(prefixIcon, color: AppColor.textSecondary),
),
validator: validator,
),
],
);
}
}
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
class SpaceHeight extends StatelessWidget {
const SpaceHeight(this.height, {super.key});
final double height;
@override
Widget build(BuildContext context) {
return SizedBox(height: height);
}
}
class SpaceWidth extends StatelessWidget {
const SpaceWidth(this.width, {super.key});
final double width;
@override
Widget build(BuildContext context) {
return SizedBox(width: width);
}
}