feat : login api call

This commit is contained in:
2025-05-17 15:24:06 +03:30
parent 0e630e709b
commit 303ff86d85
22 changed files with 518 additions and 522 deletions

View File

@@ -30,10 +30,13 @@ export 'package:rasadyar_core/presentation/common/common.dart';
export 'package:rasadyar_core/presentation/utils/utils.dart';
export 'package:rasadyar_core/presentation/widget/widget.dart';
export 'infrastructure/remote/dio_form_data.dart';
//network
export 'infrastructure/remote/dio_form_data.dart';
export 'infrastructure/remote/dio_remote.dart';
export 'infrastructure/remote/dio_response.dart';
export 'package:dio/dio.dart' show DioException;
//utils
export 'utils/logger_utils.dart';
export 'utils/safe_call_utils.dart';

View File

@@ -16,7 +16,11 @@ class DioRemote implements IHttpClient {
Future<void> init() async {
final dio = Dio(BaseOptions(baseUrl: baseUrl));
if (kDebugMode) {
dio.interceptors.add(PrettyDioLogger());
dio.interceptors.add(PrettyDioLogger(
requestHeader: true,
responseHeader: true,
requestBody: true
));
}
_dio = dio;
}

View File

@@ -3,51 +3,65 @@ import 'package:rasadyar_core/presentation/common/app_color.dart';
import 'package:rasadyar_core/presentation/common/app_fonts.dart';
class RElevated extends StatelessWidget {
RElevated({
const RElevated({
super.key,
required this.text,
required this.onPressed,
this.foregroundColor,
this.backgroundColor,
this.foregroundColor = Colors.white,
this.backgroundColor = AppColor.blueNormal,
this.disabledBackgroundColor,
this.disabledForegroundColor,
this.radius,
this.disabledForegroundColor = Colors.white,
this.radius = 8.0,
this.textStyle,
this.width = 150.0,
this.height = 56.0,
this.isFullWidth,
this.isFullWidth = false,
this.isLoading = false,
});
final String text;
final VoidCallback? onPressed;
final double width;
final double height;
final bool? isFullWidth;
Color? foregroundColor;
Color? backgroundColor;
Color? disabledForegroundColor;
Color? disabledBackgroundColor;
double? radius;
TextStyle? textStyle;
final bool isFullWidth;
final Color foregroundColor;
final Color backgroundColor;
final Color? disabledForegroundColor;
final Color? disabledBackgroundColor;
final double radius;
final TextStyle? textStyle;
final bool isLoading;
@override
Widget build(BuildContext context) {
final bool isEnabled = onPressed != null && !isLoading;
return ElevatedButton(
onPressed: onPressed,
onPressed: isEnabled ? onPressed : null,
style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor ?? AppColor.blueNormal,
foregroundColor: foregroundColor ?? Colors.white,
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
disabledBackgroundColor:
disabledBackgroundColor ?? AppColor.blueNormal.withAlpha(38),
disabledForegroundColor: disabledForegroundColor ?? Colors.white,
disabledBackgroundColor ?? backgroundColor.withAlpha(38),
disabledForegroundColor: disabledForegroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius ?? 8),
borderRadius: BorderRadius.circular(radius),
),
minimumSize: Size((isFullWidth ??false) ? double.infinity : width, height),
minimumSize: Size(isFullWidth ? double.infinity : width, height),
padding: EdgeInsets.zero,
textStyle: textStyle ?? AppFonts.yekan24,
),
child: Text(text),
child:
isLoading
? SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(foregroundColor),
),
)
: Text(text),
);
}
}

View File

@@ -2,164 +2,106 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
@immutable
enum RTextFieldVariant {
normal,
noBorder,
password,
passwordNoBorder,
}
class RTextField extends StatefulWidget {
RTextField({
super.key,
this.maxLines,
this.maxLength,
this.hintText,
this.padding,
this.onChanged,
this.onSubmitted,
this.keyboardType,
this.showCounter = false,
this.isDense,
this.initText,
this.isForNumber = false,
this.style,
this.hintStyle,
this.suffixIcon,
this.prefixIcon,
this.validator,
this.readonly = false,
this.boxConstraints,
this.minLines,
this.radius,
this.filled,
this.filledColor,
this.enabled,
this.errorStyle,
this.labelStyle,
this.label,
}) {
filled = filled ?? false;
obscure = false;
_inputBorder = OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(radius ?? 8),
);
}
RTextField.noBorder({
super.key,
this.maxLines,
this.maxLength,
this.hintText,
this.padding,
this.onChanged,
this.onSubmitted,
this.keyboardType,
this.showCounter = false,
this.isDense,
this.initText,
this.style,
this.hintStyle,
this.suffixIcon,
this.radius,
this.validator,
this.boxConstraints,
this.minLines,
this.isForNumber = false,
this.readonly = false,
this.label,
this.filled,
this.filledColor,
this.errorStyle,
this.labelStyle,
this.enabled,
}) {
_inputBorder = OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(radius ?? 16),
);
obscure = false;
filled = filled ?? true;
}
RTextField.password({
super.key,
this.maxLines = 1,
this.maxLength,
this.hintText,
this.padding,
this.onChanged,
this.onSubmitted,
this.keyboardType,
this.showCounter = false,
this.isDense,
this.initText,
this.style,
this.hintStyle,
this.suffixIcon,
this.prefixIcon,
this.radius,
this.validator,
this.boxConstraints,
this.minLines,
this.isForNumber = false,
this.readonly = false,
this.label,
this.filled,
this.filledColor,
this.errorStyle,
this.labelStyle,
this.enabled,
}) {
_inputBorder = OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(radius ?? 16),
);
filled = filled ?? true;
obscure = true;
_isPassword = true;
prefixIcon = prefixIcon ?? const Icon(CupertinoIcons.person);
}
final int? maxLines;
final int? minLines;
final int? maxLength;
final String? hintText;
final TextEditingController controller;
final String? label;
final EdgeInsets? padding;
final String? hintText;
final String? initText;
final bool obscure;
final bool readonly;
final bool enabled;
final int? maxLength;
final int? minLines;
final int? maxLines;
final Widget? suffixIcon;
final Widget? prefixIcon;
final BoxConstraints? boxConstraints;
final RTextFieldVariant variant;
final bool filled;
final Color? filledColor;
final bool showCounter;
final bool isDense;
final TextInputType? keyboardType;
final TextStyle? style;
final TextStyle? errorStyle;
final TextStyle? hintStyle;
final TextStyle? labelStyle;
final bool showCounter;
final bool? isDense;
final bool? isForNumber;
final bool readonly;
bool? obscure;
final bool? enabled;
final double? radius;
final TextInputType? keyboardType;
final Function(String)? onChanged;
final Function(String)? onSubmitted;
final FormFieldValidator? validator;
final String? initText;
Widget? suffixIcon;
Widget? prefixIcon;
bool? filled;
Color? filledColor;
bool _isPassword = false;
final TextStyle? errorStyle;
final EdgeInsets? padding;
final FormFieldValidator<String>? validator;
final void Function(String)? onChanged;
final void Function(String)? onSubmitted;
const RTextField({
super.key,
required this.controller,
this.label,
this.hintText,
this.initText,
this.obscure = false,
this.readonly = false,
this.enabled = true,
this.maxLength,
this.minLines,
this.maxLines = 1,
this.suffixIcon,
this.prefixIcon,
this.boxConstraints,
this.variant = RTextFieldVariant.normal,
this.filled = false,
this.filledColor,
this.showCounter = false,
this.isDense = false,
this.keyboardType,
this.style,
this.hintStyle,
this.labelStyle,
this.errorStyle,
this.padding,
this.validator,
this.onChanged,
this.onSubmitted,
});
final BoxConstraints? boxConstraints;
late final InputBorder? _inputBorder;
@override
State<RTextField> createState() => _RTextFieldState();
bool get _isPassword => variant == RTextFieldVariant.password;
bool get _noBorder => variant == RTextFieldVariant.noBorder;
bool get _passwordNoBorder => variant == RTextFieldVariant.passwordNoBorder;
InputBorder get _inputBorder =>
_noBorder || _passwordNoBorder ? InputBorder.none : OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColor.lightGreyDarkActive,
width: 1,
),
);
}
class _RTextFieldState extends State<RTextField> {
final TextEditingController _controller = TextEditingController();
bool? obscure;
late bool obscure;
@override
void initState() {
super.initState();
if (widget.initText != null) {
_controller.text = widget.initText!;
widget.controller.text = widget.initText!;
}
obscure = widget.obscure;
}
@@ -167,51 +109,56 @@ class _RTextFieldState extends State<RTextField> {
@override
void didUpdateWidget(covariant RTextField oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.initText != oldWidget.initText) {
_controller.text = widget.initText ?? '';
if (widget.initText != null && widget.initText != oldWidget.initText) {
widget.controller.text = widget.initText!;
}
}
Widget _buildSuffixIcon() {
if (widget.suffixIcon != null) return widget.suffixIcon!;
if (!widget._isPassword) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: GestureDetector(
onTap: () {
setState(() {
obscure = !obscure;
});
},
child: Icon(
obscure ? CupertinoIcons.eye : CupertinoIcons.eye_slash,
color: AppColor.darkGreyDarkActive,
),
),
);
}
@override
Widget build(BuildContext context) {
return Padding(
padding: widget.padding ?? EdgeInsets.zero,
child: TextFormField(
controller: _controller,
controller: widget.controller,
readOnly: widget.readonly,
minLines: widget.minLines,
maxLines: widget.maxLines,
onChanged: widget.onChanged,
validator: widget.validator,
enabled: widget.enabled,
obscureText: obscure ?? false,
onTapOutside: (event) {
FocusScope.of(context).unfocus();
},
obscureText: obscure,
onTapOutside: (_) => FocusScope.of(context).unfocus(),
onFieldSubmitted: widget.onSubmitted,
maxLength: widget.maxLength,
textDirection: TextDirection.rtl,
style: widget.style,
keyboardType: widget.keyboardType,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 16),
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
errorStyle: widget.errorStyle,
errorMaxLines: 1,
isDense: widget.isDense,
suffixIcon:
widget.suffixIcon ??
(widget._isPassword
? IconButton(
onPressed: () {
setState(() {
obscure = !obscure!;
});
},
icon: Icon(
!obscure! ? CupertinoIcons.eye_slash : CupertinoIcons.eye,
),
)
: null),
suffixIcon: _buildSuffixIcon(),
suffixIconConstraints: widget.boxConstraints,
prefixIcon: widget.prefixIcon,
prefixIconConstraints: widget.boxConstraints,
@@ -221,7 +168,7 @@ class _RTextFieldState extends State<RTextField> {
labelStyle: AppFonts.yekan14
.copyWith(color: AppColor.lightGreyDarkActive)
.merge(widget.labelStyle),
filled: widget.filled,
filled: widget.filled || widget._noBorder || widget._passwordNoBorder,
fillColor: widget.filledColor,
counter: widget.showCounter ? null : const SizedBox(),
hintStyle: widget.hintStyle,
@@ -232,4 +179,4 @@ class _RTextFieldState extends State<RTextField> {
),
);
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:rasadyar_core/core.dart';
@@ -6,7 +7,7 @@ typedef ErrorCallback = void Function(dynamic error, StackTrace? stackTrace);
typedef VoidCallback = void Function();
// تعریف دقیق تابع safeCall
Future<T?> safeCall<T>({
Future<void> safeCall<T>({
required AsyncCallback<T> call,
Function(T result)? onSuccess,
ErrorCallback? onError,
@@ -33,7 +34,7 @@ Future<T?> safeCall<T>({
}
onSuccess?.call(result);
return result;
} catch (error, stackTrace) {
if (showError) {
@@ -45,7 +46,6 @@ Future<T?> safeCall<T>({
print('safeCall error: $error\n$stackTrace');
}
return null;
} finally {
if (showLoading) {
(onHideLoading ?? _defaultHideLoading)();