Initial commit
This commit is contained in:
43
lib/app.dart
Normal file
43
lib/app.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:cargocalculaterapp/router/app_routes.dart';
|
||||
import 'package:cargocalculaterapp/router/name_routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'core/app_bloc/app_bloc.dart';
|
||||
import 'core/theme/theme_data.dart';
|
||||
import 'generated/l10n.dart';
|
||||
|
||||
class App extends StatelessWidget {
|
||||
const App({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AppBloc, AppState>(
|
||||
builder: (context, state) {
|
||||
return KeyboardDismissOnTap(
|
||||
child: MaterialApp(
|
||||
navigatorKey: rootNavigatorKey,
|
||||
initialRoute: Routes.initial,
|
||||
onGenerateRoute: AppRoutes.onGenerateRoute,
|
||||
title: 'CargoApp',
|
||||
debugShowCheckedModeBanner: false,
|
||||
themeMode: ThemeMode.values.byName(
|
||||
state.themeMode?.name ?? ThemeMode.light.name,
|
||||
),
|
||||
theme: state.themeMode == ThemeMode.light ? lightTheme : darkTheme,
|
||||
darkTheme: darkTheme,
|
||||
locale: Locale(state.appLocale),
|
||||
supportedLocales: AppLocalization.delegate.supportedLocales,
|
||||
localizationsDelegates: const [
|
||||
AppLocalization.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
130
lib/constants/constants.dart
Normal file
130
lib/constants/constants.dart
Normal file
@@ -0,0 +1,130 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../core/entity/name/name_entity.dart';
|
||||
|
||||
class Constants {
|
||||
static String baseUrl = "https://ctrans-api.felix-its.uz";
|
||||
}
|
||||
class Validations {
|
||||
static const someThingWentWrong = 'Something went wrong!';
|
||||
}
|
||||
|
||||
class DioConstants {
|
||||
static final options = Options(
|
||||
contentType: 'application/json',
|
||||
sendTimeout: const Duration(seconds: 30),
|
||||
);
|
||||
}
|
||||
|
||||
class AppKeys {
|
||||
static const locale = 'locale';
|
||||
static const accessToken = 'access_token';
|
||||
static const firstName = 'firstname';
|
||||
static const lastName = 'lastname';
|
||||
static const email = 'email';
|
||||
static const isFirst = "isFirst";
|
||||
static const gender = "gender";
|
||||
static const userId = "userId";
|
||||
static const dateOfBirth = "dateOfBirth";
|
||||
static const refreshToken = "refreshToken";
|
||||
static const phone = "phone";
|
||||
static const hasProfile = "hasProfile";
|
||||
static const isProd = 'isProd';
|
||||
static const page = 'page';
|
||||
static const limit = 'limit';
|
||||
static const status = 'status';
|
||||
static const sort = 'sort';
|
||||
static const themeMode = 'themeMode';
|
||||
static const ucode = 'UCode';
|
||||
}
|
||||
|
||||
class AppConst {
|
||||
static const limit = 20;
|
||||
static const reviewLimit = 10;
|
||||
static const all = "all";
|
||||
static const status = "status";
|
||||
static const waiting = "WAITING";
|
||||
static const partReceived = "PART_RECEIVED";
|
||||
static const fullReceived = "FULL_RECEIVED";
|
||||
static const shipped = "SHIPPED";
|
||||
static const leftChine = "LEFT_CHINA";
|
||||
static const enteredUzbekistan = "ENTERED_UZBEKISTAN";
|
||||
static const tashkentWarehouse = "TASHKENT_WAREHOUSE";
|
||||
static const customsClearance = "CUSTOMS_CLEARANCE";
|
||||
static const delivered = "DELIVERED";
|
||||
static const clickAction = "page_link";
|
||||
static const Map<String, NameEntity> statusList = {
|
||||
all: NameEntity(uz: "Barchasi", ru: "Все", zh: "全部"),
|
||||
waiting: NameEntity(uz: "Kutilmoqda", ru: "Ожидание", zh: "等待中"),
|
||||
partReceived: NameEntity(
|
||||
uz: "Yarim keldi",
|
||||
ru: "Частичный прибыл",
|
||||
zh: "部分接收",
|
||||
),
|
||||
fullReceived: NameEntity(
|
||||
uz: "To'liq keldi",
|
||||
ru: "Полный прибл",
|
||||
zh: "已全部接收",
|
||||
),
|
||||
shipped: NameEntity(uz: "Yuborildi", ru: "Отправлено", zh: "已发货"),
|
||||
leftChine: NameEntity(
|
||||
uz: "Xitoydan chiqdi",
|
||||
ru: "Покинул Китай",
|
||||
zh: "离开中国",
|
||||
),
|
||||
enteredUzbekistan: NameEntity(
|
||||
uz: "O'zbekistonga kirdi",
|
||||
ru: "Прибыл в Узбекистан",
|
||||
zh: "进入乌兹别克斯坦",
|
||||
),
|
||||
tashkentWarehouse: NameEntity(
|
||||
uz: "Toshkent omborida",
|
||||
ru: "На складе в Ташкенте",
|
||||
zh: "塔什干仓库",
|
||||
),
|
||||
customsClearance: NameEntity(
|
||||
uz: "Bojxona rasmiylashtiruvi",
|
||||
ru: "Таможенное оформление",
|
||||
zh: "清关中",
|
||||
),
|
||||
delivered: NameEntity(uz: "Yetkazib berildi", ru: "Доставлено", zh: "已送达"),
|
||||
};
|
||||
static const warehouseOptions = {
|
||||
"1": NameEntity(uz: "Yiwu", ru: "Иу", zh: "义乌"),
|
||||
"2": NameEntity(uz: "Foshan", ru: "Фошань", zh: "佛山"),
|
||||
"3": NameEntity(uz: "Qashqar", ru: "Кашгар", zh: "喀什"),
|
||||
"4": NameEntity(uz: "Hargos", ru: "Харгос", zh: "哈戈斯"),
|
||||
};
|
||||
}
|
||||
|
||||
class Urls {
|
||||
static const login = '/api/v1/auth/login/user';
|
||||
static const signUp = '/api/v1/auth/register';
|
||||
static const getProfile = '/api/v1/mobile/me';
|
||||
static const updateProfile = '/api/v1/mobile/me/{id}';
|
||||
static const verify = '/api/v1/auth/verify-sms';
|
||||
static const orderList = '/api/v1/mobile/orders';
|
||||
static const calculatePrice = '/api/v1/leads/calculate';
|
||||
static const lead = '/api/v1/leads';
|
||||
static const addFcm = '/api/v1/user/update-sfm-token';
|
||||
static const comment = '/api/v1/testimonials';
|
||||
static const notification = '/api/v1/notifications';
|
||||
static const notificationRead = '/api/v1/notifications/mark-as-read';
|
||||
static const banners = '/api/v1/slider';
|
||||
static const delete = '/api/v1/mobile/me';
|
||||
}
|
||||
|
||||
class CacheKeys {
|
||||
static const profile = "profile.cache";
|
||||
}
|
||||
|
||||
class Warnings {
|
||||
Warnings._();
|
||||
|
||||
static const someThingWentWrong = 'Something went wrong!';
|
||||
}
|
||||
|
||||
class RegExConst {
|
||||
static final phoneRegex = RegExp(r'^\+?\d{12}$');
|
||||
static final emailRegex = RegExp(r'^[a-zA-Z0-9._%+-]+@gmail\.com$');
|
||||
}
|
||||
46
lib/core/app_bloc/app_bloc.dart
Normal file
46
lib/core/app_bloc/app_bloc.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../injector_container.dart';
|
||||
import '../local_source/local_source.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
|
||||
part 'app_event.dart';
|
||||
|
||||
part 'app_state.dart';
|
||||
|
||||
class AppBloc extends Bloc<AppEvent, AppState> {
|
||||
AppBloc()
|
||||
: super(
|
||||
AppState(
|
||||
lightTheme: lightThemes.values.first,
|
||||
darkTheme: darkThemes.values.first,
|
||||
themeMode: ThemeMode.values.byName(sl<LocalSource>().getThemeMode()),
|
||||
appLocale: sl<LocalSource>().getLocale(),
|
||||
),
|
||||
) {
|
||||
on<AppThemeSwitchLight>(_switchToLightHandler);
|
||||
on<AppThemeSwitchDark>(_switchToDarkHandler);
|
||||
on<AppChangeLocale>(_changeLocale);
|
||||
}
|
||||
|
||||
void _changeLocale(AppChangeLocale event, Emitter<AppState> emit) {
|
||||
sl<LocalSource>().setLocale(event.appLocale);
|
||||
emit(state.copyWith(appLocale: event.appLocale));
|
||||
}
|
||||
|
||||
void _switchToLightHandler(
|
||||
AppThemeSwitchLight event,
|
||||
Emitter<AppState> emit,
|
||||
) {
|
||||
sl<LocalSource>().setThemeMode(ThemeMode.light.name);
|
||||
emit(
|
||||
state.copyWith(lightTheme: event.lightTheme, themeMode: ThemeMode.light),
|
||||
);
|
||||
}
|
||||
|
||||
void _switchToDarkHandler(AppThemeSwitchDark event, Emitter<AppState> emit) {
|
||||
sl<LocalSource>().setThemeMode(ThemeMode.dark.name);
|
||||
emit(state.copyWith(darkTheme: event.darkTheme, themeMode: ThemeMode.dark));
|
||||
}
|
||||
}
|
||||
32
lib/core/app_bloc/app_event.dart
Normal file
32
lib/core/app_bloc/app_event.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
part of 'app_bloc.dart';
|
||||
|
||||
sealed class AppEvent extends Equatable {
|
||||
const AppEvent();
|
||||
}
|
||||
|
||||
final class AppThemeSwitchLight extends AppEvent {
|
||||
final ThemeData lightTheme;
|
||||
|
||||
const AppThemeSwitchLight({required this.lightTheme});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [lightTheme];
|
||||
}
|
||||
|
||||
final class AppThemeSwitchDark extends AppEvent {
|
||||
final ThemeData darkTheme;
|
||||
|
||||
const AppThemeSwitchDark({required this.darkTheme});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [darkTheme];
|
||||
}
|
||||
|
||||
final class AppChangeLocale extends AppEvent {
|
||||
final String appLocale;
|
||||
|
||||
const AppChangeLocale(this.appLocale);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [appLocale];
|
||||
}
|
||||
37
lib/core/app_bloc/app_state.dart
Normal file
37
lib/core/app_bloc/app_state.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
part of 'app_bloc.dart';
|
||||
|
||||
class AppState extends Equatable {
|
||||
final ThemeData? lightTheme;
|
||||
final ThemeData? darkTheme;
|
||||
final ThemeMode? themeMode;
|
||||
final String appLocale;
|
||||
|
||||
const AppState({
|
||||
required this.appLocale,
|
||||
this.lightTheme,
|
||||
this.darkTheme,
|
||||
this.themeMode,
|
||||
});
|
||||
|
||||
AppState copyWith({
|
||||
ThemeData? lightTheme,
|
||||
ThemeData? darkTheme,
|
||||
ThemeMode? themeMode,
|
||||
String? appLocale,
|
||||
}) {
|
||||
return AppState(
|
||||
lightTheme: lightTheme ?? this.lightTheme,
|
||||
darkTheme: darkTheme ?? this.darkTheme,
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
appLocale: appLocale ?? this.appLocale,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
lightTheme,
|
||||
darkTheme,
|
||||
themeMode,
|
||||
appLocale,
|
||||
];
|
||||
}
|
||||
12
lib/core/entity/name/name_entity.dart
Normal file
12
lib/core/entity/name/name_entity.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class NameEntity extends Equatable {
|
||||
final String? uz;
|
||||
final String? ru;
|
||||
final String? zh;
|
||||
|
||||
const NameEntity({this.uz, this.ru, this.zh});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [uz, ru, zh];
|
||||
}
|
||||
26
lib/core/error/exceptions.dart
Normal file
26
lib/core/error/exceptions.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
class ServerException implements Exception {
|
||||
final String message;
|
||||
|
||||
ServerException({required this.message});
|
||||
|
||||
factory ServerException.fromJson(Map<String, dynamic> json) {
|
||||
return ServerException(message: json['message']);
|
||||
}
|
||||
}
|
||||
|
||||
class NoInternetException implements Exception {}
|
||||
|
||||
class CacheException implements Exception {
|
||||
final String message;
|
||||
|
||||
CacheException({required this.message});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleOrdersException implements Exception {
|
||||
MultipleOrdersException();
|
||||
}
|
||||
33
lib/core/error/failure.dart
Normal file
33
lib/core/error/failure.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class Failure extends Equatable {}
|
||||
|
||||
class ServerFailure extends Failure {
|
||||
final String message;
|
||||
|
||||
ServerFailure({required this.message});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
class NoInternetFailure extends Failure {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class CacheFailure extends Failure {
|
||||
final String message;
|
||||
|
||||
CacheFailure({required this.message});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
class MultipleOrderFailure extends Failure {
|
||||
MultipleOrderFailure();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
12
lib/core/extension/build_context_extension.dart
Normal file
12
lib/core/extension/build_context_extension.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/colors/app_colors.dart';
|
||||
import '../theme/theme_text_style.dart';
|
||||
|
||||
extension BuildContextExt on BuildContext {
|
||||
ThemeTextStyles get text => Theme.of(this).extension<ThemeTextStyles>()!;
|
||||
|
||||
AppThemeColors get color => Theme.of(this).extension<AppThemeColors>()!;
|
||||
|
||||
bool get isDarkMode => Theme.of(this).brightness == Brightness.dark;
|
||||
}
|
||||
11
lib/core/extension/color_extension.dart
Normal file
11
lib/core/extension/color_extension.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension HexColor on Color {
|
||||
/// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#".
|
||||
static Color fromHex(String hexString) {
|
||||
final buffer = StringBuffer();
|
||||
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
|
||||
buffer.write(hexString.replaceFirst('#', ''));
|
||||
return Color(int.parse(buffer.toString(), radix: 16));
|
||||
}
|
||||
}
|
||||
13
lib/core/extension/extension.dart
Normal file
13
lib/core/extension/extension.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
extension SliverCountExtension on int {
|
||||
int get doubleTheListCount => (this * 2) - 1;
|
||||
|
||||
int get exactIndex => (this ~/ 2);
|
||||
|
||||
int get lastIndex => (this * 2) - 2;
|
||||
}
|
||||
|
||||
extension VersionParsing on String {
|
||||
int toVersion() {
|
||||
return int.tryParse(replaceAll(".", "")) ?? 0;
|
||||
}
|
||||
}
|
||||
12
lib/core/extension/size_extentione.dart
Normal file
12
lib/core/extension/size_extentione.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import '../../router/app_routes.dart';
|
||||
|
||||
extension ScreenSize on double {
|
||||
double get w {
|
||||
return this / 375 * MediaQuery.sizeOf(rootNavigatorKey.currentContext!).width;
|
||||
}
|
||||
|
||||
double get h {
|
||||
return this / 812 * MediaQuery.sizeOf(rootNavigatorKey.currentContext!).height;
|
||||
}
|
||||
}
|
||||
73
lib/core/functions/base_finctions.dart
Normal file
73
lib/core/functions/base_finctions.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../router/app_routes.dart';
|
||||
import '../entity/name/name_entity.dart';
|
||||
import '../models/name.dart';
|
||||
|
||||
class Functions {
|
||||
static String getTranslatedItem(NameEntity? name, BuildContext context) {
|
||||
var local = Localizations.localeOf(context);
|
||||
switch (local.languageCode) {
|
||||
case 'uz':
|
||||
{
|
||||
return name?.uz ?? " ";
|
||||
}
|
||||
case "ru":
|
||||
{
|
||||
return name?.ru ?? " ";
|
||||
}
|
||||
case "zh":
|
||||
{
|
||||
return name?.zh ?? " ";
|
||||
}
|
||||
default:
|
||||
{
|
||||
return name?.uz ?? " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getTranslatedNameModel(Name? name, BuildContext context) {
|
||||
var local = Localizations.localeOf(context);
|
||||
switch (local.languageCode) {
|
||||
case 'uz':
|
||||
{
|
||||
return name?.uz ?? " ";
|
||||
}
|
||||
case "ru":
|
||||
{
|
||||
return name?.ru ?? " ";
|
||||
}
|
||||
case "zh":
|
||||
{
|
||||
return name?.zh ?? " ";
|
||||
}
|
||||
default:
|
||||
{
|
||||
return name?.uz ?? " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
static void showErrorSnackBar(String message) {
|
||||
final snackBar = SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Row(
|
||||
children: [
|
||||
const Icon(Icons.error, color: Colors.white),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
message,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
duration: const Duration(seconds: 3),
|
||||
);
|
||||
|
||||
ScaffoldMessenger.of(
|
||||
rootNavigatorKey.currentContext!,
|
||||
).showSnackBar(snackBar);
|
||||
}
|
||||
}
|
||||
13
lib/core/functions/debouncer.dart
Normal file
13
lib/core/functions/debouncer.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class Debouncer {
|
||||
static Timer? _timer;
|
||||
|
||||
static run(VoidCallback action) {
|
||||
if (null != _timer) {
|
||||
_timer?.cancel();
|
||||
}
|
||||
_timer = Timer(const Duration(milliseconds: 600), action);
|
||||
}
|
||||
}
|
||||
37
lib/core/input_format/custom_text_input_format.dart
Normal file
37
lib/core/input_format/custom_text_input_format.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class CustomTextInputFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
var re = RegExp(('[^0-9]'));
|
||||
if (newValue.selection.baseOffset == 0 && newValue.text.isNotEmpty) {
|
||||
return newValue;
|
||||
}
|
||||
if (newValue.text.isEmpty) {
|
||||
return newValue.copyWith(text: '');
|
||||
} else if (newValue.text.compareTo(oldValue.text) != 0) {
|
||||
final selectionIndexFromTheRight =
|
||||
newValue.text.length - newValue.selection.extentOffset;
|
||||
final reversedText =
|
||||
String.fromCharCodes(newValue.text.runes.toList().reversed);
|
||||
final chars = reversedText.replaceAll(re, '').split('');
|
||||
var reversedNewString = '';
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
if (i % 3 == 0 && i != 0) reversedNewString += ' ';
|
||||
reversedNewString += chars[i];
|
||||
}
|
||||
final newString =
|
||||
String.fromCharCodes(reversedNewString.runes.toList().reversed);
|
||||
|
||||
return TextEditingValue(
|
||||
text: newString,
|
||||
selection: TextSelection.collapsed(
|
||||
offset: newString.length - selectionIndexFromTheRight,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
lib/core/input_format/mask_text_formatter.dart
Normal file
64
lib/core/input_format/mask_text_formatter.dart
Normal file
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class MaskedTextInputFormatter extends TextInputFormatter {
|
||||
final String mask;
|
||||
final String separator;
|
||||
|
||||
MaskedTextInputFormatter({
|
||||
required this.mask,
|
||||
required this.separator,
|
||||
});
|
||||
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
String text = newValue.text;
|
||||
String newText = newValue.toJSON()['text'].toString();
|
||||
if (text.isNotEmpty) {
|
||||
if (text.length > oldValue.text.length) {
|
||||
if (text.length > mask.length) return oldValue;
|
||||
if (text.length < mask.length && mask[text.length - 1] == separator) {
|
||||
return TextEditingValue(
|
||||
text:
|
||||
'${oldValue.text}$separator${text.substring(text.length - 1)}',
|
||||
selection: TextSelection.collapsed(
|
||||
offset: newValue.selection.end + 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (text.length == mask.replaceAll(separator, "").length &&
|
||||
oldValue.text.isEmpty) {
|
||||
String newText = '';
|
||||
int t = 0;
|
||||
for (int i = 0; i < text.length; i++) {
|
||||
if (mask[i + t] == separator) {
|
||||
newText += separator;
|
||||
t++;
|
||||
}
|
||||
newText += text[i];
|
||||
}
|
||||
return TextEditingValue(
|
||||
text: newText,
|
||||
selection: TextSelection.collapsed(offset: newText.length),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (newText.substring(newText.length - 1) == separator) {
|
||||
return TextEditingValue(
|
||||
text: newValue.text.substring(0, newValue.text.length - 1),
|
||||
selection: TextSelection.collapsed(
|
||||
offset: newValue.selection.end - 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return TextEditingValue(
|
||||
text: newText,
|
||||
selection: TextSelection.collapsed(
|
||||
offset: newValue.selection.end,
|
||||
),
|
||||
);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
134
lib/core/local_source/local_source.dart
Normal file
134
lib/core/local_source/local_source.dart
Normal file
@@ -0,0 +1,134 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import '../../constants/constants.dart';
|
||||
|
||||
class LocalSource {
|
||||
final Box<dynamic> box;
|
||||
|
||||
LocalSource(this.box);
|
||||
|
||||
Future<void> clear() async {
|
||||
await box.clear();
|
||||
}
|
||||
|
||||
Future<void> setLocale(String locale) async {
|
||||
await box.put(AppKeys.locale, locale);
|
||||
}
|
||||
|
||||
String getLocale() {
|
||||
return box.get(AppKeys.locale, defaultValue: "uz") ?? "uz";
|
||||
}
|
||||
|
||||
void setAccessToken(String accessToken) {
|
||||
box.put(AppKeys.accessToken, accessToken);
|
||||
}
|
||||
|
||||
String? getAccessToken() {
|
||||
return box.get(AppKeys.accessToken);
|
||||
}
|
||||
|
||||
void setFirstName(String firstName) {
|
||||
box.put(AppKeys.firstName, firstName);
|
||||
}
|
||||
|
||||
String? getFirstName() {
|
||||
return box.get(AppKeys.firstName);
|
||||
}
|
||||
|
||||
void setLastName(String lastName) {
|
||||
box.put(AppKeys.lastName, lastName);
|
||||
}
|
||||
|
||||
String? getLastName() {
|
||||
return box.get(AppKeys.lastName);
|
||||
}
|
||||
|
||||
void setEmail(String email) {
|
||||
box.put(AppKeys.email, email);
|
||||
}
|
||||
|
||||
String? getEmail() {
|
||||
return box.get(AppKeys.email);
|
||||
}
|
||||
|
||||
void setIsFirstEnter(bool isFirst) {
|
||||
box.put(AppKeys.isFirst, isFirst);
|
||||
}
|
||||
|
||||
bool getIsFirstEnter() {
|
||||
return box.get(AppKeys.isFirst) ?? true;
|
||||
}
|
||||
|
||||
void setUserId(String id) {
|
||||
box.put(AppKeys.userId, id);
|
||||
}
|
||||
|
||||
String getUserId() {
|
||||
return box.get(AppKeys.userId) ?? "";
|
||||
}
|
||||
|
||||
void setDateOfBirth(String id) {
|
||||
box.put(AppKeys.dateOfBirth, id);
|
||||
}
|
||||
|
||||
String getDateOfBirth() {
|
||||
return box.get(AppKeys.dateOfBirth) ?? "";
|
||||
}
|
||||
|
||||
void setRefreshToken(String id) {
|
||||
box.put(AppKeys.refreshToken, id);
|
||||
}
|
||||
|
||||
String getRefreshToken() {
|
||||
return box.get(AppKeys.refreshToken) ?? "";
|
||||
}
|
||||
|
||||
void setPhone(String id) {
|
||||
box.put(AppKeys.phone, id);
|
||||
}
|
||||
|
||||
String getPhone() {
|
||||
return box.get(AppKeys.phone) ?? "";
|
||||
}
|
||||
|
||||
void setHasProfile(bool id) {
|
||||
box.put(AppKeys.hasProfile, id);
|
||||
}
|
||||
|
||||
bool getHasProfile() {
|
||||
return box.get(AppKeys.hasProfile) ?? false;
|
||||
}
|
||||
|
||||
void setGender(String id) {
|
||||
box.put(AppKeys.gender, id);
|
||||
}
|
||||
|
||||
String getGender() {
|
||||
return box.get(AppKeys.gender) ?? "";
|
||||
}
|
||||
|
||||
void setIsProd(bool isProd) {
|
||||
box.put(AppKeys.isProd, isProd);
|
||||
}
|
||||
|
||||
bool getIsProd() {
|
||||
return box.get(AppKeys.isProd) ?? true;
|
||||
}
|
||||
|
||||
void setThemeMode(String mode) {
|
||||
box.put(AppKeys.themeMode, mode);
|
||||
}
|
||||
|
||||
String getThemeMode() {
|
||||
return box.get(AppKeys.themeMode) ?? ThemeMode.light.name;
|
||||
}
|
||||
|
||||
void setUCode(String mode) {
|
||||
box.put(AppKeys.ucode, mode);
|
||||
}
|
||||
|
||||
String getUCode() {
|
||||
return box.get(AppKeys.ucode) ?? "";
|
||||
}
|
||||
}
|
||||
24
lib/core/models/name.dart
Normal file
24
lib/core/models/name.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class Name extends Equatable {
|
||||
const Name({this.uz, this.ru, this.zh});
|
||||
|
||||
factory Name.fromJson(dynamic json) {
|
||||
return Name(uz: json['uz'], ru: json['ru'], zh: json['zh']);
|
||||
}
|
||||
|
||||
final String? uz;
|
||||
final String? ru;
|
||||
final String? zh;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['uz'] = uz;
|
||||
map['ru'] = ru;
|
||||
map['zh'] = zh;
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [uz, ru, zh];
|
||||
}
|
||||
14
lib/core/platform/network_info.dart
Normal file
14
lib/core/platform/network_info.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart';
|
||||
|
||||
abstract class NetworkInfo {
|
||||
Future<bool> get isConnected;
|
||||
}
|
||||
|
||||
class NetworkInfoImpl implements NetworkInfo{
|
||||
final InternetConnection internetConnectionCheckerPlus;
|
||||
|
||||
NetworkInfoImpl(this.internetConnectionCheckerPlus);
|
||||
|
||||
@override
|
||||
Future<bool> get isConnected => internetConnectionCheckerPlus.hasInternetAccess;
|
||||
}
|
||||
121
lib/core/theme/app_text_styles.dart
Normal file
121
lib/core/theme/app_text_styles.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'colors/app_colors.dart';
|
||||
|
||||
class AppTextStyles {
|
||||
AppTextStyles._();
|
||||
|
||||
static const profileCategory = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
static const titleBig = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
static const authTitle = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
static const timer = TextStyle(
|
||||
color: ThemeColors.timerRed,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
static const categoryName = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1,
|
||||
letterSpacing: 0,
|
||||
);
|
||||
static const bigTitle = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
static const orderTimer = TextStyle(
|
||||
color: ThemeColors.timerRed,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
static const timerStyle = TextStyle(
|
||||
color: ThemeColors.timerRed,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
static const saleRed = TextStyle(
|
||||
color: ThemeColors.timerRed,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
static const timerDesc = TextStyle(
|
||||
color: ThemeColors.timerRed,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
static const statusText = TextStyle(
|
||||
color: LightThemeColors.buttonColorText,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
static const language = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
static const languageBig = TextStyle(
|
||||
color: LightThemeColors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: 0,
|
||||
);
|
||||
|
||||
static const authDesc = TextStyle(
|
||||
color: LightThemeColors.secondaryText,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
static const timerBlue = TextStyle(
|
||||
color: LightThemeColors.primaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
static const orderListTitle = TextStyle(
|
||||
color: LightThemeColors.black40Transparent,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1,
|
||||
);
|
||||
static const status = TextStyle(
|
||||
color: ThemeColors.green173text,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1,
|
||||
letterSpacing: 0,
|
||||
);
|
||||
static const orderTitle = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
static const statusNumber = TextStyle(
|
||||
color: LightThemeColors.statusNumber,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
static const statusDesc = TextStyle(
|
||||
color: LightThemeColors.textColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
static const secondaryText14 = TextStyle(
|
||||
color: LightThemeColors.lightSecondary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
}
|
||||
14
lib/core/theme/app_theme.dart
Normal file
14
lib/core/theme/app_theme.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:cargocalculaterapp/core/theme/theme_data.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum LightThemes { violetTheme }
|
||||
|
||||
enum DarkThemes { violetTheme }
|
||||
|
||||
Map<LightThemes, ThemeData> lightThemes = {
|
||||
LightThemes.violetTheme: lightTheme,
|
||||
};
|
||||
|
||||
Map<DarkThemes, ThemeData> darkThemes = {
|
||||
DarkThemes.violetTheme: darkTheme,
|
||||
};
|
||||
273
lib/core/theme/colors/app_colors.dart
Normal file
273
lib/core/theme/colors/app_colors.dart
Normal file
@@ -0,0 +1,273 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ThemeColors {
|
||||
static const timerRed = Color(0xFFCF0000);
|
||||
static const successInputSMS = Color(0xFF36B82C);
|
||||
static const warningSurface = Color(0xFFFFEFE4);
|
||||
static const green173Light = Color(0xFFC7FCF6);
|
||||
static const green191Light = Color(0xFFE9FBFF);
|
||||
static const warning = Color(0xFFEE5800);
|
||||
static const green173text = Color(0xFF02685E);
|
||||
static const green191text = Color(0xFF00668F);
|
||||
}
|
||||
|
||||
class LightThemeColors extends ThemeColors {
|
||||
static const dividerColor = Color(0x0dFFFFFF);
|
||||
static const accentColor = Color(0xFF4275ba); // Neon Yellow
|
||||
static const darkBackground = Color(0xFF171717); // Dark Grey
|
||||
static const grey = Color(0xFF242424); // Grey
|
||||
static const midGrey = Color(0xFF474747); // Mid Grey
|
||||
static const lightGrey = Color(0xFF747474); // Light Grey
|
||||
static const white = Color(0xFFffffff);
|
||||
static const white60Transparent = Color(0x99FFFFFF);
|
||||
static const white30Transparent = Color(0x4dFFFFFF);
|
||||
static const white10Transparent = Color(0x1aFFFFFF);
|
||||
static const white20Transparent = Color(0x33FFFFFF);
|
||||
static const white40Transparent = Color(0x66FFFFFF);
|
||||
static const errorColor = Color(0xFFCF0000);
|
||||
|
||||
///shimmer
|
||||
static const backgroundGray = Color(0x80FFFFFF);
|
||||
static const shimmerHighlight = Color(0xffffffff);
|
||||
static const statusNumber = Color(0xff495057);
|
||||
|
||||
///new colors
|
||||
static const primaryColor = Color(0xff0074B0);
|
||||
static const textColor = Color(0xFF0C0C0C);
|
||||
static const secondaryText = Color(0xFF85938E);
|
||||
static const scaffoldBackgroundColor = Color(0xFFffffff);
|
||||
static const borderColor = Color(0xFF707479);
|
||||
static const edittextBackground = Color(0xFFF8FBFC);
|
||||
static const buttonColorText = Color(0xffffffff);
|
||||
static const disabledColor = Color(0x331783B2);
|
||||
static const iconBackground = Color(0xffE3F3F6);
|
||||
static const lightSecondary = Color(0xff858D93);
|
||||
static const grayBackground = Color(0xffF8FBFC);
|
||||
static const statusBackground = Color(0xffffffff);
|
||||
static const lightBorder = Color(0xffE3E3E3);
|
||||
static const black40Transparent = Color(0x66000000);
|
||||
static const statusIconBackground = Color(0xffF1F3F5);
|
||||
}
|
||||
|
||||
class DarkThemeColors {
|
||||
static const dividerColor = Color(0x0dFFFFFF);
|
||||
static const accentColor = Color(0xFF4275ba); // Neon Yellow
|
||||
static const darkBackground = Color(0xFF171717); // Dark Grey
|
||||
static const grey = Color(0xFF242424); // Grey
|
||||
static const midGrey = Color(0xFF474747); // Mid Grey
|
||||
static const lightGrey = Color(0xFF747474); // Light Grey
|
||||
static const white = Colors.white;
|
||||
static const white60Transparent = Color(0x99FFFFFF);
|
||||
static const white30Transparent = Color(0x4dFFFFFF);
|
||||
static const white10Transparent = Color(0x1aFFFFFF);
|
||||
static const errorColor = Color(0xFFCF0000);
|
||||
|
||||
///new colors
|
||||
static const primaryColor = Color(0xff1783B2);
|
||||
static const textColor = Color(0xFFFFFFFF);
|
||||
static const secondaryText = Color(0xFFC7C7C7);
|
||||
static const scaffoldBackgroundColor = Color(0xFF171717);
|
||||
static const borderColor = Color(0xFF707479);
|
||||
static const edittextBackground = Color(0xFF000000);
|
||||
static const buttonColorText = Color(0xffffffff);
|
||||
static const disabledColor = Color(0x331783B2);
|
||||
static const iconBackground = Color(0xff1A272D);
|
||||
static const lightSecondary = Color(0xffc7c7c7);
|
||||
static const grayBackground = Color(0xff000000);
|
||||
static const statusBackground = Color(0xff1A272D);
|
||||
static const lightBorder = Color(0xff252525);
|
||||
static const statusIconBackground = Color(0xff000000);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class AppThemeColors extends ThemeExtension<AppThemeColors> {
|
||||
final Color scaffoldBackgroundColor;
|
||||
final Color textColor;
|
||||
final Color dividerColor;
|
||||
final Color accentColor;
|
||||
final Color darkBackground;
|
||||
final Color grey;
|
||||
final Color midGrey;
|
||||
final Color lightGrey;
|
||||
final Color white;
|
||||
final Color white60Transparent;
|
||||
final Color white30Transparent;
|
||||
final Color white10Transparent;
|
||||
final Color buttonColorText;
|
||||
|
||||
///newcolors
|
||||
final Color primaryColor;
|
||||
final Color secondaryText;
|
||||
final Color borderColor;
|
||||
final Color edittextBackground;
|
||||
final Color iconBackground;
|
||||
final Color lightSecondary;
|
||||
final Color grayBackground;
|
||||
final Color statusBackground;
|
||||
final Color lightBorder;
|
||||
final Color statusIconBackground;
|
||||
|
||||
const AppThemeColors({
|
||||
required this.scaffoldBackgroundColor,
|
||||
required this.textColor,
|
||||
required this.dividerColor,
|
||||
required this.accentColor,
|
||||
required this.darkBackground,
|
||||
required this.grey,
|
||||
required this.midGrey,
|
||||
required this.lightGrey,
|
||||
required this.white,
|
||||
required this.white60Transparent,
|
||||
required this.white30Transparent,
|
||||
required this.white10Transparent,
|
||||
required this.buttonColorText,
|
||||
required this.primaryColor,
|
||||
required this.secondaryText,
|
||||
required this.borderColor,
|
||||
required this.edittextBackground,
|
||||
required this.iconBackground,
|
||||
required this.lightSecondary,
|
||||
required this.grayBackground,
|
||||
required this.statusBackground,
|
||||
required this.lightBorder,
|
||||
required this.statusIconBackground,
|
||||
});
|
||||
|
||||
@override
|
||||
ThemeExtension<AppThemeColors> copyWith({
|
||||
Color? scaffoldBackgroundColor,
|
||||
Color? textColor,
|
||||
Color? dividerColor,
|
||||
Color? accentColor,
|
||||
Color? darkBackground,
|
||||
Color? grey,
|
||||
Color? midGrey,
|
||||
Color? lightGrey,
|
||||
Color? white,
|
||||
Color? white60Transparent,
|
||||
Color? white30Transparent,
|
||||
Color? white10Transparent,
|
||||
Color? buttonColorText,
|
||||
Color? primaryColor,
|
||||
Color? secondaryText,
|
||||
Color? borderColor,
|
||||
Color? edittextBackground,
|
||||
Color? iconBackground,
|
||||
Color? lightSecondary,
|
||||
Color? grayBackground,
|
||||
Color? statusBackground,
|
||||
Color? lightBorder,
|
||||
Color? statusIconBackground,
|
||||
}) {
|
||||
return AppThemeColors(
|
||||
scaffoldBackgroundColor:
|
||||
scaffoldBackgroundColor ?? this.scaffoldBackgroundColor,
|
||||
textColor: textColor ?? this.textColor,
|
||||
dividerColor: dividerColor ?? this.dividerColor,
|
||||
accentColor: accentColor ?? this.accentColor,
|
||||
darkBackground: darkBackground ?? this.darkBackground,
|
||||
grey: grey ?? this.grey,
|
||||
midGrey: midGrey ?? this.midGrey,
|
||||
lightGrey: lightGrey ?? this.lightGrey,
|
||||
white: white ?? this.white,
|
||||
white60Transparent: white60Transparent ?? this.white60Transparent,
|
||||
white30Transparent: white30Transparent ?? this.white30Transparent,
|
||||
white10Transparent: white10Transparent ?? this.white10Transparent,
|
||||
buttonColorText: buttonColorText ?? this.buttonColorText,
|
||||
primaryColor: primaryColor ?? this.primaryColor,
|
||||
secondaryText: secondaryText ?? this.secondaryText,
|
||||
borderColor: borderColor ?? this.borderColor,
|
||||
edittextBackground: edittextBackground ?? this.edittextBackground,
|
||||
iconBackground: iconBackground ?? this.iconBackground,
|
||||
lightSecondary: lightSecondary ?? this.lightSecondary,
|
||||
grayBackground: grayBackground ?? this.grayBackground,
|
||||
statusBackground: statusBackground ?? this.statusBackground,
|
||||
lightBorder: lightBorder ?? this.lightBorder,
|
||||
statusIconBackground: statusIconBackground ?? this.statusIconBackground,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AppThemeColors lerp(ThemeExtension<AppThemeColors>? other, double t) {
|
||||
if (other is! AppThemeColors) return this;
|
||||
|
||||
return AppThemeColors(
|
||||
scaffoldBackgroundColor: Color.lerp(scaffoldBackgroundColor, other.scaffoldBackgroundColor, t)!,
|
||||
textColor: Color.lerp(textColor, other.textColor, t)!,
|
||||
dividerColor: Color.lerp(dividerColor, other.dividerColor, t)!,
|
||||
accentColor: Color.lerp(accentColor, other.accentColor, t)!,
|
||||
darkBackground: Color.lerp(darkBackground, other.darkBackground, t)!,
|
||||
grey: Color.lerp(grey, other.grey, t)!,
|
||||
midGrey: Color.lerp(midGrey, other.midGrey, t)!,
|
||||
lightGrey: Color.lerp(lightGrey, other.lightGrey, t)!,
|
||||
white: Color.lerp(white, other.white, t)!,
|
||||
white60Transparent: Color.lerp(white60Transparent, other.white60Transparent, t)!,
|
||||
white30Transparent: Color.lerp(white30Transparent, other.white30Transparent, t)!,
|
||||
white10Transparent: Color.lerp(white10Transparent, other.white10Transparent, t)!,
|
||||
buttonColorText: Color.lerp(buttonColorText, other.buttonColorText, t)!,
|
||||
primaryColor: Color.lerp(primaryColor, other.primaryColor, t)!,
|
||||
secondaryText: Color.lerp(secondaryText, other.secondaryText, t)!,
|
||||
borderColor: Color.lerp(borderColor, other.borderColor, t)!,
|
||||
edittextBackground: Color.lerp(edittextBackground, other.edittextBackground, t)!,
|
||||
iconBackground: Color.lerp(iconBackground, other.iconBackground, t)!,
|
||||
lightSecondary: Color.lerp(lightSecondary, other.lightSecondary, t)!,
|
||||
grayBackground: Color.lerp(grayBackground, other.grayBackground, t)!,
|
||||
statusBackground: Color.lerp(statusBackground, other.statusBackground, t)!,
|
||||
lightBorder: Color.lerp(lightBorder, other.lightBorder, t)!,
|
||||
statusIconBackground: Color.lerp(statusIconBackground, other.statusIconBackground, t)!,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static get light => const AppThemeColors(
|
||||
scaffoldBackgroundColor: LightThemeColors.scaffoldBackgroundColor,
|
||||
textColor: LightThemeColors.textColor,
|
||||
dividerColor: LightThemeColors.dividerColor,
|
||||
accentColor: LightThemeColors.accentColor,
|
||||
darkBackground: LightThemeColors.darkBackground,
|
||||
grey: LightThemeColors.grey,
|
||||
midGrey: LightThemeColors.midGrey,
|
||||
lightGrey: LightThemeColors.lightGrey,
|
||||
white: LightThemeColors.white,
|
||||
white60Transparent: LightThemeColors.white60Transparent,
|
||||
white30Transparent: LightThemeColors.white30Transparent,
|
||||
white10Transparent: LightThemeColors.white10Transparent,
|
||||
buttonColorText: LightThemeColors.buttonColorText,
|
||||
primaryColor: LightThemeColors.primaryColor,
|
||||
secondaryText: LightThemeColors.secondaryText,
|
||||
borderColor: LightThemeColors.borderColor,
|
||||
edittextBackground: LightThemeColors.edittextBackground,
|
||||
iconBackground: LightThemeColors.iconBackground,
|
||||
lightSecondary: LightThemeColors.lightSecondary,
|
||||
grayBackground: LightThemeColors.grayBackground,
|
||||
statusBackground: LightThemeColors.statusBackground,
|
||||
lightBorder: LightThemeColors.lightBorder,
|
||||
statusIconBackground: LightThemeColors.statusIconBackground,
|
||||
);
|
||||
|
||||
static get dark => const AppThemeColors(
|
||||
scaffoldBackgroundColor: DarkThemeColors.scaffoldBackgroundColor,
|
||||
textColor: DarkThemeColors.textColor,
|
||||
dividerColor: DarkThemeColors.dividerColor,
|
||||
accentColor: DarkThemeColors.accentColor,
|
||||
darkBackground: DarkThemeColors.darkBackground,
|
||||
grey: DarkThemeColors.grey,
|
||||
midGrey: DarkThemeColors.midGrey,
|
||||
lightGrey: DarkThemeColors.lightGrey,
|
||||
white: DarkThemeColors.white,
|
||||
white60Transparent: DarkThemeColors.white60Transparent,
|
||||
white30Transparent: DarkThemeColors.white30Transparent,
|
||||
white10Transparent: DarkThemeColors.white10Transparent,
|
||||
buttonColorText: DarkThemeColors.buttonColorText,
|
||||
primaryColor: DarkThemeColors.primaryColor,
|
||||
secondaryText: DarkThemeColors.secondaryText,
|
||||
borderColor: DarkThemeColors.borderColor,
|
||||
edittextBackground: DarkThemeColors.edittextBackground,
|
||||
iconBackground: DarkThemeColors.iconBackground,
|
||||
lightSecondary: DarkThemeColors.lightSecondary,
|
||||
grayBackground: DarkThemeColors.grayBackground,
|
||||
statusBackground: DarkThemeColors.statusBackground,
|
||||
lightBorder: DarkThemeColors.lightBorder,
|
||||
statusIconBackground: DarkThemeColors.statusIconBackground,
|
||||
);
|
||||
}
|
||||
434
lib/core/theme/theme_data.dart
Normal file
434
lib/core/theme/theme_data.dart
Normal file
@@ -0,0 +1,434 @@
|
||||
import 'package:cargocalculaterapp/core/theme/theme_text_style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'colors/app_colors.dart';
|
||||
|
||||
final appTheme = ThemeData(
|
||||
fontFamily: "Inter",
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
AppThemeColors.light,
|
||||
AppThemeColors.dark,
|
||||
ThemeTextStyles.light,
|
||||
ThemeTextStyles.dark,
|
||||
],
|
||||
useMaterial3: true,
|
||||
applyElevationOverlayColor: true,
|
||||
// disabledColor: ThemeColors.disabledColor,
|
||||
// splashColor: ThemeColors.primary1Transparent,
|
||||
// focusColor: ThemeColors.primaryColor,
|
||||
// colorSchemeSeed: ThemeColors.primaryColor,
|
||||
visualDensity: VisualDensity.standard,
|
||||
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||
pageTransitionsTheme: const PageTransitionsTheme(
|
||||
builders: {
|
||||
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||
},
|
||||
),
|
||||
// textButtonTheme: TextButtonThemeData(
|
||||
// style: ButtonStyle(
|
||||
// padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||
// EdgeInsets.zero,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
dividerTheme: const DividerThemeData(thickness: 1),
|
||||
);
|
||||
|
||||
final lightTheme = appTheme.copyWith(
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
AppThemeColors.light,
|
||||
ThemeTextStyles.light,
|
||||
],
|
||||
scaffoldBackgroundColor: LightThemeColors.scaffoldBackgroundColor,
|
||||
brightness: Brightness.light,
|
||||
dividerTheme: appTheme.dividerTheme.copyWith(
|
||||
color: LightThemeColors.dividerColor,
|
||||
),
|
||||
colorScheme: const ColorScheme.light(
|
||||
secondary: LightThemeColors.white,
|
||||
primary: LightThemeColors.accentColor,
|
||||
),
|
||||
// listTileTheme: const ListTileThemeData(
|
||||
// minVerticalPadding: 14,
|
||||
// minLeadingWidth: 16,
|
||||
// horizontalTitleGap: 12,
|
||||
// tileColor: LightThemeColors.textFieldBackGround,
|
||||
// selectedColor: LightThemeColors.backgroundColor,
|
||||
// selectedTileColor: LightThemeColors.backgroundColor,
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: AppUtils.kBorderRadius4,
|
||||
// ),
|
||||
// ),
|
||||
tabBarTheme: TabBarThemeData(
|
||||
labelColor: LightThemeColors.accentColor,
|
||||
unselectedLabelColor: LightThemeColors.grey,
|
||||
labelPadding: EdgeInsets.zero,
|
||||
labelStyle: const TextStyle(fontSize: 11, fontWeight: FontWeight.w400),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: LightThemeColors.accentColor, width: 2.0),
|
||||
),
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
return LightThemeColors.lightGrey;
|
||||
}),
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
scrolledUnderElevation: 0,
|
||||
foregroundColor: LightThemeColors.scaffoldBackgroundColor,
|
||||
backgroundColor: LightThemeColors.white,
|
||||
surfaceTintColor: LightThemeColors.scaffoldBackgroundColor,
|
||||
shadowColor: Colors.black,
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: LightThemeColors.textColor,
|
||||
),
|
||||
toolbarHeight: 56,
|
||||
iconTheme: IconThemeData(color: LightThemeColors.primaryColor),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
contentPadding: EdgeInsets.all(16),
|
||||
alignLabelWithHint: true,
|
||||
labelStyle: TextStyle(
|
||||
color: LightThemeColors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: LightThemeColors.secondaryText,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: LightThemeColors.errorColor),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: LightThemeColors.borderColor),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: LightThemeColors.borderColor),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: LightThemeColors.primaryColor),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: LightThemeColors.white10Transparent),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: LightThemeColors.errorColor),
|
||||
),
|
||||
filled: true,
|
||||
isDense: true,
|
||||
fillColor: LightThemeColors.edittextBackground,
|
||||
floatingLabelAlignment: FloatingLabelAlignment.start,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>((
|
||||
Set<WidgetState> states,
|
||||
) {
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return LightThemeColors.errorColor;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
elevation: WidgetStateProperty.all<double>(0),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(
|
||||
const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: LightThemeColors.textColor,
|
||||
),
|
||||
),
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return LightThemeColors.white;
|
||||
} else {
|
||||
return LightThemeColors.white;
|
||||
}
|
||||
}),
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return LightThemeColors.disabledColor;
|
||||
} else {
|
||||
return LightThemeColors.primaryColor;
|
||||
}
|
||||
}),
|
||||
maximumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.infinity, 56),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.infinity, 56),
|
||||
),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
backgroundColor: LightThemeColors.scaffoldBackgroundColor,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
showSelectedLabels: true,
|
||||
selectedLabelStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
unselectedLabelStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
unselectedItemColor: LightThemeColors.lightSecondary,
|
||||
selectedItemColor: LightThemeColors.primaryColor,
|
||||
selectedIconTheme: IconThemeData(size: 25),
|
||||
unselectedIconTheme: IconThemeData(size: 25),
|
||||
elevation: 2,
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
elevation: WidgetStateProperty.all<double>(2),
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
return LightThemeColors.scaffoldBackgroundColor;
|
||||
}),
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return Colors.transparent;
|
||||
} else {
|
||||
return Colors.transparent;
|
||||
}
|
||||
}),
|
||||
maximumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.infinity, double.infinity),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.minPositive, double.minPositive),
|
||||
),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
),
|
||||
),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(
|
||||
const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: LightThemeColors.accentColor,
|
||||
),
|
||||
),
|
||||
// overlayColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
// return LightThemeColors.lightGrey;
|
||||
// }),
|
||||
),
|
||||
),
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: Colors.transparent,
|
||||
// surfaceTintColor: Colors.transparent,
|
||||
modalBackgroundColor: Colors.transparent,
|
||||
modalElevation: 0,
|
||||
elevation: 0,
|
||||
),
|
||||
);
|
||||
|
||||
final darkTheme = appTheme.copyWith(
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
AppThemeColors.dark,
|
||||
ThemeTextStyles.dark,
|
||||
],
|
||||
scaffoldBackgroundColor: DarkThemeColors.scaffoldBackgroundColor,
|
||||
brightness: Brightness.dark,
|
||||
dividerTheme: appTheme.dividerTheme.copyWith(
|
||||
color: DarkThemeColors.dividerColor,
|
||||
),
|
||||
colorScheme: const ColorScheme.dark(secondary: Colors.white),
|
||||
tabBarTheme: TabBarThemeData(
|
||||
labelColor: DarkThemeColors.accentColor,
|
||||
unselectedLabelColor: DarkThemeColors.grey,
|
||||
labelPadding: EdgeInsets.zero,
|
||||
labelStyle: const TextStyle(fontSize: 11, fontWeight: FontWeight.w400),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: DarkThemeColors.accentColor, width: 2.0),
|
||||
),
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
return DarkThemeColors.lightGrey;
|
||||
}),
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
scrolledUnderElevation: 0,
|
||||
foregroundColor: DarkThemeColors.scaffoldBackgroundColor,
|
||||
backgroundColor: DarkThemeColors.scaffoldBackgroundColor,
|
||||
surfaceTintColor: DarkThemeColors.scaffoldBackgroundColor,
|
||||
shadowColor: Colors.black,
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: DarkThemeColors.textColor,
|
||||
),
|
||||
toolbarHeight: 56,
|
||||
iconTheme: IconThemeData(color: DarkThemeColors.primaryColor),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
alignLabelWithHint: true,
|
||||
contentPadding: EdgeInsets.all(16),
|
||||
labelStyle: TextStyle(
|
||||
color: DarkThemeColors.textColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: DarkThemeColors.secondaryText,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: DarkThemeColors.errorColor),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: DarkThemeColors.borderColor),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: DarkThemeColors.borderColor),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: DarkThemeColors.primaryColor),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: DarkThemeColors.white10Transparent),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
borderSide: BorderSide(color: DarkThemeColors.errorColor),
|
||||
),
|
||||
filled: true,
|
||||
isDense: true,
|
||||
fillColor: DarkThemeColors.edittextBackground,
|
||||
floatingLabelAlignment: FloatingLabelAlignment.start,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>((
|
||||
Set<WidgetState> states,
|
||||
) {
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return DarkThemeColors.errorColor;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
elevation: WidgetStateProperty.all<double>(0),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(
|
||||
const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: DarkThemeColors.buttonColorText,
|
||||
),
|
||||
),
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return DarkThemeColors.white60Transparent;
|
||||
} else {
|
||||
return DarkThemeColors.buttonColorText;
|
||||
}
|
||||
}),
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return DarkThemeColors.disabledColor;
|
||||
} else {
|
||||
return DarkThemeColors.primaryColor;
|
||||
}
|
||||
}),
|
||||
maximumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.infinity, 56),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.infinity, 56),
|
||||
),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
backgroundColor: DarkThemeColors.scaffoldBackgroundColor,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
showSelectedLabels: true,
|
||||
selectedLabelStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
unselectedLabelStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
unselectedItemColor: DarkThemeColors.lightSecondary,
|
||||
selectedItemColor: DarkThemeColors.primaryColor,
|
||||
selectedIconTheme: IconThemeData(size: 25),
|
||||
unselectedIconTheme: IconThemeData(size: 25),
|
||||
elevation: 2,
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
elevation: WidgetStateProperty.all<double>(2),
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
return DarkThemeColors.scaffoldBackgroundColor;
|
||||
}),
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return Colors.transparent;
|
||||
} else {
|
||||
return Colors.transparent;
|
||||
}
|
||||
}),
|
||||
maximumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.infinity, double.infinity),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all<Size>(
|
||||
const Size(double.minPositive, double.minPositive),
|
||||
),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
),
|
||||
),
|
||||
textStyle: WidgetStateProperty.all<TextStyle>(
|
||||
const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: DarkThemeColors.accentColor,
|
||||
),
|
||||
),
|
||||
// overlayColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
// return DarkThemeColors.lightGrey;
|
||||
// }),
|
||||
),
|
||||
),
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: Colors.transparent,
|
||||
// surfaceTintColor: Colors.transparent,
|
||||
modalBackgroundColor: Colors.transparent,
|
||||
modalElevation: 0,
|
||||
elevation: 0,
|
||||
),
|
||||
);
|
||||
118
lib/core/theme/theme_text_style.dart
Normal file
118
lib/core/theme/theme_text_style.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
import 'package:cargocalculaterapp/core/theme/colors/app_colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'app_text_styles.dart';
|
||||
|
||||
@immutable
|
||||
class ThemeTextStyles extends ThemeExtension<ThemeTextStyles> {
|
||||
final TextStyle profileCategory;
|
||||
final TextStyle titleBig;
|
||||
final TextStyle authTitle;
|
||||
final TextStyle categoryName;
|
||||
final TextStyle bigTitle;
|
||||
final TextStyle statusText;
|
||||
final TextStyle authDesc;
|
||||
final TextStyle orderListTitle;
|
||||
final TextStyle orderTitle;
|
||||
final TextStyle statusNumber;
|
||||
final TextStyle statusDesc;
|
||||
final TextStyle secondaryText14;
|
||||
|
||||
@override
|
||||
ThemeExtension<ThemeTextStyles> copyWith({
|
||||
TextStyle? profileCategory,
|
||||
TextStyle? titleBig,
|
||||
TextStyle? authTitle,
|
||||
TextStyle? categoryName,
|
||||
TextStyle? bigTitle,
|
||||
TextStyle? statusText,
|
||||
TextStyle? authDesc,
|
||||
TextStyle? orderListTitle,
|
||||
TextStyle? orderTitle,
|
||||
TextStyle? statusNumber,
|
||||
TextStyle? statusDesc,
|
||||
TextStyle? secondaryText14,
|
||||
}) {
|
||||
return ThemeTextStyles(
|
||||
profileCategory: profileCategory ?? this.profileCategory,
|
||||
titleBig: titleBig ?? this.titleBig,
|
||||
authTitle: authTitle ?? this.authTitle,
|
||||
categoryName: categoryName ?? this.categoryName,
|
||||
bigTitle: bigTitle ?? this.bigTitle,
|
||||
statusText: statusText ?? this.statusText,
|
||||
authDesc: authDesc ?? this.authDesc,
|
||||
orderListTitle: orderListTitle ?? this.orderListTitle,
|
||||
orderTitle: orderTitle ?? this.orderTitle,
|
||||
statusNumber: statusNumber ?? this.statusNumber,
|
||||
statusDesc: statusDesc ?? this.statusDesc,
|
||||
secondaryText14: secondaryText14 ?? this.secondaryText14,
|
||||
);
|
||||
}
|
||||
|
||||
const ThemeTextStyles({
|
||||
required this.profileCategory,
|
||||
required this.titleBig,
|
||||
required this.authTitle,
|
||||
required this.categoryName,
|
||||
required this.bigTitle,
|
||||
required this.statusText,
|
||||
required this.authDesc,
|
||||
required this.orderListTitle,
|
||||
required this.orderTitle,
|
||||
required this.statusNumber,
|
||||
required this.statusDesc,
|
||||
required this.secondaryText14,
|
||||
});
|
||||
|
||||
static get light => const ThemeTextStyles(
|
||||
profileCategory: AppTextStyles.profileCategory,
|
||||
titleBig: AppTextStyles.titleBig,
|
||||
authTitle: AppTextStyles.authTitle,
|
||||
categoryName: AppTextStyles.categoryName,
|
||||
bigTitle: AppTextStyles.bigTitle,
|
||||
statusText: AppTextStyles.statusText,
|
||||
authDesc: AppTextStyles.authDesc,
|
||||
orderListTitle: AppTextStyles.orderListTitle,
|
||||
orderTitle: AppTextStyles.orderTitle,
|
||||
statusNumber: AppTextStyles.statusNumber,
|
||||
statusDesc: AppTextStyles.statusDesc,
|
||||
secondaryText14: AppTextStyles.secondaryText14,
|
||||
);
|
||||
|
||||
static get dark => ThemeTextStyles(
|
||||
profileCategory: AppTextStyles.profileCategory.copyWith(
|
||||
color: DarkThemeColors.textColor,
|
||||
),
|
||||
titleBig: AppTextStyles.titleBig.copyWith(color: DarkThemeColors.textColor),
|
||||
authTitle: AppTextStyles.authTitle.copyWith(color: DarkThemeColors.textColor),
|
||||
categoryName: AppTextStyles.categoryName.copyWith(color: DarkThemeColors.textColor),
|
||||
bigTitle: AppTextStyles.bigTitle.copyWith(color: DarkThemeColors.textColor),
|
||||
statusText: AppTextStyles.statusText.copyWith(color: DarkThemeColors.buttonColorText),
|
||||
authDesc: AppTextStyles.authDesc.copyWith(color: DarkThemeColors.secondaryText),
|
||||
orderListTitle: AppTextStyles.orderListTitle.copyWith(color: DarkThemeColors.secondaryText),
|
||||
orderTitle: AppTextStyles.orderTitle.copyWith(color: DarkThemeColors.textColor),
|
||||
statusNumber: AppTextStyles.statusNumber.copyWith(color: DarkThemeColors.secondaryText),
|
||||
statusDesc: AppTextStyles.statusDesc.copyWith(color: DarkThemeColors.textColor),
|
||||
secondaryText14: AppTextStyles.secondaryText14.copyWith(color: DarkThemeColors.lightSecondary),
|
||||
);
|
||||
|
||||
@override
|
||||
ThemeTextStyles lerp(ThemeExtension<ThemeTextStyles>? other, double t) {
|
||||
if (other is! ThemeTextStyles) return this;
|
||||
|
||||
return ThemeTextStyles(
|
||||
profileCategory: TextStyle.lerp(profileCategory, other.profileCategory, t)!,
|
||||
titleBig: TextStyle.lerp(titleBig, other.titleBig, t)!,
|
||||
authTitle: TextStyle.lerp(authTitle, other.authTitle, t)!,
|
||||
categoryName: TextStyle.lerp(categoryName, other.categoryName, t)!,
|
||||
bigTitle: TextStyle.lerp(bigTitle, other.bigTitle, t)!,
|
||||
statusText: TextStyle.lerp(statusText, other.statusText, t)!,
|
||||
authDesc: TextStyle.lerp(authDesc, other.authDesc, t)!,
|
||||
orderListTitle: TextStyle.lerp(orderListTitle, other.orderListTitle, t)!,
|
||||
orderTitle: TextStyle.lerp(orderTitle, other.orderTitle, t)!,
|
||||
statusNumber: TextStyle.lerp(statusNumber, other.statusNumber, t)!,
|
||||
statusDesc: TextStyle.lerp(statusDesc, other.statusDesc, t)!,
|
||||
secondaryText14: TextStyle.lerp(secondaryText14, other.secondaryText14, t)!,
|
||||
);
|
||||
}
|
||||
}
|
||||
14
lib/core/usecase/usecase.dart
Normal file
14
lib/core/usecase/usecase.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../error/failure.dart';
|
||||
|
||||
abstract class UseCase<Type, Params> {
|
||||
Future<Either<Failure, Type>> call(Params params);
|
||||
}
|
||||
|
||||
class NoParams extends Equatable {
|
||||
const NoParams();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
484
lib/core/utils/app_utils.dart
Normal file
484
lib/core/utils/app_utils.dart
Normal file
@@ -0,0 +1,484 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/colors/app_colors.dart';
|
||||
|
||||
class AppUtils {
|
||||
AppUtils._();
|
||||
|
||||
static const kDividerR = Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
color: LightThemeColors.dividerColor,
|
||||
);
|
||||
static const kGap12 = SliverToBoxAdapter(child: kBoxHeight12);
|
||||
static const kGap16 = SliverToBoxAdapter(child: kBoxHeight16);
|
||||
static const kGap8 = SliverToBoxAdapter(child: kBoxHeight8);
|
||||
static const kGap4 = SliverToBoxAdapter(child: kBoxHeight4);
|
||||
static const kGap10 = SliverToBoxAdapter(child: kBoxHeight10);
|
||||
static const kGap22 = SliverToBoxAdapter(child: kBoxHeight22);
|
||||
static const kGap24 = SliverToBoxAdapter(child: kBoxHeight24);
|
||||
static const kGapBox = SliverToBoxAdapter(child: SizedBox());
|
||||
static const kBoxWith4 = SizedBox(width: 4);
|
||||
static const kBoxWith8 = SizedBox(width: 8);
|
||||
static const kBoxWith12 = SizedBox(width: 12);
|
||||
|
||||
/// divider
|
||||
static const kDivider = Divider(
|
||||
height: 2,
|
||||
thickness: 2,
|
||||
color: LightThemeColors.dividerColor,
|
||||
);
|
||||
static const kPad16Divider = Divider(
|
||||
height: 2,
|
||||
thickness: 2,
|
||||
indent: 16,
|
||||
color: LightThemeColors.dividerColor,
|
||||
);
|
||||
static const kGap16Divider = SliverToBoxAdapter(child: kPadHor16Divider);
|
||||
static const kPadHor16Divider = Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
indent: 16,
|
||||
endIndent: 16,
|
||||
);
|
||||
static const kPad60Divider = Padding(
|
||||
padding: EdgeInsets.only(left: 60),
|
||||
child: Divider(height: 1, thickness: 1),
|
||||
);
|
||||
|
||||
/// spacer
|
||||
static const kSpacer = Spacer();
|
||||
|
||||
/// Sizedbox
|
||||
static const kBox = SizedBox.shrink();
|
||||
static const kBoxHeight2 = SizedBox(height: 2);
|
||||
static const kBoxHeight3 = SizedBox(height: 3);
|
||||
static const kBoxHeight4 = SizedBox(height: 4);
|
||||
static const kBoxHeight6 = SizedBox(height: 6);
|
||||
static const kBoxHeight8 = SizedBox(height: 8);
|
||||
static const kBoxHeight10 = SizedBox(height: 10);
|
||||
static const kBoxHeight12 = SizedBox(height: 12);
|
||||
static const kBoxHeight14 = SizedBox(height: 14);
|
||||
static const kBoxHeight16 = SizedBox(height: 16);
|
||||
static const kBoxHeight18 = SizedBox(height: 18);
|
||||
static const kBoxHeight20 = SizedBox(height: 20);
|
||||
static const kBoxHeight22 = SizedBox(height: 22);
|
||||
static const kBoxHeight24 = SizedBox(height: 24);
|
||||
static const kBoxHeight26 = SizedBox(height: 26);
|
||||
static const kBoxHeight28 = SizedBox(height: 28);
|
||||
static const kBoxHeight40 = SizedBox(height: 40);
|
||||
static const kBoxHeight42 = SizedBox(height: 42);
|
||||
static const kBoxHeight38 = SizedBox(height: 48);
|
||||
static const kBoxHeight45 = SizedBox(height: 45);
|
||||
static const kBoxHeight48 = SizedBox(height: 48);
|
||||
static const kBoxHeight58 = SizedBox(height: 58);
|
||||
|
||||
static const kBoxHeight30 = SizedBox(height: 30);
|
||||
static const kBoxHeight32 = SizedBox(height: 32);
|
||||
static const kBoxHeight34 = SizedBox(height: 34);
|
||||
static const kBoxHeight36 = SizedBox(height: 36);
|
||||
static const kBoxHeight64 = SizedBox(height: 64);
|
||||
static const kBoxHeight84 = SizedBox(height: 84);
|
||||
static const kBoxHeight124 = SizedBox(height: 124);
|
||||
static const kBoxHeight204 = SizedBox(height: 204);
|
||||
static const kBoxWidth2 = SizedBox(width: 2);
|
||||
static const kBoxWidth8 = SizedBox(width: 8);
|
||||
static const kBoxWidth6 = SizedBox(width: 6);
|
||||
static const kBoxWidth4 = SizedBox(width: 4);
|
||||
static const kBoxWidth3 = SizedBox(width: 3);
|
||||
static const kBoxWidth10 = SizedBox(width: 10);
|
||||
static const kBoxWidth12 = SizedBox(width: 12);
|
||||
static const kBoxWidth14 = SizedBox(width: 14);
|
||||
static const kBoxWidth20 = SizedBox(width: 20);
|
||||
static const kBoxWidth16 = SizedBox(width: 16);
|
||||
static const kBoxWidth22 = SizedBox(width: 22);
|
||||
static const kBoxWidth24 = SizedBox(width: 24);
|
||||
static const kBoxWidth30 = SizedBox(width: 30);
|
||||
static const kBoxWidth40 = SizedBox(width: 40);
|
||||
static const kBoxWidth56 = SizedBox(width: 56);
|
||||
static const kBoxWidth95 = SizedBox(width: 95);
|
||||
|
||||
/// padding
|
||||
static const kPaddingAll4 = EdgeInsets.all(4);
|
||||
static const kPaddingAll3 = EdgeInsets.all(3);
|
||||
static const kPaddingAll2 = EdgeInsets.all(2);
|
||||
static const kPaddingAll1 = EdgeInsets.all(1);
|
||||
static const kPaddingHorizontal12Vertical8 = EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 8,
|
||||
);
|
||||
static const kPaddingHorizontal12Vertical10 = EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
);
|
||||
static const kPaddingVer10 = EdgeInsets.symmetric(vertical: 10);
|
||||
static const kPaddingAll6 = EdgeInsets.all(6);
|
||||
static const kPaddingHorizontal16Vertical8 = EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8,
|
||||
);
|
||||
static const kPaddingAll8 = EdgeInsets.all(8);
|
||||
static const kPaddingAll12 = EdgeInsets.all(12);
|
||||
static const kPaddingAll10 = EdgeInsets.all(10);
|
||||
static const kPaddingAll16 = EdgeInsets.all(16);
|
||||
static const kPaddingAll18 = EdgeInsets.all(18);
|
||||
static const kPaddingAll20 = EdgeInsets.all(20);
|
||||
static const kPaddingAllB16 = EdgeInsets.fromLTRB(16, 16, 16, 0);
|
||||
static const kPaddingAll24 = EdgeInsets.all(24);
|
||||
static const kPaddingHorizontal16 = EdgeInsets.symmetric(horizontal: 16);
|
||||
static const kPaddingHorizontal16T0B8 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 8,
|
||||
top: 0,
|
||||
);
|
||||
static const kPaddingHorizontal16T0B16 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
top: 0,
|
||||
);
|
||||
static const kPaddingHorizontal32T32B20 = EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 20,
|
||||
top: 32,
|
||||
);
|
||||
static const kPaddingLTRB8 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: 8,
|
||||
);
|
||||
static const kPaddingL8T12R16B12 = EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 16,
|
||||
top: 12,
|
||||
bottom: 8,
|
||||
);
|
||||
static const kPaddingLTR = EdgeInsets.only(left: 16, right: 16, top: 16);
|
||||
|
||||
static const kPaddingL37 = EdgeInsets.only(left: 37);
|
||||
static const kPaddingT4R8B4 = EdgeInsets.only(right: 8, bottom: 4, top: 4);
|
||||
static const kPaddingT24 = EdgeInsets.only(top: 24);
|
||||
|
||||
static const kPaddingR4 = EdgeInsets.only(right: 4);
|
||||
static const kPaddingR10 = EdgeInsets.only(right: 10);
|
||||
static const kPaddingR16 = EdgeInsets.only(right: 16);
|
||||
static const kPaddingLT8RB = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
bottom: 16,
|
||||
);
|
||||
static const kPaddingLT0RB = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 0,
|
||||
bottom: 16,
|
||||
);
|
||||
static const kPadding12LTRB0 = EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 12,
|
||||
bottom: 0,
|
||||
);
|
||||
static const kPaddingTop10 = EdgeInsets.only(top: 10);
|
||||
static const kPaddingTop15 = EdgeInsets.only(top: 15);
|
||||
static const kPaddingTop21 = EdgeInsets.only(top: 21);
|
||||
static const kPaddingTop24 = EdgeInsets.only(top: 24);
|
||||
static const kPaddingTop28 = EdgeInsets.only(top: 28);
|
||||
static const kPaddingTop20 = EdgeInsets.only(top: 40);
|
||||
|
||||
static const kPaddingL16R12 = EdgeInsets.only(left: 16, right: 12, bottom: 8);
|
||||
static const kPaddingL16R12TB8 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 12,
|
||||
bottom: 8,
|
||||
top: 8,
|
||||
);
|
||||
static const kPaddingL16R16T0B24 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 24,
|
||||
top: 0,
|
||||
);
|
||||
static const kPaddingL16R16T24B16 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 24,
|
||||
bottom: 16,
|
||||
);
|
||||
static const kPaddingL16R16T24B0 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 24,
|
||||
);
|
||||
static const kPaddingL16R16T16B24 = EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: 24,
|
||||
);
|
||||
static const kPaddingL8T8 = EdgeInsets.only(left: 8, top: 8);
|
||||
static const kPaddingL60 = EdgeInsets.only(left: 60);
|
||||
static const kPaddingT20B24R16 = EdgeInsets.only(
|
||||
top: 20,
|
||||
bottom: 24,
|
||||
right: 16,
|
||||
);
|
||||
static const kPaddingL16T20B24 = EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 20,
|
||||
bottom: 24,
|
||||
);
|
||||
static const kPaddingR20T16B16 = EdgeInsets.only(
|
||||
right: 20,
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
);
|
||||
|
||||
static const kPaddingHor32Ver20 = EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
vertical: 20,
|
||||
);
|
||||
static const kPaddingHor16 = EdgeInsets.symmetric(horizontal: 16);
|
||||
static const kPaddingHor4 = EdgeInsets.symmetric(horizontal: 4);
|
||||
static const kPaddingHor6 = EdgeInsets.symmetric(horizontal: 6);
|
||||
static const kPaddingVer12 = EdgeInsets.symmetric(vertical: 12);
|
||||
static const kPaddingVer16 = EdgeInsets.symmetric(vertical: 16);
|
||||
static const kPaddingVer24 = EdgeInsets.symmetric(vertical: 24);
|
||||
static const kPaddingVer44 = EdgeInsets.symmetric(vertical: 44);
|
||||
static const kPaddingHor18 = EdgeInsets.symmetric(horizontal: 18);
|
||||
static const kPaddingHor20 = EdgeInsets.symmetric(horizontal: 20);
|
||||
static const kPaddingHor24 = EdgeInsets.symmetric(horizontal: 24);
|
||||
static const kPaddingHor28 = EdgeInsets.symmetric(horizontal: 28);
|
||||
static const kPaddingHor12 = EdgeInsets.symmetric(horizontal: 12);
|
||||
static const kPaddingHor10 = EdgeInsets.symmetric(horizontal: 10);
|
||||
static const kPaddingHor32 = EdgeInsets.symmetric(horizontal: 32);
|
||||
static const kPaddingHor34 = EdgeInsets.symmetric(horizontal: 34);
|
||||
static const kPaddingHor36 = EdgeInsets.symmetric(horizontal: 36);
|
||||
static const kPaddingHor38 = EdgeInsets.symmetric(horizontal: 38);
|
||||
static const kPaddingHor44 = EdgeInsets.symmetric(horizontal: 44);
|
||||
static const kPaddingVer8 = EdgeInsets.symmetric(vertical: 8);
|
||||
static const kPaddingHor16Ver12 = EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
);
|
||||
static const kPaddingHor12Ver16 = EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 16,
|
||||
);
|
||||
static const kPaddingHor32Ver16 = EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
vertical: 16,
|
||||
);
|
||||
static const kPaddingHor16Ver8 = EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
);
|
||||
static const kPaddingHor8Ver4 = EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
);
|
||||
static const kPaddingHor8Ver5 = EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 5,
|
||||
);
|
||||
static const kPaddingHor6Ver4 = EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 4,
|
||||
);
|
||||
static const kPaddingHor12Ver8 = EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
);
|
||||
static const kPaddingVer10Hor16 = EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 16,
|
||||
);
|
||||
static const kPaddingVer12Hor18 = EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 16,
|
||||
);
|
||||
static const kPaddingVer8Hor12 = EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 12,
|
||||
);
|
||||
static const kPaddingVer10Hor12 = EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 12,
|
||||
);
|
||||
static const kPaddingVer2Hor4 = EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 4,
|
||||
);
|
||||
static const kPaddingVer2Hor8 = EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 8,
|
||||
);
|
||||
static const kPaddingVer3Hor10 = EdgeInsets.symmetric(
|
||||
vertical: 3,
|
||||
horizontal: 10,
|
||||
);
|
||||
static const kPaddingVer2Hor10 = EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 10,
|
||||
);
|
||||
static const kPaddingVer2Hor6 = EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 6,
|
||||
);
|
||||
static const kPaddingVer5Hor2 = EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 2,
|
||||
);
|
||||
static const kPaddingVer18Hor16 = EdgeInsets.symmetric(
|
||||
vertical: 18,
|
||||
horizontal: 16,
|
||||
);
|
||||
static const kPaddingVer24Hor16 = EdgeInsets.symmetric(
|
||||
vertical: 24,
|
||||
horizontal: 16,
|
||||
);
|
||||
static const kPaddingVer24Hor8 = EdgeInsets.symmetric(
|
||||
vertical: 24,
|
||||
horizontal: 8,
|
||||
);
|
||||
static const kPaddingVer14Hor12 = EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
horizontal: 12,
|
||||
);
|
||||
static const kPaddingVer14Hor16 = EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
horizontal: 16,
|
||||
);
|
||||
static const kPaddingVer16Hor24 = EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 24,
|
||||
);
|
||||
static const kPaddingVer4Hor12 = EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 12,
|
||||
);
|
||||
static const kPaddingVer4Hor16 = EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 16,
|
||||
);
|
||||
static const kPaddingVer6Hor12 = EdgeInsets.symmetric(
|
||||
vertical: 6,
|
||||
horizontal: 12,
|
||||
);
|
||||
static const kPaddingVer12Hor14 = EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 14,
|
||||
);
|
||||
static const kPaddingVer28Hor28 = EdgeInsets.symmetric(
|
||||
vertical: 28,
|
||||
horizontal: 28,
|
||||
);
|
||||
|
||||
/// border radius
|
||||
static const kRadius = Radius.zero;
|
||||
static const kRadius8 = Radius.circular(8);
|
||||
static const kRadius6 = Radius.circular(6);
|
||||
static const kRadius12 = Radius.circular(12);
|
||||
static const kBorderRadius = BorderRadius.all(Radius.circular(0));
|
||||
static const kBorderRadius2 = BorderRadius.all(Radius.circular(2));
|
||||
static const kBorderRadius4 = BorderRadius.all(Radius.circular(4));
|
||||
static const kBorderRadius5 = BorderRadius.all(Radius.circular(5));
|
||||
static const kBorderRadius6 = BorderRadius.all(Radius.circular(6));
|
||||
static const kBorderRadius8 = BorderRadius.all(Radius.circular(8));
|
||||
static const kBorderRadiusTop8 = BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
);
|
||||
static const kBorderRadiusTop10 = BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(10),
|
||||
);
|
||||
static const kBorderRadiusTop12 = BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
);
|
||||
static const kBorderRadiusTop16 = BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
);
|
||||
static const kBorderRadiusBottom16 = BorderRadius.only(
|
||||
bottomLeft: Radius.circular(16),
|
||||
bottomRight: Radius.circular(16),
|
||||
);
|
||||
static const kBorderRadiusTop24 = BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
topRight: Radius.circular(24),
|
||||
);
|
||||
static const kBorderRadiusBottom8 = BorderRadius.only(
|
||||
bottomLeft: Radius.circular(8),
|
||||
bottomRight: Radius.circular(8),
|
||||
);
|
||||
static const kBorderRadiusBottom10 = BorderRadius.only(
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
);
|
||||
static const kBorderRadiusTopBot12 = BorderRadius.only(
|
||||
topRight: AppUtils.kRadius12,
|
||||
bottomRight: AppUtils.kRadius12,
|
||||
);
|
||||
static const kBorderRadiusLeftTopBob12 = BorderRadius.only(
|
||||
topLeft: AppUtils.kRadius12,
|
||||
bottomLeft: AppUtils.kRadius12,
|
||||
);
|
||||
static const kBorderRadius12 = BorderRadius.all(Radius.circular(12));
|
||||
static const kBorderRadius10 = BorderRadius.all(Radius.circular(10));
|
||||
static const kBorderRadius16 = BorderRadius.all(Radius.circular(16));
|
||||
static const kBorderRadius14 = BorderRadius.all(Radius.circular(14));
|
||||
static const kBorderRadius24 = BorderRadius.all(Radius.circular(24));
|
||||
static const kBorderRadius20 = BorderRadius.all(Radius.circular(20));
|
||||
static const kBorderRadius32 = BorderRadius.all(Radius.circular(32));
|
||||
static const kBorderRadius48 = BorderRadius.all(Radius.circular(48));
|
||||
static const kBorderRadius64 = BorderRadius.all(Radius.circular(64));
|
||||
// static const kBoxDecoration = BoxDecoration(
|
||||
// borderRadius: AppUtils.kBorderRadius24,
|
||||
// color: LightThemeColors.whiteBackground,
|
||||
// );
|
||||
// static const kBoxDecorationGray = BoxDecoration(
|
||||
// borderRadius: AppUtils.kBorderRadius24,
|
||||
// color: LightThemeColors.backgroundGray,
|
||||
// );
|
||||
//
|
||||
// static const kBoxDecoration12 = BoxDecoration(
|
||||
// borderRadius: AppUtils.kBorderRadius12,
|
||||
// color: LightThemeColors.whiteBackground,
|
||||
// );
|
||||
// static const kBoxDecoration12BorderColor = BoxDecoration(
|
||||
// borderRadius: AppUtils.kBorderRadius12,
|
||||
// color: LightThemeColors.badgeGray,
|
||||
// );
|
||||
// static const kBoxDecoration12Gray = BoxDecoration(
|
||||
// borderRadius: AppUtils.kBorderRadius12,
|
||||
// color: LightThemeColors.scaffoldBackgroundColor,
|
||||
// );
|
||||
// static BoxDecoration kBoxDecoration12WhiteBorderRed = BoxDecoration(
|
||||
// borderRadius: AppUtils.kBorderRadius12,
|
||||
// color: LightThemeColors.whiteBackground,
|
||||
// border: Border.all(color: LightThemeColors.red),
|
||||
// );
|
||||
// static const kBoxDecorationTop24 = BoxDecoration(
|
||||
// color: LightThemeColors.white,
|
||||
// borderRadius: AppUtils.kBorderRadiusTop24,
|
||||
// );
|
||||
// static const kBoxDecorationTop = BoxDecoration(
|
||||
// borderRadius: BorderRadius.only(
|
||||
// topLeft: Radius.circular(24),
|
||||
// topRight: Radius.circular(24),
|
||||
// ),
|
||||
// color: LightThemeColors.whiteBackground,
|
||||
// );
|
||||
// static const kBoxDecorationBottom = BoxDecoration(
|
||||
// borderRadius: BorderRadius.only(
|
||||
// bottomLeft: Radius.circular(24),
|
||||
// bottomRight: Radius.circular(24),
|
||||
// ),
|
||||
// color: LightThemeColors.whiteBackground,
|
||||
// );
|
||||
}
|
||||
99
lib/core/widgets/drop_down/custom_drop_down.dart
Normal file
99
lib/core/widgets/drop_down/custom_drop_down.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
import 'package:cargocalculaterapp/constants/constants.dart';
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../theme/app_text_styles.dart';
|
||||
import '../../utils/app_utils.dart';
|
||||
|
||||
class CustomDropDown extends StatelessWidget {
|
||||
const CustomDropDown({
|
||||
super.key,
|
||||
this.name,
|
||||
required this.isRequired,
|
||||
this.errorText,
|
||||
this.isError,
|
||||
this.style,
|
||||
required this.onChange,
|
||||
this.value,
|
||||
});
|
||||
|
||||
final String? name;
|
||||
final bool isRequired;
|
||||
final String? errorText;
|
||||
final bool? isError;
|
||||
final TextStyle? style;
|
||||
final String? value;
|
||||
final Function(String?) onChange;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (name?.isNotEmpty ?? false)
|
||||
Text(name ?? '', style: style ?? context.text.categoryName),
|
||||
AppUtils.kBoxWidth4,
|
||||
if (isRequired) SvgPicture.asset("assets/svg/ic_required.svg"),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight8,
|
||||
InputDecorator(
|
||||
decoration: InputDecoration(
|
||||
fillColor: context.color.white10Transparent,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.color.borderColor,
|
||||
),
|
||||
borderRadius: AppUtils.kBorderRadius12,
|
||||
),
|
||||
contentPadding: AppUtils.kPaddingAll16,
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton(
|
||||
dropdownColor: context.color.grayBackground,
|
||||
hint: Text(AppLocalization.current.warehouse,style: TextStyle(
|
||||
color: context.color.secondaryText,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
),),
|
||||
icon: Icon(
|
||||
Icons.keyboard_arrow_down_outlined,
|
||||
size: 24,
|
||||
color: context.color.textColor,
|
||||
),
|
||||
isDense: true,
|
||||
iconSize: 0,
|
||||
value: value,
|
||||
items:
|
||||
AppConst.warehouseOptions.keys.map((entry) {
|
||||
return DropdownMenuItem(
|
||||
value: entry,
|
||||
child: Text(
|
||||
Functions.getTranslatedItem(
|
||||
AppConst.warehouseOptions[entry],
|
||||
context,
|
||||
),
|
||||
style: context.text.profileCategory,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: onChange,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isError ?? false) AppUtils.kBoxHeight4,
|
||||
if (isError ?? false)
|
||||
Text(
|
||||
errorText ?? "",
|
||||
style: AppTextStyles.timer.copyWith(fontSize: 12),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
24
lib/core/widgets/loading/custom_loading.dart
Normal file
24
lib/core/widgets/loading/custom_loading.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../theme/colors/app_colors.dart';
|
||||
|
||||
class CustomLoadingWidget extends StatelessWidget {
|
||||
final Color? color;
|
||||
|
||||
const CustomLoadingWidget({super.key, this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Platform.isAndroid
|
||||
? CircularProgressIndicator(
|
||||
color: color ?? LightThemeColors.lightGrey,
|
||||
)
|
||||
: CupertinoActivityIndicator(color: color),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
90
lib/core/widgets/loading/progress_hud.dart
Normal file
90
lib/core/widgets/loading/progress_hud.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
import "dart:io";
|
||||
import "package:cargocalculaterapp/core/theme/colors/app_colors.dart";
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "dart:ui";
|
||||
|
||||
///
|
||||
/// Wrap around any widget that makes an async call to show a modal progress
|
||||
/// indicator while the async call is in progress.
|
||||
///
|
||||
/// HUD=Heads Up Display
|
||||
///
|
||||
class ModalProgressHUD extends StatelessWidget {
|
||||
/// A required [bool] to toggle the modal overlay.
|
||||
final bool inAsyncCall;
|
||||
|
||||
/// A [double] specifying the opacity of the modal overlay, defaults to 0.3
|
||||
final double opacity;
|
||||
|
||||
/// A [Color] object which is assigned to the loading barrier, defaults to grey
|
||||
final Color color;
|
||||
|
||||
/// A [Widget] which is shown at the center of the modal overlay,
|
||||
/// defaults to the standard android spinner animation.
|
||||
|
||||
/// An [Offset] object which is applied to the [progressIndicator] when specified.
|
||||
final Offset? offset;
|
||||
|
||||
/// A [bool] which controls whether the modal overlay can be dismissible when interated.
|
||||
final bool dismissible;
|
||||
|
||||
/// A [Widget] over which the modal overlay is activated.
|
||||
final Widget child;
|
||||
|
||||
/// A [double] value specifying the amount of background blur when progress hud is active.
|
||||
final double blur;
|
||||
|
||||
const ModalProgressHUD({
|
||||
super.key,
|
||||
required this.inAsyncCall,
|
||||
this.opacity = 0.3,
|
||||
this.color = Colors.grey,
|
||||
this.offset,
|
||||
this.dismissible = false,
|
||||
required this.child,
|
||||
this.blur = 0.0,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget layOutProgressIndicator;
|
||||
if (offset == null) {
|
||||
layOutProgressIndicator = Center(
|
||||
child:
|
||||
Platform.isIOS
|
||||
? const CupertinoActivityIndicator()
|
||||
: const CircularProgressIndicator(
|
||||
color: LightThemeColors.accentColor,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
layOutProgressIndicator = Positioned(
|
||||
left: offset!.dx,
|
||||
top: offset!.dy,
|
||||
child:
|
||||
Platform.isIOS
|
||||
? const CupertinoActivityIndicator()
|
||||
: const CircularProgressIndicator(
|
||||
color: LightThemeColors.accentColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
child,
|
||||
if (inAsyncCall) ...[
|
||||
BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
|
||||
child: Opacity(
|
||||
opacity: opacity,
|
||||
child: ModalBarrier(dismissible: dismissible, color: color),
|
||||
),
|
||||
),
|
||||
layOutProgressIndicator,
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
95
lib/core/widgets/rating/rating_bar_widget.dart
Normal file
95
lib/core/widgets/rating/rating_bar_widget.dart
Normal file
@@ -0,0 +1,95 @@
|
||||
import 'package:cargocalculaterapp/core/extension/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../utils/app_utils.dart';
|
||||
|
||||
typedef RatingChangeCallback = void Function(double rating)?;
|
||||
|
||||
const starPoints = 5;
|
||||
|
||||
class RatingBarWidget extends StatelessWidget {
|
||||
final double rating;
|
||||
final double iconsSize;
|
||||
final Color activeRatingColor;
|
||||
final Color inactiveRatingColor;
|
||||
final RatingChangeCallback onRatingChanged;
|
||||
final bool isAnimate;
|
||||
final Widget separator;
|
||||
final bool isSharp;
|
||||
|
||||
const RatingBarWidget({
|
||||
super.key,
|
||||
required this.rating,
|
||||
this.iconsSize = 18,
|
||||
this.activeRatingColor = const Color(0xffFFA047),
|
||||
this.inactiveRatingColor = const Color(0xffCED5DF),
|
||||
this.onRatingChanged,
|
||||
this.isAnimate = false,
|
||||
this.separator = AppUtils.kBoxWidth3,
|
||||
this.isSharp = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isAnimate) {
|
||||
return TweenAnimationBuilder<double>(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
curve: Curves.easeInOut,
|
||||
tween: Tween<double>(begin: 0, end: rating),
|
||||
builder: (_, value, __) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: List.generate(
|
||||
10,
|
||||
(index) => index.isEven
|
||||
? buildStar(index.exactIndex, rating, isSharp)
|
||||
: separator,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: List.generate(
|
||||
10,
|
||||
(index) => index.isEven
|
||||
? buildStar(index.exactIndex, rating, isSharp)
|
||||
: separator,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildStar(int index, double rating, isSharp) {
|
||||
Widget icon;
|
||||
double present = rating - index;
|
||||
icon = ShaderMask(
|
||||
blendMode: BlendMode.srcIn,
|
||||
shaderCallback: (bounds) {
|
||||
return LinearGradient(
|
||||
tileMode: TileMode.clamp,
|
||||
colors: [activeRatingColor, inactiveRatingColor],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
stops: [present, present],
|
||||
).createShader(bounds);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
isSharp
|
||||
? "assets/svg/ic_star_selected.svg"
|
||||
: "assets/svg/ic_star_selected.svg",
|
||||
height: iconsSize,
|
||||
width: iconsSize,
|
||||
),
|
||||
);
|
||||
|
||||
return InkResponse(
|
||||
onTap: onRatingChanged == null
|
||||
? null
|
||||
: () => onRatingChanged!(index + 1.0),
|
||||
child: icon,
|
||||
);
|
||||
}
|
||||
}
|
||||
40
lib/core/widgets/shimmer/shimmer_widget.dart
Normal file
40
lib/core/widgets/shimmer/shimmer_widget.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
import '../../theme/colors/app_colors.dart';
|
||||
import '../../utils/app_utils.dart';
|
||||
|
||||
class ShimmerWidget extends StatelessWidget {
|
||||
const ShimmerWidget({
|
||||
super.key,
|
||||
required this.width,
|
||||
required this.height,
|
||||
this.borderRadius = AppUtils.kBorderRadius6,
|
||||
this.margin,
|
||||
});
|
||||
|
||||
final double width;
|
||||
final double height;
|
||||
final BorderRadiusGeometry? borderRadius;
|
||||
final EdgeInsetsGeometry? margin;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Shimmer.fromColors(
|
||||
enabled: true,
|
||||
baseColor:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? const Color(0x80FFFFFF)
|
||||
: const Color(0xffF8F8F9),
|
||||
highlightColor: LightThemeColors.shimmerHighlight,
|
||||
child: Container(
|
||||
width: width,
|
||||
height: height,
|
||||
margin: margin,
|
||||
decoration: BoxDecoration(
|
||||
color: LightThemeColors.white,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
108
lib/core/widgets/text_filds/custom_text_field_name.dart
Normal file
108
lib/core/widgets/text_filds/custom_text_field_name.dart
Normal file
@@ -0,0 +1,108 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../theme/app_text_styles.dart';
|
||||
import '../../theme/colors/app_colors.dart';
|
||||
import '../../utils/app_utils.dart';
|
||||
|
||||
class CustomTextFieldName extends StatelessWidget {
|
||||
const CustomTextFieldName({
|
||||
super.key,
|
||||
this.name,
|
||||
required this.hint,
|
||||
this.readOnly = false,
|
||||
this.onTap,
|
||||
this.prefixWidget,
|
||||
this.controller,
|
||||
this.format,
|
||||
this.inputType,
|
||||
this.onchange,
|
||||
this.style,
|
||||
this.suffix,
|
||||
this.errorText,
|
||||
this.isError,
|
||||
this.isRequired = false,
|
||||
this.maxLines,
|
||||
this.minLines,
|
||||
this.inputStyle,
|
||||
this.textCapitalization = TextCapitalization.sentences,
|
||||
this.fillColor,
|
||||
this.obscureText = false,
|
||||
});
|
||||
|
||||
final String? name;
|
||||
final String hint;
|
||||
final bool readOnly;
|
||||
final Function()? onTap;
|
||||
final Widget? prefixWidget;
|
||||
final TextEditingController? controller;
|
||||
final List<TextInputFormatter>? format;
|
||||
final TextInputType? inputType;
|
||||
final bool isRequired;
|
||||
final Function(String)? onchange;
|
||||
final TextStyle? style;
|
||||
final Widget? suffix;
|
||||
final String? errorText;
|
||||
final bool? isError;
|
||||
final int? maxLines;
|
||||
final int? minLines;
|
||||
final TextStyle? inputStyle;
|
||||
final TextCapitalization textCapitalization;
|
||||
final Color? fillColor;
|
||||
final bool obscureText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (name?.isNotEmpty ?? false)
|
||||
Text(name ?? '', style: style ?? context.text.categoryName),
|
||||
AppUtils.kBoxWidth4,
|
||||
if (isRequired) SvgPicture.asset("assets/svg/ic_required.svg"),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight8,
|
||||
TextField(
|
||||
style: inputStyle ?? TextStyle(color: context.color.textColor),
|
||||
keyboardType: inputType,
|
||||
controller: controller,
|
||||
onTap: onTap,
|
||||
readOnly: readOnly,
|
||||
onChanged: onchange,
|
||||
inputFormatters: format,
|
||||
maxLines: maxLines,
|
||||
minLines: minLines,
|
||||
obscureText: obscureText,
|
||||
textCapitalization: textCapitalization,
|
||||
decoration: InputDecoration(
|
||||
fillColor: fillColor,
|
||||
hintText: hint,
|
||||
prefixIcon: prefixWidget,
|
||||
suffixIcon: suffix,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
!(isError ?? false)
|
||||
? context.color.accentColor
|
||||
: ThemeColors.timerRed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isError ?? false) AppUtils.kBoxHeight4,
|
||||
if (isError ?? false)
|
||||
Text(
|
||||
errorText ?? "",
|
||||
style: AppTextStyles.timer.copyWith(fontSize: 12),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_response.dart';
|
||||
|
||||
abstract class AuthRemoteDataSource {
|
||||
Future<LoginResponse> login(LoginRequest request);
|
||||
|
||||
Future<SignUpResponse> signUp(SignUpRequest request);
|
||||
|
||||
Future<AuthVerificationResponse> verify(AuthVerificationRequest request);
|
||||
|
||||
Future<FcmAddResponse> addFcm(FcmAddRequest request);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_request.dart';
|
||||
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_response.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../core/error/exceptions.dart';
|
||||
import '../../../../../core/local_source/local_source.dart';
|
||||
import '../../../../../injector_container.dart';
|
||||
import 'auth_remote_data_source.dart';
|
||||
|
||||
class AuthRemoteDataSourceImpl extends AuthRemoteDataSource {
|
||||
final Dio dio;
|
||||
|
||||
AuthRemoteDataSourceImpl(this.dio);
|
||||
|
||||
@override
|
||||
Future<LoginResponse> login(LoginRequest request) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
Constants.baseUrl + Urls.login,
|
||||
options: DioConstants.options,
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return LoginResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SignUpResponse> signUp(SignUpRequest request) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
Constants.baseUrl + Urls.signUp,
|
||||
options: DioConstants.options,
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return SignUpResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AuthVerificationResponse> verify(
|
||||
AuthVerificationRequest request,
|
||||
) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
Constants.baseUrl + Urls.verify,
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return AuthVerificationResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FcmAddResponse> addFcm(FcmAddRequest request) async {
|
||||
try {
|
||||
final Response response = await dio.put(
|
||||
Constants.baseUrl + Urls.addFcm,
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return FcmAddResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
lib/features/auth/data/model/auth_verification_request.dart
Normal file
21
lib/features/auth/data/model/auth_verification_request.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
class AuthVerificationRequest {
|
||||
AuthVerificationRequest({this.phoneNumber, this.smsCode, this.email});
|
||||
|
||||
AuthVerificationRequest.fromJson(dynamic json) {
|
||||
phoneNumber = json['phone_number'];
|
||||
smsCode = json['smsCode'];
|
||||
email = json['email'];
|
||||
}
|
||||
|
||||
String? phoneNumber;
|
||||
String? smsCode;
|
||||
String? email;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['smsCode'] = smsCode;
|
||||
map['email'] = email;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
16
lib/features/auth/data/model/auth_verification_response.dart
Normal file
16
lib/features/auth/data/model/auth_verification_response.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
class AuthVerificationResponse {
|
||||
AuthVerificationResponse({
|
||||
this.accessToken,});
|
||||
|
||||
AuthVerificationResponse.fromJson(dynamic json) {
|
||||
accessToken = json['access_token'];
|
||||
}
|
||||
String? accessToken;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['access_token'] = accessToken;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
16
lib/features/auth/data/model/fcm_add_request.dart
Normal file
16
lib/features/auth/data/model/fcm_add_request.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
class FcmAddRequest {
|
||||
FcmAddRequest({
|
||||
this.sfmToken,});
|
||||
|
||||
FcmAddRequest.fromJson(dynamic json) {
|
||||
sfmToken = json['sfm_token'];
|
||||
}
|
||||
String? sfmToken;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['sfm_token'] = sfmToken;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
51
lib/features/auth/data/model/fcm_add_response.dart
Normal file
51
lib/features/auth/data/model/fcm_add_response.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
class FcmAddResponse {
|
||||
FcmAddResponse({
|
||||
this.message,
|
||||
this.user,});
|
||||
|
||||
FcmAddResponse.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
user = json['user'] != null ? User.fromJson(json['user']) : null;
|
||||
}
|
||||
String? message;
|
||||
User? user;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
if (user != null) {
|
||||
map['user'] = user?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class User {
|
||||
User({
|
||||
this.fullname,
|
||||
this.phoneNumber,
|
||||
this.email,
|
||||
this.sfmToken,});
|
||||
|
||||
User.fromJson(dynamic json) {
|
||||
fullname = json['fullname'];
|
||||
phoneNumber = json['phone_number'];
|
||||
email = json['email'];
|
||||
sfmToken = json['sfm_token'];
|
||||
}
|
||||
String? fullname;
|
||||
String? phoneNumber;
|
||||
String? email;
|
||||
String? sfmToken;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['fullname'] = fullname;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['email'] = email;
|
||||
map['sfm_token'] = sfmToken;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
24
lib/features/auth/data/model/login_request.dart
Normal file
24
lib/features/auth/data/model/login_request.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
class LoginRequest {
|
||||
LoginRequest({
|
||||
this.email,
|
||||
this.phone,
|
||||
this.ucode,});
|
||||
|
||||
LoginRequest.fromJson(dynamic json) {
|
||||
email = json['email'];
|
||||
phone = json['phone'];
|
||||
ucode = json['ucode'];
|
||||
}
|
||||
String? email;
|
||||
String? phone;
|
||||
String? ucode;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['email'] = email;
|
||||
map['phone'] = phone;
|
||||
map['ucode'] = ucode;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
20
lib/features/auth/data/model/login_response.dart
Normal file
20
lib/features/auth/data/model/login_response.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
class LoginResponse {
|
||||
LoginResponse({
|
||||
this.token,
|
||||
this.refreshToken,});
|
||||
|
||||
LoginResponse.fromJson(dynamic json) {
|
||||
token = json['token'];
|
||||
refreshToken = json['refreshToken'];
|
||||
}
|
||||
String? token;
|
||||
String? refreshToken;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['token'] = token;
|
||||
map['refreshToken'] = refreshToken;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
25
lib/features/auth/data/model/sign_up_request.dart
Normal file
25
lib/features/auth/data/model/sign_up_request.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
class SignUpRequest {
|
||||
SignUpRequest({
|
||||
this.phoneNumber,
|
||||
this.email,
|
||||
this.fullname,});
|
||||
|
||||
SignUpRequest.fromJson(dynamic json) {
|
||||
phoneNumber = json['phone_number'];
|
||||
email = json['email'];
|
||||
fullname = json['fullname'];
|
||||
}
|
||||
String? phoneNumber;
|
||||
String? email;
|
||||
String? fullname;
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['email'] = email;
|
||||
map['fullname'] = fullname;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
16
lib/features/auth/data/model/sign_up_response.dart
Normal file
16
lib/features/auth/data/model/sign_up_response.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
class SignUpResponse {
|
||||
SignUpResponse({
|
||||
this.token,});
|
||||
|
||||
SignUpResponse.fromJson(dynamic json) {
|
||||
token = json['token'];
|
||||
}
|
||||
String? token;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['token'] = token;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
73
lib/features/auth/data/repository/auth_repository_impl.dart
Normal file
73
lib/features/auth/data/repository/auth_repository_impl.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/exceptions.dart';
|
||||
import '../../domain/repository/auth_repository.dart';
|
||||
import '../data_source/remote/auth_remote_data_source.dart';
|
||||
|
||||
class AuthRepositoryImpl extends AuthRepository {
|
||||
final AuthRemoteDataSource remoteDataSource;
|
||||
|
||||
AuthRepositoryImpl(this.remoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LoginResponse>> login(LoginRequest request) async {
|
||||
try {
|
||||
final response = await remoteDataSource.login(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
if (e is ServerException) {
|
||||
return Left(ServerFailure(message: e.message));
|
||||
}
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, SignUpResponse>> signUp(SignUpRequest request) async {
|
||||
try {
|
||||
final response = await remoteDataSource.signUp(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
if (e is ServerException) {
|
||||
return Left(ServerFailure(message: e.message));
|
||||
}
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, AuthVerificationResponse>> verify(
|
||||
AuthVerificationRequest request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.verify(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
if (e is ServerException) {
|
||||
return Left(ServerFailure(message: e.message));
|
||||
}
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, FcmAddResponse>> fcmAdd(FcmAddRequest request) async {
|
||||
try {
|
||||
final response = await remoteDataSource.addFcm(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
if (e is ServerException) {
|
||||
return Left(ServerFailure(message: e.message));
|
||||
}
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
23
lib/features/auth/domain/repository/auth_repository.dart
Normal file
23
lib/features/auth/domain/repository/auth_repository.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_response.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../../../core/error/failure.dart';
|
||||
|
||||
abstract class AuthRepository {
|
||||
Future<Either<Failure, LoginResponse>> login(LoginRequest request);
|
||||
|
||||
Future<Either<Failure, SignUpResponse>> signUp(SignUpRequest request);
|
||||
|
||||
Future<Either<Failure, AuthVerificationResponse>> verify(
|
||||
AuthVerificationRequest request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, FcmAddResponse>> fcmAdd(FcmAddRequest request);
|
||||
}
|
||||
18
lib/features/auth/domain/usecases/fcm_add_usecase.dart
Normal file
18
lib/features/auth/domain/usecases/fcm_add_usecase.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/fcm_add_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/auth_repository.dart';
|
||||
|
||||
class FcmAddUseCase extends UseCase<FcmAddResponse, FcmAddRequest> {
|
||||
final AuthRepository repository;
|
||||
|
||||
FcmAddUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, FcmAddResponse>> call(FcmAddRequest params) async {
|
||||
return await repository.fcmAdd(params);
|
||||
}
|
||||
}
|
||||
19
lib/features/auth/domain/usecases/login_usecase.dart
Normal file
19
lib/features/auth/domain/usecases/login_usecase.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/login_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/auth_repository.dart';
|
||||
|
||||
class LoginUseCase extends UseCase<LoginResponse, LoginRequest> {
|
||||
final AuthRepository repository;
|
||||
|
||||
LoginUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LoginResponse>> call(LoginRequest params) async {
|
||||
final response = await repository.login(params);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
19
lib/features/auth/domain/usecases/sign_up_usecase.dart
Normal file
19
lib/features/auth/domain/usecases/sign_up_usecase.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/sign_up_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/auth_repository.dart';
|
||||
|
||||
class SignUpUseCase extends UseCase<SignUpResponse, SignUpRequest> {
|
||||
final AuthRepository repository;
|
||||
|
||||
SignUpUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, SignUpResponse>> call(SignUpRequest params) async {
|
||||
final response = await repository.signUp(params);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
22
lib/features/auth/domain/usecases/verify_phone_usecase.dart
Normal file
22
lib/features/auth/domain/usecases/verify_phone_usecase.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/data/model/auth_verification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/auth_repository.dart';
|
||||
|
||||
class VerifyPhoneUseCase
|
||||
extends UseCase<AuthVerificationResponse, AuthVerificationRequest> {
|
||||
final AuthRepository repository;
|
||||
|
||||
VerifyPhoneUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, AuthVerificationResponse>> call(
|
||||
AuthVerificationRequest params,
|
||||
) async {
|
||||
final response = await repository.verify(params);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
116
lib/features/auth/presentation/bloc/auth/auth_bloc.dart
Normal file
116
lib/features/auth/presentation/bloc/auth/auth_bloc.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'package:cargocalculaterapp/core/local_source/local_source.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:cargocalculaterapp/router/app_routes.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../injector_container.dart';
|
||||
import '../../../../../router/name_routes.dart';
|
||||
import '../../../data/model/login_request.dart';
|
||||
import '../../../domain/usecases/login_usecase.dart';
|
||||
import '../../pages/auth_confirm/argument/auth_confirm_argument.dart';
|
||||
|
||||
part 'auth_event.dart';
|
||||
|
||||
part 'auth_state.dart';
|
||||
|
||||
class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
AuthBloc(this.loginUseCase)
|
||||
: super(
|
||||
const AuthState(
|
||||
passwordHidden: true,
|
||||
isLoading: false,
|
||||
isButtonEnabled: false,
|
||||
),
|
||||
) {
|
||||
on<PasswordVisibilityEvent>(_showHidePassword);
|
||||
on<OnInputEnterEvent>(_onInputEnter);
|
||||
on<SubmitEvent>(_onSubmit);
|
||||
}
|
||||
|
||||
final LoginUseCase loginUseCase;
|
||||
|
||||
void _showHidePassword(
|
||||
PasswordVisibilityEvent event,
|
||||
Emitter<AuthState> emit,
|
||||
) {
|
||||
emit(state.copyWith(passwordHidden: !state.passwordHidden));
|
||||
}
|
||||
|
||||
void _onInputEnter(OnInputEnterEvent event, Emitter<AuthState> emit) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isButtonEnabled:
|
||||
(isEmail(event.login) || isPhoneNumber(event.login)) &&
|
||||
event.password.isNotEmpty,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSubmit(SubmitEvent event, Emitter<AuthState> emit) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final response = await loginUseCase(
|
||||
LoginRequest(
|
||||
email: isEmail(event.login) ? event.login : "",
|
||||
phone: isPhoneNumber(event.login)
|
||||
? event.login.replaceAll("+", "")
|
||||
: "",
|
||||
ucode: event.password,
|
||||
),
|
||||
);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
showErrorSnackBar();
|
||||
},
|
||||
(r) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
sl<LocalSource>().setUCode(event.password);
|
||||
Navigator.pushNamed(
|
||||
rootNavigatorKey.currentContext!,
|
||||
Routes.authConfirm,
|
||||
arguments: AuthConfirmArgument(
|
||||
fullName: "",
|
||||
mail: isEmail(event.login) ? event.login : "",
|
||||
phoneNumber: isPhoneNumber(event.login) ? event.login : "",
|
||||
fromLoginPage: true,
|
||||
password: event.password,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
bool isEmail(String input) {
|
||||
return RegExConst.emailRegex.hasMatch(input);
|
||||
}
|
||||
|
||||
bool isPhoneNumber(String input) {
|
||||
return RegExConst.phoneRegex.hasMatch(input);
|
||||
}
|
||||
|
||||
void showErrorSnackBar() {
|
||||
final snackBar = SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Row(
|
||||
children: [
|
||||
const Icon(Icons.error, color: Colors.white),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
AppLocalization.current.login_error,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
duration: const Duration(seconds: 3),
|
||||
);
|
||||
|
||||
ScaffoldMessenger.of(
|
||||
rootNavigatorKey.currentContext!,
|
||||
).showSnackBar(snackBar);
|
||||
}
|
||||
}
|
||||
32
lib/features/auth/presentation/bloc/auth/auth_event.dart
Normal file
32
lib/features/auth/presentation/bloc/auth/auth_event.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
part of 'auth_bloc.dart';
|
||||
|
||||
sealed class AuthEvent extends Equatable {
|
||||
const AuthEvent();
|
||||
}
|
||||
|
||||
final class PasswordVisibilityEvent extends AuthEvent {
|
||||
const PasswordVisibilityEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class OnInputEnterEvent extends AuthEvent {
|
||||
final String login;
|
||||
final String password;
|
||||
|
||||
const OnInputEnterEvent({required this.login, required this.password});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [login, password];
|
||||
}
|
||||
|
||||
final class SubmitEvent extends AuthEvent {
|
||||
final String login;
|
||||
final String password;
|
||||
|
||||
const SubmitEvent({required this.login, required this.password});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [login, password];
|
||||
}
|
||||
28
lib/features/auth/presentation/bloc/auth/auth_state.dart
Normal file
28
lib/features/auth/presentation/bloc/auth/auth_state.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
part of 'auth_bloc.dart';
|
||||
|
||||
class AuthState extends Equatable {
|
||||
const AuthState({
|
||||
required this.passwordHidden,
|
||||
required this.isLoading,
|
||||
required this.isButtonEnabled,
|
||||
});
|
||||
|
||||
final bool passwordHidden;
|
||||
final bool isLoading;
|
||||
final bool isButtonEnabled;
|
||||
|
||||
AuthState copyWith({
|
||||
bool? passwordHidden,
|
||||
bool? isLoading,
|
||||
bool? isButtonEnabled,
|
||||
}) {
|
||||
return AuthState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
passwordHidden: passwordHidden ?? this.passwordHidden,
|
||||
isButtonEnabled: isButtonEnabled ?? this.isButtonEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [passwordHidden, isLoading, isButtonEnabled];
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../../core/error/failure.dart';
|
||||
import '../../../../../core/functions/base_finctions.dart';
|
||||
import '../../../../../core/local_source/local_source.dart';
|
||||
import '../../../../../injector_container.dart';
|
||||
import '../../../../../service/notification_service.dart';
|
||||
import '../../../data/model/auth_verification_request.dart';
|
||||
import '../../../data/model/fcm_add_request.dart';
|
||||
import '../../../data/model/login_request.dart';
|
||||
import '../../../data/model/sign_up_request.dart';
|
||||
import '../../../domain/usecases/fcm_add_usecase.dart';
|
||||
import '../../../domain/usecases/login_usecase.dart';
|
||||
import '../../../domain/usecases/sign_up_usecase.dart';
|
||||
import '../../../domain/usecases/verify_phone_usecase.dart';
|
||||
import '../../pages/auth_confirm/argument/auth_confirm_argument.dart';
|
||||
|
||||
part 'auth_confirm_event.dart';
|
||||
|
||||
part 'auth_confirm_state.dart';
|
||||
|
||||
class AuthConfirmBloc extends Bloc<AuthConfirmEvent, AuthConfirmState> {
|
||||
AuthConfirmBloc(
|
||||
this.signUpUseCase,
|
||||
this.verifyPhoneUseCase,
|
||||
this.loginUseCase,
|
||||
this.fcmAddUseCase,
|
||||
) : super(const InitialState(true)) {
|
||||
on<TimerChangedEvent>(_timerChanged);
|
||||
on<SmsCodeEnterEvent>(_codeChanged);
|
||||
on<ResendCodeEvent>(_resendCode);
|
||||
on<OnSubmitEvent>(_onSubmitCode);
|
||||
}
|
||||
|
||||
final SignUpUseCase signUpUseCase;
|
||||
final VerifyPhoneUseCase verifyPhoneUseCase;
|
||||
final LoginUseCase loginUseCase;
|
||||
final FcmAddUseCase fcmAddUseCase;
|
||||
|
||||
Future<void> _timerChanged(
|
||||
TimerChangedEvent event,
|
||||
Emitter<AuthConfirmState> emit,
|
||||
) async {
|
||||
emit(InitialState(event.isVisible));
|
||||
}
|
||||
|
||||
void _codeChanged(SmsCodeEnterEvent event, Emitter<AuthConfirmState> emit) {
|
||||
if (event.code.length == 4) {
|
||||
emit(ButtonEnableState(state.isTimerVisible, event.code));
|
||||
} else {
|
||||
emit(InitialState(state.isTimerVisible));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _resendCode(
|
||||
ResendCodeEvent event,
|
||||
Emitter<AuthConfirmState> emit,
|
||||
) async {
|
||||
add(const TimerChangedEvent(isVisible: true));
|
||||
if (event.argument.fromLoginPage) {
|
||||
await loginUseCase(
|
||||
LoginRequest(
|
||||
email: event.argument.mail,
|
||||
phone: event.argument.phoneNumber?.replaceAll("+", ""),
|
||||
ucode: event.argument.password,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await signUpUseCase(
|
||||
SignUpRequest(
|
||||
fullname: event.argument.fullName,
|
||||
email: event.argument.mail,
|
||||
phoneNumber: event.argument.phoneNumber?.replaceAll("+", ""),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSubmitCode(
|
||||
OnSubmitEvent event,
|
||||
Emitter<AuthConfirmState> emit,
|
||||
) async {
|
||||
emit(LoadingState(state.isTimerVisible));
|
||||
final response = await verifyPhoneUseCase(
|
||||
AuthVerificationRequest(
|
||||
phoneNumber: event.argument?.phoneNumber?.replaceAll("+", ""),
|
||||
smsCode: event.code,
|
||||
email: event.argument?.mail ?? "",
|
||||
),
|
||||
);
|
||||
await response.fold(
|
||||
(l) {
|
||||
if (l is ServerFailure) {
|
||||
Functions.showErrorSnackBar(l.message);
|
||||
}
|
||||
emit(InitialState(state.isTimerVisible));
|
||||
},
|
||||
(r) async {
|
||||
sl<LocalSource>().setHasProfile(true);
|
||||
sl<LocalSource>().setAccessToken(r.accessToken ?? "");
|
||||
await _addFcmToken();
|
||||
emit(SuccessSate(state.isTimerVisible));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _addFcmToken() async {
|
||||
final String fcmToken = await NotificationService.getFcmToken();
|
||||
await fcmAddUseCase(FcmAddRequest(sfmToken: fcmToken));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
part of 'auth_confirm_bloc.dart';
|
||||
|
||||
sealed class AuthConfirmEvent extends Equatable {
|
||||
const AuthConfirmEvent();
|
||||
}
|
||||
|
||||
final class SmsCodeEnterEvent extends AuthConfirmEvent {
|
||||
final String code;
|
||||
|
||||
const SmsCodeEnterEvent(this.code);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [code];
|
||||
}
|
||||
|
||||
final class TimerChangedEvent extends AuthConfirmEvent {
|
||||
final bool isVisible;
|
||||
|
||||
const TimerChangedEvent({required this.isVisible});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isVisible];
|
||||
}
|
||||
|
||||
final class OnSubmitEvent extends AuthConfirmEvent {
|
||||
final String code;
|
||||
final AuthConfirmArgument? argument;
|
||||
|
||||
const OnSubmitEvent({required this.code, required this.argument});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [code, argument];
|
||||
}
|
||||
|
||||
final class ResendCodeEvent extends AuthConfirmEvent {
|
||||
final AuthConfirmArgument argument;
|
||||
|
||||
const ResendCodeEvent({required this.argument});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [argument];
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
part of 'auth_confirm_bloc.dart';
|
||||
|
||||
sealed class AuthConfirmState extends Equatable {
|
||||
const AuthConfirmState(this.isTimerVisible);
|
||||
|
||||
final bool isTimerVisible;
|
||||
}
|
||||
|
||||
class InitialState extends AuthConfirmState {
|
||||
const InitialState(super.isTimerVisible);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [super.isTimerVisible];
|
||||
}
|
||||
|
||||
class LoadingState extends AuthConfirmState {
|
||||
const LoadingState(super.isTimerVisible);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [super.isTimerVisible];
|
||||
}
|
||||
|
||||
class SuccessSate extends AuthConfirmState {
|
||||
const SuccessSate(super.isTimerVisible);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [super.isTimerVisible];
|
||||
}
|
||||
|
||||
class ErrorState extends AuthConfirmState {
|
||||
const ErrorState(super.isTimerVisible);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [super.isTimerVisible];
|
||||
}
|
||||
|
||||
class ButtonEnableState extends AuthConfirmState {
|
||||
const ButtonEnableState(super.isTimerVisible, this.code);
|
||||
|
||||
final String code;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [super.isTimerVisible, code];
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import 'package:cargocalculaterapp/core/local_source/local_source.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:cargocalculaterapp/router/app_routes.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../core/error/failure.dart';
|
||||
import '../../../../../core/functions/base_finctions.dart';
|
||||
import '../../../../../injector_container.dart';
|
||||
import '../../../../../router/name_routes.dart';
|
||||
import '../../../data/model/sign_up_request.dart';
|
||||
import '../../../domain/usecases/sign_up_usecase.dart';
|
||||
import '../../pages/auth_confirm/argument/auth_confirm_argument.dart';
|
||||
|
||||
part 'sign_up_event.dart';
|
||||
|
||||
part 'sign_up_state.dart';
|
||||
|
||||
class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
|
||||
SignUpBloc(this.signUpUseCase)
|
||||
: super(
|
||||
const SignUpState(
|
||||
isLoading: false,
|
||||
isButtonEnabled: false,
|
||||
passwordHidden: true,
|
||||
),
|
||||
) {
|
||||
on<PasswordVisibilityEvent>(_passwordVisibility);
|
||||
on<OnInputEnterEvent>(_onDataEnter);
|
||||
on<SubmitEvent>(_signUp);
|
||||
}
|
||||
|
||||
final SignUpUseCase signUpUseCase;
|
||||
|
||||
void _passwordVisibility(
|
||||
PasswordVisibilityEvent event,
|
||||
Emitter<SignUpState> emit,
|
||||
) {
|
||||
emit(state.copyWith(isButtonEnabled: !state.isButtonEnabled));
|
||||
}
|
||||
|
||||
void _onDataEnter(OnInputEnterEvent event, Emitter<SignUpState> emit) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isButtonEnabled:
|
||||
(isEmail(event.login) || isPhoneNumber(event.login)) &&
|
||||
event.fullName.isNotEmpty,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool isEmail(String input) {
|
||||
return RegExConst.emailRegex.hasMatch(input);
|
||||
}
|
||||
|
||||
bool isPhoneNumber(String input) {
|
||||
return RegExConst.phoneRegex.hasMatch(input);
|
||||
}
|
||||
|
||||
Future<void> _signUp(SubmitEvent event, Emitter<SignUpState> emit) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final response = await signUpUseCase(
|
||||
SignUpRequest(
|
||||
fullname: event.fullName,
|
||||
email: isEmail(event.login) ? event.login : "",
|
||||
phoneNumber: isPhoneNumber(event.login)
|
||||
? event.login.replaceAll("+", "")
|
||||
: "",
|
||||
),
|
||||
);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
if (l is ServerFailure) {
|
||||
Functions.showErrorSnackBar(AppLocalization.current.phone_registered);
|
||||
}
|
||||
},
|
||||
(r) {
|
||||
sl<LocalSource>().setAccessToken(r.token ?? "");
|
||||
emit(state.copyWith(isLoading: false));
|
||||
Navigator.pushNamed(
|
||||
rootNavigatorKey.currentContext!,
|
||||
Routes.authConfirm,
|
||||
arguments: AuthConfirmArgument(
|
||||
fullName: event.fullName,
|
||||
mail: isEmail(event.login) ? event.login : "",
|
||||
phoneNumber: isPhoneNumber(event.login) ? event.login : "",
|
||||
fromLoginPage: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
part of 'sign_up_bloc.dart';
|
||||
|
||||
sealed class SignUpEvent extends Equatable {
|
||||
const SignUpEvent();
|
||||
}
|
||||
|
||||
final class PasswordVisibilityEvent extends SignUpEvent {
|
||||
const PasswordVisibilityEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class OnInputEnterEvent extends SignUpEvent {
|
||||
final String login;
|
||||
final String fullName;
|
||||
|
||||
const OnInputEnterEvent({required this.login, required this.fullName});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [login, fullName];
|
||||
}
|
||||
|
||||
final class SubmitEvent extends SignUpEvent {
|
||||
final String login;
|
||||
final String fullName;
|
||||
|
||||
const SubmitEvent({required this.login, required this.fullName});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [login, fullName];
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
part of 'sign_up_bloc.dart';
|
||||
|
||||
class SignUpState extends Equatable {
|
||||
const SignUpState({
|
||||
required this.passwordHidden,
|
||||
required this.isLoading,
|
||||
required this.isButtonEnabled,
|
||||
});
|
||||
|
||||
final bool passwordHidden;
|
||||
final bool isLoading;
|
||||
final bool isButtonEnabled;
|
||||
|
||||
SignUpState copyWith({
|
||||
bool? passwordHidden,
|
||||
bool? isLoading,
|
||||
bool? isButtonEnabled,
|
||||
}) {
|
||||
return SignUpState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
passwordHidden: passwordHidden ?? this.passwordHidden,
|
||||
isButtonEnabled: isButtonEnabled ?? this.isButtonEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [passwordHidden, isLoading, isButtonEnabled];
|
||||
}
|
||||
185
lib/features/auth/presentation/pages/auth/auth_page.dart
Normal file
185
lib/features/auth/presentation/pages/auth/auth_page.dart
Normal file
@@ -0,0 +1,185 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../../../../../core/widgets/loading/custom_loading.dart';
|
||||
import '../../../../../core/widgets/text_filds/custom_text_field_name.dart';
|
||||
import '../../../../../router/name_routes.dart';
|
||||
import '../../bloc/auth/auth_bloc.dart';
|
||||
import '../mixin/auth_mixin.dart';
|
||||
|
||||
class AuthPage extends StatefulWidget {
|
||||
const AuthPage({super.key});
|
||||
|
||||
@override
|
||||
State<AuthPage> createState() => _AuthPageState();
|
||||
}
|
||||
|
||||
class _AuthPageState extends State<AuthPage> with AuthMixin {
|
||||
@override
|
||||
void initState() {
|
||||
initControllers();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: ListView(
|
||||
padding: AppUtils.kPaddingHor16,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||
child: SvgPicture.asset("assets/svg/ic_logo_auth.svg"),
|
||||
),
|
||||
AppUtils.kBoxHeight48,
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius12,
|
||||
color: context.color.iconBackground,
|
||||
),
|
||||
padding: AppUtils.kPaddingAll6,
|
||||
child: SvgPicture.asset("assets/svg/ic_user.svg"),
|
||||
),
|
||||
AppUtils.kBoxWidth12,
|
||||
Text(
|
||||
AppLocalization.current.auth_login,
|
||||
style: context.text.authTitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight32,
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.enter_phone_or_mail,
|
||||
inputType: TextInputType.text,
|
||||
name: AppLocalization.current.phone,
|
||||
controller: loginController,
|
||||
onchange: (value) {
|
||||
context.read<AuthBloc>().add(
|
||||
OnInputEnterEvent(
|
||||
login: value,
|
||||
password: passwordController.text,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
CustomTextFieldName(
|
||||
hint: "********",
|
||||
prefixWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"CT-",
|
||||
style: context.text.profileCategory.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
inputType: TextInputType.number,
|
||||
name: AppLocalization.current.password,
|
||||
obscureText: state.passwordHidden,
|
||||
maxLines: 1,
|
||||
controller: passwordController,
|
||||
suffix: IconButton(
|
||||
onPressed: () {
|
||||
context.read<AuthBloc>().add(
|
||||
const PasswordVisibilityEvent(),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
state.passwordHidden
|
||||
? Icons.visibility_off_rounded
|
||||
: Icons.visibility_rounded,
|
||||
),
|
||||
),
|
||||
onchange: (value) {
|
||||
context.read<AuthBloc>().add(
|
||||
OnInputEnterEvent(
|
||||
login: loginController.text,
|
||||
password: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight48,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: ElevatedButton(
|
||||
onPressed: state.isButtonEnabled && !state.isLoading
|
||||
? () {
|
||||
context.read<AuthBloc>().add(
|
||||
SubmitEvent(
|
||||
login: loginController.text,
|
||||
password: "CT-${passwordController.text}",
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: state.isLoading
|
||||
? const CustomLoadingWidget()
|
||||
: Text(AppLocalization.current.auth),
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Text(
|
||||
AppLocalization.current.no_account,
|
||||
style: context.text.authDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, Routes.signUp);
|
||||
},
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: context.color.scaffoldBackgroundColor,
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
border: Border.all(color: context.color.primaryColor),
|
||||
),
|
||||
height: 56,
|
||||
padding: AppUtils.kPaddingHor16,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalization.current.sign_up,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: context.color.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class AuthConfirmArgument {
|
||||
final String? fullName;
|
||||
final String? mail;
|
||||
final String? phoneNumber;
|
||||
final bool fromLoginPage;
|
||||
final String? password;
|
||||
|
||||
AuthConfirmArgument({
|
||||
this.fullName,
|
||||
required this.mail,
|
||||
required this.phoneNumber,
|
||||
required this.fromLoginPage,
|
||||
this.password,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/custom_loading.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/presentation/pages/auth_confirm/timer_widget.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:cargocalculaterapp/router/name_routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
import '../../../../../core/theme/app_text_styles.dart';
|
||||
import '../../../../../core/theme/colors/app_colors.dart';
|
||||
import '../../bloc/auth_confirm/auth_confirm_bloc.dart';
|
||||
import '../mixin/auth_confirm_mixin.dart';
|
||||
import 'argument/auth_confirm_argument.dart';
|
||||
|
||||
class AuthConfirmPage extends StatefulWidget {
|
||||
const AuthConfirmPage({super.key, required this.argument});
|
||||
|
||||
final AuthConfirmArgument? argument;
|
||||
|
||||
@override
|
||||
State<AuthConfirmPage> createState() => _AuthConfirmPageState();
|
||||
}
|
||||
|
||||
class _AuthConfirmPageState extends State<AuthConfirmPage>
|
||||
with AuthConfirmMixin {
|
||||
@override
|
||||
void initState() {
|
||||
initControllers();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<AuthConfirmBloc, AuthConfirmState>(
|
||||
listener: (context, state) {
|
||||
if (state is SuccessSate) {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
Routes.main,
|
||||
(route) => route.isFirst,
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Padding(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 64,
|
||||
height: 64,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.iconBackground,
|
||||
),
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svg/ic_mail.svg",
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
(widget.argument?.mail?.isNotEmpty ?? false)
|
||||
? AppLocalization.current.confirm_email
|
||||
: AppLocalization.current.confirm_phone_text,
|
||||
style: context.text.authTitle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
(widget.argument?.mail?.isNotEmpty ?? false)
|
||||
? AppLocalization.current.enter_code_mail(
|
||||
widget.argument?.mail ?? "",
|
||||
)
|
||||
: AppLocalization.current.enter_code_phone(
|
||||
widget.argument?.phoneNumber ?? "",
|
||||
),
|
||||
style: context.text.authDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight48,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 296,
|
||||
child: PinCodeTextField(
|
||||
controller: confirmController,
|
||||
appContext: context,
|
||||
autoDisposeControllers: false,
|
||||
length: 4,
|
||||
animationType: AnimationType.scale,
|
||||
enabled: true,
|
||||
enablePinAutofill: true,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
onChanged: (code) {
|
||||
context.read<AuthConfirmBloc>().add(
|
||||
SmsCodeEnterEvent(code),
|
||||
);
|
||||
},
|
||||
autoFocus: true,
|
||||
keyboardType: const TextInputType.numberWithOptions(),
|
||||
showCursor: true,
|
||||
textStyle: context.text.bigTitle,
|
||||
cursorColor: context.color.accentColor,
|
||||
cursorHeight: 20,
|
||||
enableActiveFill: true,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
pinTheme: PinTheme(
|
||||
activeColor:
|
||||
state is ErrorState
|
||||
? ThemeColors.timerRed
|
||||
: state is SuccessSate
|
||||
? ThemeColors.successInputSMS
|
||||
: context.color.borderColor,
|
||||
selectedFillColor:
|
||||
context.color.scaffoldBackgroundColor,
|
||||
activeFillColor:
|
||||
context.color.scaffoldBackgroundColor,
|
||||
shape: PinCodeFieldShape.box,
|
||||
inactiveFillColor:
|
||||
context.color.scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(12),
|
||||
),
|
||||
borderWidth: 1,
|
||||
inactiveColor:
|
||||
state is ErrorState
|
||||
? ThemeColors.timerRed
|
||||
: state is SuccessSate
|
||||
? ThemeColors.successInputSMS
|
||||
: context.color.borderColor,
|
||||
fieldWidth: 56,
|
||||
fieldHeight: 64,
|
||||
selectedColor:
|
||||
state is ErrorState
|
||||
? ThemeColors.timerRed
|
||||
: state is SuccessSate
|
||||
? ThemeColors.successInputSMS
|
||||
: context.color.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state is ErrorState) AppUtils.kBoxHeight16,
|
||||
if (state is ErrorState)
|
||||
Center(
|
||||
child: Text(
|
||||
AppLocalization.current.incorrect_code,
|
||||
style: AppTextStyles.timerBlue,
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight24,
|
||||
if (state.isTimerVisible)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalization.current.send_sms,
|
||||
style: AppTextStyles.timerBlue,
|
||||
),
|
||||
const TimerWidget(),
|
||||
],
|
||||
),
|
||||
if (!state.isTimerVisible)
|
||||
Center(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (widget.argument != null) {
|
||||
context.read<AuthConfirmBloc>().add(
|
||||
ResendCodeEvent(argument: widget.argument!),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalization.current.send_sms,
|
||||
style: AppTextStyles.timerBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight48,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: ElevatedButton(
|
||||
onPressed:
|
||||
(confirmController.text.length == 4 &&
|
||||
state is! LoadingState)
|
||||
? () {
|
||||
context.read<AuthConfirmBloc>().add(
|
||||
OnSubmitEvent(
|
||||
code: confirmController.text,
|
||||
argument: widget.argument,
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child:
|
||||
state is LoadingState
|
||||
? const CustomLoadingWidget()
|
||||
: Text(AppLocalization.current.confirm),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeController();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'dart:async';
|
||||
import 'package:cargocalculaterapp/core/theme/app_text_styles.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../bloc/auth_confirm/auth_confirm_bloc.dart';
|
||||
|
||||
class TimerWidget extends StatefulWidget {
|
||||
const TimerWidget({super.key});
|
||||
|
||||
@override
|
||||
State<TimerWidget> createState() => _TimerWidgetState();
|
||||
}
|
||||
|
||||
class _TimerWidgetState extends State<TimerWidget> {
|
||||
Timer? timer;
|
||||
int time = 60;
|
||||
String timeText = "60";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (timer?.isActive ?? false) {
|
||||
timer?.cancel();
|
||||
}
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) async {
|
||||
time--;
|
||||
if (time == 0) {
|
||||
timer.cancel();
|
||||
context.read<AuthConfirmBloc>().add(
|
||||
const TimerChangedEvent(isVisible: false),
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
timeText = time.toString().padLeft(2, "0");
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(" 00:$timeText", style: AppTextStyles.timerStyle);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
mixin AuthConfirmMixin {
|
||||
late TextEditingController confirmController;
|
||||
|
||||
void initControllers() {
|
||||
confirmController = TextEditingController();
|
||||
}
|
||||
|
||||
void disposeController() {
|
||||
confirmController.dispose();
|
||||
}
|
||||
}
|
||||
16
lib/features/auth/presentation/pages/mixin/auth_mixin.dart
Normal file
16
lib/features/auth/presentation/pages/mixin/auth_mixin.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
mixin AuthMixin {
|
||||
late TextEditingController loginController;
|
||||
late TextEditingController passwordController;
|
||||
|
||||
void initControllers() {
|
||||
loginController = TextEditingController();
|
||||
passwordController = TextEditingController();
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
loginController.dispose();
|
||||
passwordController.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
mixin SignUpMixin {
|
||||
late TextEditingController loginController;
|
||||
late TextEditingController fullNameController;
|
||||
|
||||
void initControllers() {
|
||||
loginController = TextEditingController();
|
||||
fullNameController = TextEditingController();
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
loginController.dispose();
|
||||
fullNameController.dispose();
|
||||
}
|
||||
}
|
||||
158
lib/features/auth/presentation/pages/sign_up/sign_up_page.dart
Normal file
158
lib/features/auth/presentation/pages/sign_up/sign_up_page.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/features/auth/presentation/pages/mixin/sign_up_mixin.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../../../../core/utils/app_utils.dart';
|
||||
import '../../../../../core/widgets/loading/custom_loading.dart';
|
||||
import '../../../../../core/widgets/text_filds/custom_text_field_name.dart';
|
||||
import '../../../../../generated/l10n.dart';
|
||||
import '../../bloc/sign_up/sign_up_bloc.dart';
|
||||
|
||||
class SignUpPage extends StatefulWidget {
|
||||
const SignUpPage({super.key});
|
||||
|
||||
@override
|
||||
State<SignUpPage> createState() => _SignUpPageState();
|
||||
}
|
||||
|
||||
class _SignUpPageState extends State<SignUpPage> with SignUpMixin {
|
||||
@override
|
||||
void initState() {
|
||||
initControllers();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpBloc, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: ListView(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
width: 64,
|
||||
height: 64,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.iconBackground,
|
||||
),
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svg/ic_add_user.svg",
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Text(
|
||||
AppLocalization.current.sign_up,
|
||||
style: context.text.authTitle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
AppUtils.kBoxHeight24,
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.full_name_hint,
|
||||
inputType: TextInputType.name,
|
||||
name: AppLocalization.current.full_name,
|
||||
controller: fullNameController,
|
||||
onchange: (value) {
|
||||
context.read<SignUpBloc>().add(
|
||||
OnInputEnterEvent(
|
||||
login: loginController.text,
|
||||
fullName: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.enter_phone_or_mail,
|
||||
inputType: TextInputType.text,
|
||||
name: AppLocalization.current.phone,
|
||||
controller: loginController,
|
||||
onchange: (value) {
|
||||
context.read<SignUpBloc>().add(
|
||||
OnInputEnterEvent(
|
||||
login: value,
|
||||
fullName: fullNameController.text,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight48,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: ElevatedButton(
|
||||
onPressed:
|
||||
state.isButtonEnabled && !state.isLoading
|
||||
? () {
|
||||
context.read<SignUpBloc>().add(
|
||||
SubmitEvent(
|
||||
login: loginController.text,
|
||||
fullName: fullNameController.text,
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child:
|
||||
state.isLoading
|
||||
? const CustomLoadingWidget()
|
||||
: Text(AppLocalization.current.sign_up),
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Text(
|
||||
AppLocalization.current.has_account,
|
||||
style: context.text.authDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: context.color.scaffoldBackgroundColor,
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
border: Border.all(color: context.color.primaryColor),
|
||||
),
|
||||
height: 56,
|
||||
padding: AppUtils.kPaddingHor16,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalization.current.auth,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: context.color.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
|
||||
|
||||
abstract class CalculatorRemoteDataSource {
|
||||
Future<CalculatePriceResponse> priceCalculate(PriceCalculateRequest request);
|
||||
Future<LeadCreateResponse> createLead(LeadCreateRequest request);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../../../../constants/constants.dart';
|
||||
import '../../../../core/error/exceptions.dart';
|
||||
import '../../../../core/local_source/local_source.dart';
|
||||
import '../../../../injector_container.dart';
|
||||
import '../model/lead_create_response.dart';
|
||||
import '../model/lead_create_request.dart';
|
||||
import 'calculator_remote_data_source.dart';
|
||||
|
||||
class CalculatorRemoteDataSourceImpl extends CalculatorRemoteDataSource {
|
||||
final Dio dio;
|
||||
|
||||
CalculatorRemoteDataSourceImpl(this.dio);
|
||||
|
||||
@override
|
||||
Future<CalculatePriceResponse> priceCalculate(
|
||||
PriceCalculateRequest request,
|
||||
) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
Constants.baseUrl + Urls.calculatePrice,
|
||||
options:
|
||||
DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return CalculatePriceResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LeadCreateResponse> createLead(LeadCreateRequest request) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
Constants.baseUrl + Urls.lead,
|
||||
options:
|
||||
DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return LeadCreateResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class CalculatePriceResponse {
|
||||
CalculatePriceResponse({this.price});
|
||||
|
||||
CalculatePriceResponse.fromJson(dynamic json) {
|
||||
price = double.tryParse(json['price'].toString());
|
||||
}
|
||||
|
||||
double? price;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['price'] = price;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
73
lib/features/calculator/data/model/lead_create_request.dart
Normal file
73
lib/features/calculator/data/model/lead_create_request.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
class LeadCreateRequest {
|
||||
LeadCreateRequest({
|
||||
this.warehouseCode,
|
||||
this.wharehouseUz,
|
||||
this.wharehouseRu,
|
||||
this.wharehouseZh,
|
||||
this.nameUz,
|
||||
this.nameRu,
|
||||
this.nameZh,
|
||||
this.userUcode,
|
||||
this.phoneNumber,
|
||||
this.gmail,
|
||||
this.weight,
|
||||
this.price,
|
||||
this.status,
|
||||
this.averageWeightKg,
|
||||
this.m3,
|
||||
});
|
||||
|
||||
LeadCreateRequest.fromJson(dynamic json) {
|
||||
warehouseCode = json['warehouse_code'];
|
||||
wharehouseUz = json['wharehouseUz'];
|
||||
wharehouseRu = json['wharehouseRu'];
|
||||
wharehouseZh = json['wharehouseZh'];
|
||||
nameUz = json['nameUz'];
|
||||
nameRu = json['nameRu'];
|
||||
nameZh = json['nameZh'];
|
||||
userUcode = json['user_ucode'];
|
||||
phoneNumber = json['phone_number'];
|
||||
gmail = json['gmail'];
|
||||
weight = json['weight'];
|
||||
price = json['price'];
|
||||
status = json['status'];
|
||||
averageWeightKg = json['average_weight_kg'];
|
||||
m3 = json['status'];
|
||||
}
|
||||
|
||||
int? warehouseCode;
|
||||
String? wharehouseUz;
|
||||
String? wharehouseRu;
|
||||
String? wharehouseZh;
|
||||
String? nameUz;
|
||||
String? nameRu;
|
||||
String? nameZh;
|
||||
String? userUcode;
|
||||
String? phoneNumber;
|
||||
String? gmail;
|
||||
String? weight;
|
||||
String? price;
|
||||
String? status;
|
||||
String? averageWeightKg;
|
||||
String? m3;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['warehouse_code'] = warehouseCode;
|
||||
map['wharehouseUz'] = wharehouseUz;
|
||||
map['wharehouseRu'] = wharehouseRu;
|
||||
map['wharehouseZh'] = wharehouseZh;
|
||||
map['nameUz'] = nameUz;
|
||||
map['nameRu'] = nameRu;
|
||||
map['nameZh'] = nameZh;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['gmail'] = gmail;
|
||||
map['weight'] = weight;
|
||||
map['price'] = price;
|
||||
map['status'] = status;
|
||||
map['average_weight_kg'] = averageWeightKg;
|
||||
map['m3'] = m3;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
118
lib/features/calculator/data/model/lead_create_response.dart
Normal file
118
lib/features/calculator/data/model/lead_create_response.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
class LeadCreateResponse {
|
||||
LeadCreateResponse({
|
||||
this.userUcode,
|
||||
this.phoneNumber,
|
||||
this.gmail,
|
||||
this.weight,
|
||||
this.status,
|
||||
this.nameUz,
|
||||
this.nameRu,
|
||||
this.nameZh,
|
||||
this.wharehouseUz,
|
||||
this.wharehouseRu,
|
||||
this.wharehouseZh,
|
||||
this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
LeadCreateResponse.fromJson(dynamic json) {
|
||||
userUcode = json['user_ucode'];
|
||||
phoneNumber = json['phone_number'];
|
||||
gmail = json['gmail'];
|
||||
weight = json['weight'];
|
||||
status = json['status'] != null ? Status.fromJson(json['status']) : null;
|
||||
nameUz = json['nameUz'];
|
||||
nameRu = json['nameRu'];
|
||||
nameZh = json['nameZh'];
|
||||
wharehouseUz = json['wharehouseUz'];
|
||||
wharehouseRu = json['wharehouseRu'];
|
||||
wharehouseZh = json['wharehouseZh'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
id = json['id'];
|
||||
}
|
||||
|
||||
String? userUcode;
|
||||
String? phoneNumber;
|
||||
String? gmail;
|
||||
String? weight;
|
||||
Status? status;
|
||||
String? nameUz;
|
||||
String? nameRu;
|
||||
String? nameZh;
|
||||
String? wharehouseUz;
|
||||
String? wharehouseRu;
|
||||
String? wharehouseZh;
|
||||
String? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['user_ucode'] = userUcode;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['gmail'] = gmail;
|
||||
map['weight'] = weight;
|
||||
if (status != null) {
|
||||
map['status'] = status?.toJson();
|
||||
}
|
||||
map['nameUz'] = nameUz;
|
||||
map['nameRu'] = nameRu;
|
||||
map['nameZh'] = nameZh;
|
||||
map['wharehouseUz'] = wharehouseUz;
|
||||
map['wharehouseRu'] = wharehouseRu;
|
||||
map['wharehouseZh'] = wharehouseZh;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
|
||||
map['id'] = id;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Status {
|
||||
Status({this.code, this.translations});
|
||||
|
||||
Status.fromJson(dynamic json) {
|
||||
code = json['code'];
|
||||
translations =
|
||||
json['translations'] != null
|
||||
? Translations.fromJson(json['translations'])
|
||||
: null;
|
||||
}
|
||||
|
||||
String? code;
|
||||
Translations? translations;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['code'] = code;
|
||||
if (translations != null) {
|
||||
map['translations'] = translations?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Translations {
|
||||
Translations({this.uz, this.ru, this.zh});
|
||||
|
||||
Translations.fromJson(dynamic json) {
|
||||
uz = json['uz'];
|
||||
ru = json['ru'];
|
||||
zh = json['zh'];
|
||||
}
|
||||
|
||||
String? uz;
|
||||
String? ru;
|
||||
String? zh;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['uz'] = uz;
|
||||
map['ru'] = ru;
|
||||
map['zh'] = zh;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
class PriceCalculateRequest {
|
||||
PriceCalculateRequest({
|
||||
this.avg,
|
||||
this.m3,});
|
||||
|
||||
PriceCalculateRequest.fromJson(dynamic json) {
|
||||
avg = json['avg'];
|
||||
m3 = json['m3'];
|
||||
}
|
||||
double? avg;
|
||||
double? m3;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['avg'] = avg;
|
||||
map['m3'] = m3;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/domain/repository/calculator_repository.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../data_source/calculator_remote_data_source.dart';
|
||||
|
||||
class CalculatorRepositoryImpl extends CalculatorRepository {
|
||||
final CalculatorRemoteDataSource calculatorRemoteDataSource;
|
||||
|
||||
CalculatorRepositoryImpl(this.calculatorRemoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, CalculatePriceResponse>> calculatePrice(
|
||||
PriceCalculateRequest request,
|
||||
) async {
|
||||
try {
|
||||
final response = await calculatorRemoteDataSource.priceCalculate(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LeadCreateResponse>> createLead(LeadCreateRequest request) async {
|
||||
try {
|
||||
final response = await calculatorRemoteDataSource.createLead(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failure.dart';
|
||||
|
||||
abstract class CalculatorRepository {
|
||||
Future<Either<Failure, CalculatePriceResponse>> calculatePrice(
|
||||
PriceCalculateRequest request,
|
||||
);
|
||||
Future<Either<Failure, LeadCreateResponse>> createLead(
|
||||
LeadCreateRequest request,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/calculate_price_response.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/price_calculate_request.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/calculator_repository.dart';
|
||||
|
||||
class CalculatePriceUseCase
|
||||
extends UseCase<CalculatePriceResponse, PriceCalculateRequest> {
|
||||
final CalculatorRepository repository;
|
||||
|
||||
CalculatePriceUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, CalculatePriceResponse>> call(
|
||||
PriceCalculateRequest params,
|
||||
) async {
|
||||
return await repository.calculatePrice(params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_request.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/data/model/lead_create_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/calculator_repository.dart';
|
||||
|
||||
class CreateLeadUseCase
|
||||
extends UseCase<LeadCreateResponse, LeadCreateRequest> {
|
||||
final CalculatorRepository repository;
|
||||
|
||||
CreateLeadUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LeadCreateResponse>> call(
|
||||
LeadCreateRequest params,
|
||||
) async {
|
||||
return await repository.createLead(params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class CalculatorInfoArgument {
|
||||
final String productName;
|
||||
final String weraHouse;
|
||||
final double weight;
|
||||
final double size;
|
||||
final double averageWeight;
|
||||
final double deliveryPrice;
|
||||
|
||||
CalculatorInfoArgument({
|
||||
required this.productName,
|
||||
required this.weraHouse,
|
||||
required this.weight,
|
||||
required this.size,
|
||||
required this.averageWeight,
|
||||
required this.deliveryPrice,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import 'package:cargocalculaterapp/router/app_routes.dart';
|
||||
import 'package:cargocalculaterapp/router/name_routes.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../data/model/price_calculate_request.dart';
|
||||
import '../../domain/usecase/calculate_price_usecase.dart';
|
||||
import '../arguments/calculator_info_argument.dart';
|
||||
|
||||
part 'calculator_event.dart';
|
||||
|
||||
part 'calculator_state.dart';
|
||||
|
||||
class CalculatorBloc extends Bloc<CalculatorEvent, CalculatorState> {
|
||||
CalculatorBloc(this.calculatePriceUseCase)
|
||||
: super(const CalculatorState(isLoading: false)) {
|
||||
on<WeightSizeEnterEvent>(_weightSizeEntered);
|
||||
on<WareHouseSelectEvent>(_wareHouseSelect);
|
||||
on<CalculateEvent>(_calculate);
|
||||
}
|
||||
|
||||
final CalculatePriceUseCase calculatePriceUseCase;
|
||||
|
||||
|
||||
void _weightSizeEntered(
|
||||
WeightSizeEnterEvent event,
|
||||
Emitter<CalculatorState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
size: double.tryParse(event.size.replaceAll(",", "")),
|
||||
weight: double.tryParse(event.weight.replaceAll(",", "")),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _wareHouseSelect(
|
||||
WareHouseSelectEvent event,
|
||||
Emitter<CalculatorState> emit,
|
||||
) {
|
||||
emit(state.copyWith(selectedWarehouse: event.wareHouse));
|
||||
}
|
||||
|
||||
Future<void> _calculate(
|
||||
CalculateEvent event,
|
||||
Emitter<CalculatorState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final response = await calculatePriceUseCase(
|
||||
PriceCalculateRequest(
|
||||
m3: state.size,
|
||||
avg: (state.weight ?? 0) / (state.size ?? 1),
|
||||
),
|
||||
);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
},
|
||||
(r) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
Navigator.pushNamed(
|
||||
rootNavigatorKey.currentContext!,
|
||||
Routes.calculationInfo,
|
||||
arguments: CalculatorInfoArgument(
|
||||
productName: event.productName,
|
||||
deliveryPrice: r.price ?? 0,
|
||||
size: state.size ?? 0,
|
||||
averageWeight: (state.weight ?? 0) / (state.size ?? 1),
|
||||
weight: state.weight ?? 0,
|
||||
weraHouse: state.selectedWarehouse ?? "",
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
part of 'calculator_bloc.dart';
|
||||
|
||||
sealed class CalculatorEvent extends Equatable {
|
||||
const CalculatorEvent();
|
||||
}
|
||||
|
||||
final class WeightSizeEnterEvent extends CalculatorEvent {
|
||||
final String weight;
|
||||
final String size;
|
||||
|
||||
const WeightSizeEnterEvent({required this.weight, required this.size});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [weight, size];
|
||||
}
|
||||
|
||||
final class WareHouseSelectEvent extends CalculatorEvent {
|
||||
final String wareHouse;
|
||||
|
||||
const WareHouseSelectEvent({required this.wareHouse});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [wareHouse];
|
||||
}
|
||||
|
||||
final class CalculateEvent extends CalculatorEvent {
|
||||
const CalculateEvent({required this.productName});
|
||||
|
||||
final String productName;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [productName];
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../generated/l10n.dart';
|
||||
import '../../../../../router/app_routes.dart';
|
||||
import '../../../../../router/name_routes.dart';
|
||||
import '../../../data/model/lead_create_request.dart';
|
||||
import '../../../domain/usecase/create_lead_usecase.dart';
|
||||
import '../../arguments/calculator_info_argument.dart';
|
||||
import '../../pages/calculator_info/dialog/order_dialog.dart';
|
||||
|
||||
part 'calculator_info_event.dart';
|
||||
|
||||
part 'calculator_info_state.dart';
|
||||
|
||||
class CalculatorInfoBloc
|
||||
extends Bloc<CalculatorInfoEvent, CalculatorInfoState> {
|
||||
CalculatorInfoBloc(this.createLeadUseCase)
|
||||
: super(const CalculatorInfoState(isLoading: false)) {
|
||||
on<CreateLeadEvent>(_createLead);
|
||||
on<ShowSuccessDialogEvent>(_showSuccessDialog);
|
||||
}
|
||||
|
||||
final CreateLeadUseCase createLeadUseCase;
|
||||
|
||||
Future<void> _createLead(
|
||||
CreateLeadEvent event,
|
||||
Emitter<CalculatorInfoState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final local = Localizations.localeOf(
|
||||
rootNavigatorKey.currentContext!,
|
||||
).languageCode;
|
||||
final response = await createLeadUseCase(
|
||||
LeadCreateRequest(
|
||||
status: "NOT_CONTACTED",
|
||||
m3: "${event.argument.size}",
|
||||
averageWeightKg: "${event.argument.averageWeight}",
|
||||
weight: "${event.argument.weight}",
|
||||
nameRu: local == "ru" ? event.argument.productName : "",
|
||||
nameUz: local == "uz" ? event.argument.productName : "",
|
||||
nameZh: local == "zh" ? event.argument.productName : "",
|
||||
price: "${event.argument.deliveryPrice}",
|
||||
warehouseCode: int.tryParse(event.argument.weraHouse),
|
||||
wharehouseRu: AppConst.warehouseOptions[event.argument.weraHouse]?.ru,
|
||||
wharehouseUz: AppConst.warehouseOptions[event.argument.weraHouse]?.uz,
|
||||
wharehouseZh: AppConst.warehouseOptions[event.argument.weraHouse]?.zh,
|
||||
),
|
||||
);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
},
|
||||
(r) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
add(ShowSuccessDialogEvent(isCall: event.isCall));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showSuccessDialog(
|
||||
ShowSuccessDialogEvent event,
|
||||
Emitter<CalculatorInfoState> emit,
|
||||
) {
|
||||
if (event.isCall) {
|
||||
showCupertinoModalPopup(
|
||||
context: rootNavigatorKey.currentContext!,
|
||||
builder: (context) => CupertinoActionSheet(
|
||||
actions: [
|
||||
CupertinoActionSheetAction(
|
||||
child: Text(
|
||||
"+998 99-110-22-22",
|
||||
style: TextStyle(color: context.color.primaryColor),
|
||||
),
|
||||
onPressed: () async {
|
||||
final Uri url = Uri(scheme: 'tel', path: "+998991102222");
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url);
|
||||
}
|
||||
Navigator.pop(rootNavigatorKey.currentContext!, true);
|
||||
},
|
||||
),
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
onPressed: () {
|
||||
Navigator.pop(rootNavigatorKey.currentContext!);
|
||||
},
|
||||
child: Text(
|
||||
AppLocalization.current.cancel,
|
||||
style: TextStyle(color: context.color.textColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
).then((value) {
|
||||
if (value is bool) {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
rootNavigatorKey.currentContext!,
|
||||
Routes.main,
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showDialog(
|
||||
context: rootNavigatorKey.currentContext!,
|
||||
builder: (context) => OrderDialog(isCall: event.isCall),
|
||||
).then((value) {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
rootNavigatorKey.currentContext!,
|
||||
Routes.main,
|
||||
(route) => false,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
part of 'calculator_info_bloc.dart';
|
||||
|
||||
sealed class CalculatorInfoEvent extends Equatable {
|
||||
const CalculatorInfoEvent();
|
||||
}
|
||||
|
||||
final class CreateLeadEvent extends CalculatorInfoEvent {
|
||||
const CreateLeadEvent({required this.argument, required this.isCall});
|
||||
|
||||
final bool isCall;
|
||||
final CalculatorInfoArgument argument;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [argument, isCall];
|
||||
}
|
||||
|
||||
final class ShowSuccessDialogEvent extends CalculatorInfoEvent {
|
||||
const ShowSuccessDialogEvent({required this.isCall});
|
||||
|
||||
final bool isCall;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isCall];
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
part of 'calculator_info_bloc.dart';
|
||||
|
||||
class CalculatorInfoState extends Equatable {
|
||||
const CalculatorInfoState({required this.isLoading});
|
||||
|
||||
final bool isLoading;
|
||||
|
||||
CalculatorInfoState copyWith({bool? isLoading}) {
|
||||
return CalculatorInfoState(isLoading: isLoading ?? this.isLoading);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isLoading];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
part of 'calculator_bloc.dart';
|
||||
|
||||
class CalculatorState extends Equatable {
|
||||
const CalculatorState({
|
||||
required this.isLoading,
|
||||
this.weight,
|
||||
this.size,
|
||||
this.selectedWarehouse,
|
||||
});
|
||||
|
||||
final bool isLoading;
|
||||
final double? weight;
|
||||
final double? size;
|
||||
final String? selectedWarehouse;
|
||||
|
||||
CalculatorState copyWith({
|
||||
bool? isLoading,
|
||||
double? weight,
|
||||
double? size,
|
||||
String? selectedWarehouse,
|
||||
}) {
|
||||
return CalculatorState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
weight: weight ?? this.weight,
|
||||
size: size ?? this.size,
|
||||
selectedWarehouse: selectedWarehouse ?? this.selectedWarehouse,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isLoading, weight, size, selectedWarehouse];
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
mixin CalculatorMixin{
|
||||
late TextEditingController weightController;
|
||||
late TextEditingController sizeController;
|
||||
late TextEditingController averageWeightController;
|
||||
late TextEditingController productNameController;
|
||||
|
||||
void initControllers(){
|
||||
weightController=TextEditingController();
|
||||
sizeController=TextEditingController();
|
||||
averageWeightController=TextEditingController();
|
||||
productNameController=TextEditingController();
|
||||
}
|
||||
|
||||
void disposeController(){
|
||||
weightController.dispose();
|
||||
sizeController.dispose();
|
||||
averageWeightController.dispose();
|
||||
productNameController.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/progress_hud.dart';
|
||||
import 'package:cargocalculaterapp/features/calculator/presentation/pages/calculator_info/widget/calculator_info_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../generated/l10n.dart';
|
||||
import '../../arguments/calculator_info_argument.dart';
|
||||
import '../../bloc/calculator_info/calculator_info_bloc.dart';
|
||||
|
||||
class CalculatorInfoPage extends StatelessWidget {
|
||||
const CalculatorInfoPage({super.key, required this.argument});
|
||||
|
||||
final CalculatorInfoArgument? argument;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<CalculatorInfoBloc, CalculatorInfoState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
backgroundColor: context.color.grayBackground,
|
||||
appBar: AppBar(title: Text(AppLocalization.current.calculator)),
|
||||
body: ModalProgressHUD(
|
||||
inAsyncCall: state.isLoading,
|
||||
child: ListView(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 32),
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.statusBackground,
|
||||
border: Border.all(color: context.color.lightBorder),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CalculatorInfoWidget(
|
||||
title: AppLocalization.current.product_name,
|
||||
name: argument?.productName ?? "-",
|
||||
),
|
||||
CalculatorInfoWidget(
|
||||
title: AppLocalization.current.warehouse,
|
||||
name: Functions.getTranslatedItem(
|
||||
AppConst.warehouseOptions[argument?.weraHouse],
|
||||
context,
|
||||
),
|
||||
),
|
||||
CalculatorInfoWidget(
|
||||
title: AppLocalization.current.weight,
|
||||
name: "${argument?.weight} kg",
|
||||
),
|
||||
CalculatorInfoWidget(
|
||||
title: AppLocalization.current.size,
|
||||
name: "${argument?.size} m³",
|
||||
),
|
||||
CalculatorInfoWidget(
|
||||
title: AppLocalization.current.average_weight,
|
||||
name: "${argument?.averageWeight}",
|
||||
),
|
||||
CalculatorInfoWidget(
|
||||
title: AppLocalization.current.delivery_price,
|
||||
name: "${argument?.deliveryPrice}",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (argument != null) {
|
||||
context.read<CalculatorInfoBloc>().add(
|
||||
CreateLeadEvent(
|
||||
isCall: true,
|
||||
argument: argument!,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: context.color.scaffoldBackgroundColor,
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
border: Border.all(
|
||||
color: context.color.primaryColor,
|
||||
),
|
||||
),
|
||||
height: 56,
|
||||
padding: AppUtils.kPaddingHor16,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/svg/ic_phone.svg",
|
||||
colorFilter: ColorFilter.mode(
|
||||
context.color.textColor,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxWidth4,
|
||||
Text(
|
||||
AppLocalization.current.phone_call,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: context.color.textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxWidth16,
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (argument != null) {
|
||||
context.read<CalculatorInfoBloc>().add(
|
||||
CreateLeadEvent(
|
||||
isCall: false,
|
||||
argument: argument!,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset("assets/svg/ic_list_1.svg"),
|
||||
AppUtils.kBoxWidth4,
|
||||
Text(AppLocalization.current.send),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class OrderDialog extends StatelessWidget {
|
||||
const OrderDialog({super.key, required this.isCall});
|
||||
|
||||
final bool isCall;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: context.color.scaffoldBackgroundColor,
|
||||
insetPadding: AppUtils.kPaddingAll16,
|
||||
child: Padding(
|
||||
padding: AppUtils.kPaddingVer24Hor16,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset("assets/svg/ic_done.svg"),
|
||||
AppUtils.kBoxHeight20,
|
||||
Text(
|
||||
isCall
|
||||
? AppLocalization.current.done_phone
|
||||
: AppLocalization.current.done_order,
|
||||
style: context.text.authDesc.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
AppUtils.kBoxHeight20,
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: Text(AppLocalization.current.done_ready),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../../core/utils/app_utils.dart';
|
||||
|
||||
class CalculatorInfoWidget extends StatelessWidget {
|
||||
const CalculatorInfoWidget({super.key, required this.title, required this.name});
|
||||
|
||||
final String title;
|
||||
final String name;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Text(title, style: context.text.orderListTitle)),
|
||||
AppUtils.kBoxWidth8,
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
style: context.text.profileCategory,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
146
lib/features/calculator/presentation/pages/calculator_page.dart
Normal file
146
lib/features/calculator/presentation/pages/calculator_page.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/progress_hud.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../../core/widgets/drop_down/custom_drop_down.dart';
|
||||
import '../../../../core/widgets/text_filds/custom_text_field_name.dart';
|
||||
import '../bloc/calculator_bloc.dart';
|
||||
import '../mixin/calculator_mixin.dart';
|
||||
|
||||
class CalculatorPage extends StatefulWidget {
|
||||
const CalculatorPage({super.key});
|
||||
|
||||
@override
|
||||
State<CalculatorPage> createState() => _CalculatorPageState();
|
||||
}
|
||||
|
||||
class _CalculatorPageState extends State<CalculatorPage> with CalculatorMixin {
|
||||
@override
|
||||
void initState() {
|
||||
initControllers();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<CalculatorBloc, CalculatorState>(
|
||||
listener: (context, state) {
|
||||
if ((state.weight ?? 0) > 0 && (state.size ?? 0) > 0) {
|
||||
averageWeightController.text =
|
||||
"${(state.weight ?? 0) / (state.size ?? 1)}";
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
backgroundColor: context.color.grayBackground,
|
||||
appBar: AppBar(title: Text(AppLocalization.current.calculator)),
|
||||
body: ModalProgressHUD(
|
||||
inAsyncCall: state.isLoading,
|
||||
child: ListView(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
children: [
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.weight,
|
||||
name: AppLocalization.current.weight_in_kg,
|
||||
isRequired: true,
|
||||
format: [
|
||||
FilteringTextInputFormatter.allow(
|
||||
RegExp(r'^\d*[.,]?\d{0,}$'), // accepts both "." and ","
|
||||
),
|
||||
],
|
||||
controller: weightController,
|
||||
inputType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
onchange: (value) {
|
||||
context.read<CalculatorBloc>().add(
|
||||
WeightSizeEnterEvent(
|
||||
weight: value,
|
||||
size: sizeController.text,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.size,
|
||||
name: AppLocalization.current.size_in_m3,
|
||||
isRequired: true,
|
||||
controller: sizeController,
|
||||
format: [
|
||||
FilteringTextInputFormatter.allow(
|
||||
RegExp(r'^\d*[.,]?\d{0,}$'), // accepts both "." and ","
|
||||
),
|
||||
],
|
||||
inputType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
onchange: (value) {
|
||||
context.read<CalculatorBloc>().add(
|
||||
WeightSizeEnterEvent(
|
||||
weight: weightController.text,
|
||||
size: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.average_weight,
|
||||
name: AppLocalization.current.average_weight_in,
|
||||
controller: averageWeightController,
|
||||
readOnly: true,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
CustomTextFieldName(
|
||||
hint: AppLocalization.current.product_name,
|
||||
isRequired: true,
|
||||
name: AppLocalization.current.product_name,
|
||||
controller: productNameController,
|
||||
inputType: TextInputType.name,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
CustomDropDown(
|
||||
isRequired: false,
|
||||
value: state.selectedWarehouse,
|
||||
|
||||
onChange: (value) {
|
||||
context.read<CalculatorBloc>().add(
|
||||
WareHouseSelectEvent(wareHouse: value ?? ""),
|
||||
);
|
||||
},
|
||||
name: AppLocalization.current.select_warehouse,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
ElevatedButton(
|
||||
onPressed: ((state.weight ?? 0) > 0 && (state.size ?? 0) > 0)
|
||||
? () {
|
||||
context.read<CalculatorBloc>().add(
|
||||
CalculateEvent(
|
||||
productName: productNameController.text,
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset("assets/svg/ic_calculator_1.svg"),
|
||||
AppUtils.kBoxWith8,
|
||||
Text(AppLocalization.current.calculate),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../core/utils/app_utils.dart';
|
||||
|
||||
class OrderDataWidget extends StatelessWidget {
|
||||
const OrderDataWidget({super.key, required this.title, required this.name});
|
||||
|
||||
final String title;
|
||||
final String name;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Text(title, style: context.text.profileCategory)),
|
||||
AppUtils.kBoxWidth8,
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
style: context.text.profileCategory,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const Padding(padding: AppUtils.kPaddingVer8, child: AppUtils.kDivider),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
|
||||
import '../../models/banner_response.dart';
|
||||
|
||||
abstract class HomeRemoteDataSource {
|
||||
Future<OrdersListResponse> ordersList(Map<String, dynamic> request);
|
||||
|
||||
Future<OrderSingleResponse> orderSingle(String orderId);
|
||||
|
||||
Future<CommentResponse> comment(CommentRequest request);
|
||||
|
||||
Future<NotificationResponse> notification(Map<String, dynamic> request);
|
||||
|
||||
Future<ReadNotificationResponse> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
);
|
||||
|
||||
Future<List<BannerResponse>> bannersResponse();
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/banner_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../core/error/exceptions.dart';
|
||||
import '../../../../../core/local_source/local_source.dart';
|
||||
import '../../../../../injector_container.dart';
|
||||
import 'home_remote_data_source.dart';
|
||||
|
||||
class HomeRemoteDataSourceImpl extends HomeRemoteDataSource {
|
||||
final Dio dio;
|
||||
|
||||
HomeRemoteDataSourceImpl(this.dio);
|
||||
|
||||
@override
|
||||
Future<OrdersListResponse> ordersList(Map<String, dynamic> request) async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
Constants.baseUrl + Urls.orderList,
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
queryParameters: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return OrdersListResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OrderSingleResponse> orderSingle(String orderId) async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
"${Constants.baseUrl}${Urls.orderList}/$orderId",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return OrderSingleResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CommentResponse> comment(CommentRequest request) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
"${Constants.baseUrl}${Urls.comment}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return CommentResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NotificationResponse> notification(
|
||||
Map<String, dynamic> request,
|
||||
) async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
"${Constants.baseUrl}${Urls.notification}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
queryParameters: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return NotificationResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ReadNotificationResponse> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
) async {
|
||||
try {
|
||||
final Response response = await dio.put(
|
||||
"${Constants.baseUrl}${Urls.notificationRead}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return ReadNotificationResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<BannerResponse>> bannersResponse() async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
"${Constants.baseUrl}${Urls.banners}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final List<BannerResponse> images = (response.data as List)
|
||||
.map((json) => BannerResponse.fromJson(json))
|
||||
.toList();
|
||||
return images;
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
lib/features/home/data/models/banner_response.dart
Normal file
27
lib/features/home/data/models/banner_response.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
class BannerResponse {
|
||||
BannerResponse({this.id, this.image, this.createdAt, this.updatedAt, this.v});
|
||||
|
||||
BannerResponse.fromJson(dynamic json) {
|
||||
id = json['_id'];
|
||||
image = json['image'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
}
|
||||
|
||||
String? id;
|
||||
String? image;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['_id'] = id;
|
||||
map['image'] = image;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
24
lib/features/home/data/models/comment_request.dart
Normal file
24
lib/features/home/data/models/comment_request.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
class CommentRequest {
|
||||
CommentRequest({
|
||||
this.message,
|
||||
this.rate,
|
||||
this.orderNumber,});
|
||||
|
||||
CommentRequest.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
rate = json['rate'];
|
||||
orderNumber = json['order_number'];
|
||||
}
|
||||
String? message;
|
||||
int? rate;
|
||||
String? orderNumber;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
map['rate'] = rate;
|
||||
map['order_number'] = orderNumber;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
44
lib/features/home/data/models/comment_response.dart
Normal file
44
lib/features/home/data/models/comment_response.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
class CommentResponse {
|
||||
CommentResponse({
|
||||
this.message,
|
||||
this.rate,
|
||||
this.userUcode,
|
||||
this.orderNumber,
|
||||
this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,});
|
||||
|
||||
CommentResponse.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
rate = json['rate'];
|
||||
userUcode = json['user_ucode'];
|
||||
orderNumber = json['order_number'];
|
||||
id = json['_id'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
}
|
||||
String? message;
|
||||
int? rate;
|
||||
String? userUcode;
|
||||
String? orderNumber;
|
||||
String? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
map['rate'] = rate;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['order_number'] = orderNumber;
|
||||
map['_id'] = id;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
79
lib/features/home/data/models/notification_response.dart
Normal file
79
lib/features/home/data/models/notification_response.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'package:cargocalculaterapp/core/models/name.dart';
|
||||
|
||||
class NotificationResponse {
|
||||
NotificationResponse({this.notifications, this.totalCount});
|
||||
|
||||
NotificationResponse.fromJson(dynamic json) {
|
||||
if (json['notifications'] != null) {
|
||||
notifications = [];
|
||||
json['notifications'].forEach((v) {
|
||||
notifications?.add(Notifications.fromJson(v));
|
||||
});
|
||||
}
|
||||
totalCount = json['totalCount'];
|
||||
}
|
||||
|
||||
List<Notifications>? notifications;
|
||||
int? totalCount;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
if (notifications != null) {
|
||||
map['notifications'] = notifications?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['totalCount'] = totalCount;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Notifications {
|
||||
Notifications({
|
||||
this.id,
|
||||
this.userUcode,
|
||||
this.orderNumber,
|
||||
this.text,
|
||||
this.status,
|
||||
this.isRead,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,
|
||||
});
|
||||
|
||||
Notifications.fromJson(dynamic json) {
|
||||
id = json['_id'];
|
||||
userUcode = json['user_ucode'];
|
||||
orderNumber = json['order_number'];
|
||||
text = json['text'] != null ? Name.fromJson(json['text']) : null;
|
||||
status = json['status'];
|
||||
isRead = json['is_read'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
}
|
||||
|
||||
String? id;
|
||||
String? userUcode;
|
||||
String? orderNumber;
|
||||
Name? text;
|
||||
String? status;
|
||||
bool? isRead;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['_id'] = id;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['order_number'] = orderNumber;
|
||||
if (text != null) {
|
||||
map['text'] = text?.toJson();
|
||||
}
|
||||
map['status'] = status;
|
||||
map['is_read'] = isRead;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
145
lib/features/home/data/models/order_single_response.dart
Normal file
145
lib/features/home/data/models/order_single_response.dart
Normal file
@@ -0,0 +1,145 @@
|
||||
import 'package:cargocalculaterapp/core/models/name.dart';
|
||||
|
||||
class OrderSingleResponse {
|
||||
OrderSingleResponse({
|
||||
this.id,
|
||||
this.userUcode,
|
||||
this.phoneNumber,
|
||||
this.gmail,
|
||||
this.quantity,
|
||||
this.nameUz,
|
||||
this.nameRu,
|
||||
this.nameZh,
|
||||
this.weight,
|
||||
this.m3,
|
||||
this.averageWeightKg,
|
||||
this.wharehouseUz,
|
||||
this.wharehouseRu,
|
||||
this.wharehouseZh,
|
||||
this.deliveryPrice,
|
||||
this.shipmentId,
|
||||
this.leadId,
|
||||
this.status,
|
||||
this.partiallyArrived,
|
||||
this.fullArrived,
|
||||
this.orderNumber,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,
|
||||
this.arrivalDate,
|
||||
this.images,
|
||||
});
|
||||
|
||||
OrderSingleResponse.fromJson(dynamic json) {
|
||||
userUcode = json['user_ucode'];
|
||||
phoneNumber = json['phone_number'];
|
||||
gmail = json['gmail'];
|
||||
quantity = json['quantity'];
|
||||
nameUz = json['nameUz'];
|
||||
nameRu = json['nameRu'];
|
||||
nameZh = json['nameZh'];
|
||||
weight = json['weight'];
|
||||
m3 = json['m3'];
|
||||
averageWeightKg = json['average_weight_kg'];
|
||||
wharehouseUz = json['wharehouseUz'];
|
||||
wharehouseRu = json['wharehouseRu'];
|
||||
wharehouseZh = json['wharehouseZh'];
|
||||
deliveryPrice = json['delivery_price'];
|
||||
shipmentId = json['shipment_id'];
|
||||
leadId = json['lead_id'];
|
||||
status = json['status'] != null ? Status.fromJson(json['status']) : null;
|
||||
partiallyArrived = json['partially_arrived'];
|
||||
fullArrived = json['full_arrived'];
|
||||
orderNumber = json['order_number'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
id = json['id'];
|
||||
arrivalDate = json['arrival_date'];
|
||||
images = json['images'] != null ? json['images'].cast<String>() : [];
|
||||
}
|
||||
|
||||
String? userUcode;
|
||||
String? phoneNumber;
|
||||
String? gmail;
|
||||
int? quantity;
|
||||
String? nameUz;
|
||||
String? nameRu;
|
||||
String? nameZh;
|
||||
String? weight;
|
||||
String? m3;
|
||||
String? averageWeightKg;
|
||||
String? wharehouseUz;
|
||||
String? wharehouseRu;
|
||||
String? wharehouseZh;
|
||||
String? deliveryPrice;
|
||||
String? shipmentId;
|
||||
String? leadId;
|
||||
Status? status;
|
||||
bool? partiallyArrived;
|
||||
bool? fullArrived;
|
||||
String? orderNumber;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
String? id;
|
||||
String? arrivalDate;
|
||||
List<String>? images;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['user_ucode'] = userUcode;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['gmail'] = gmail;
|
||||
map['quantity'] = quantity;
|
||||
map['nameUz'] = nameUz;
|
||||
map['nameRu'] = nameRu;
|
||||
map['nameZh'] = nameZh;
|
||||
map['weight'] = weight;
|
||||
map['m3'] = m3;
|
||||
map['average_weight_kg'] = averageWeightKg;
|
||||
map['wharehouseUz'] = wharehouseUz;
|
||||
map['wharehouseRu'] = wharehouseRu;
|
||||
map['wharehouseZh'] = wharehouseZh;
|
||||
map['delivery_price'] = deliveryPrice;
|
||||
map['shipment_id'] = shipmentId;
|
||||
map['lead_id'] = leadId;
|
||||
if (status != null) {
|
||||
map['status'] = status?.toJson();
|
||||
}
|
||||
map['partially_arrived'] = partiallyArrived;
|
||||
map['full_arrived'] = fullArrived;
|
||||
map['order_number'] = orderNumber;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
map['id'] = id;
|
||||
map['arrival_date'] = arrivalDate;
|
||||
map['images'] = images;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Status {
|
||||
Status({this.code, this.translations});
|
||||
|
||||
Status.fromJson(dynamic json) {
|
||||
code = json['code'];
|
||||
translations =
|
||||
json['translations'] != null
|
||||
? Name.fromJson(json['translations'])
|
||||
: null;
|
||||
}
|
||||
|
||||
String? code;
|
||||
Name? translations;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['code'] = code;
|
||||
if (translations != null) {
|
||||
map['translations'] = translations?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
160
lib/features/home/data/models/orders_list_response.dart
Normal file
160
lib/features/home/data/models/orders_list_response.dart
Normal file
@@ -0,0 +1,160 @@
|
||||
import '../../../../core/models/name.dart';
|
||||
|
||||
class OrdersListResponse {
|
||||
OrdersListResponse({this.totalCount, this.data});
|
||||
|
||||
OrdersListResponse.fromJson(dynamic json) {
|
||||
totalCount = json['totalCount'];
|
||||
if (json['data'] != null) {
|
||||
data = [];
|
||||
json['data'].forEach((v) {
|
||||
data?.add(Data.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int? totalCount;
|
||||
List<Data>? data;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['totalCount'] = totalCount;
|
||||
if (data != null) {
|
||||
map['data'] = data?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Data {
|
||||
Data({
|
||||
this.id,
|
||||
this.userUcode,
|
||||
this.phoneNumber,
|
||||
this.gmail,
|
||||
this.quantity,
|
||||
this.nameUz,
|
||||
this.nameRu,
|
||||
this.nameZh,
|
||||
this.size,
|
||||
this.wharehouseUz,
|
||||
this.wharehouseRu,
|
||||
this.wharehouseZh,
|
||||
this.deliveryPrice,
|
||||
this.shipmentId,
|
||||
this.leadId,
|
||||
this.status,
|
||||
this.partiallyArrived,
|
||||
this.fullArrived,
|
||||
this.orderNumber,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,
|
||||
this.arrivalDate,
|
||||
});
|
||||
|
||||
Data.fromJson(dynamic json) {
|
||||
userUcode = json['user_ucode'];
|
||||
phoneNumber = json['phone_number'];
|
||||
gmail = json['gmail'];
|
||||
quantity = json['quantity'];
|
||||
nameUz = json['nameUz'];
|
||||
nameRu = json['nameRu'];
|
||||
nameZh = json['nameZh'];
|
||||
size = json['size'];
|
||||
wharehouseUz = json['wharehouseUz'];
|
||||
wharehouseRu = json['wharehouseRu'];
|
||||
wharehouseZh = json['wharehouseZh'];
|
||||
deliveryPrice = json['delivery_price'];
|
||||
shipmentId = json['shipment_id'];
|
||||
leadId = json['lead_id'];
|
||||
status = json['status'] != null ? Status.fromJson(json['status']) : null;
|
||||
partiallyArrived = json['partially_arrived'];
|
||||
fullArrived = json['full_arrived'];
|
||||
orderNumber = json['order_number'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
id = json['id'];
|
||||
arrivalDate = json['arrival_date'];
|
||||
}
|
||||
|
||||
String? userUcode;
|
||||
String? phoneNumber;
|
||||
String? gmail;
|
||||
int? quantity;
|
||||
String? nameUz;
|
||||
String? nameRu;
|
||||
String? nameZh;
|
||||
String? size;
|
||||
String? wharehouseUz;
|
||||
String? wharehouseRu;
|
||||
String? wharehouseZh;
|
||||
String? deliveryPrice;
|
||||
String? shipmentId;
|
||||
String? leadId;
|
||||
Status? status;
|
||||
bool? partiallyArrived;
|
||||
bool? fullArrived;
|
||||
String? orderNumber;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
String? id;
|
||||
String? arrivalDate;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['_id'] = id;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['gmail'] = gmail;
|
||||
map['quantity'] = quantity;
|
||||
map['nameUz'] = nameUz;
|
||||
map['nameRu'] = nameRu;
|
||||
map['nameZh'] = nameZh;
|
||||
map['size'] = size;
|
||||
map['wharehouseUz'] = wharehouseUz;
|
||||
map['wharehouseRu'] = wharehouseRu;
|
||||
map['wharehouseZh'] = wharehouseZh;
|
||||
map['delivery_price'] = deliveryPrice;
|
||||
map['shipment_id'] = shipmentId;
|
||||
map['lead_id'] = leadId;
|
||||
if (status != null) {
|
||||
map['status'] = status?.toJson();
|
||||
}
|
||||
map['partially_arrived'] = partiallyArrived;
|
||||
map['full_arrived'] = fullArrived;
|
||||
map['order_number'] = orderNumber;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
map['id'] = id;
|
||||
map['arrival_date'] = arrivalDate;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Status {
|
||||
Status({this.code, this.translations});
|
||||
|
||||
Status.fromJson(dynamic json) {
|
||||
code = json['code'];
|
||||
translations =
|
||||
json['translations'] != null
|
||||
? Name.fromJson(json['translations'])
|
||||
: null;
|
||||
}
|
||||
|
||||
String? code;
|
||||
Name? translations;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['code'] = code;
|
||||
if (translations != null) {
|
||||
map['translations'] = translations?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
16
lib/features/home/data/models/read_notification_request.dart
Normal file
16
lib/features/home/data/models/read_notification_request.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
class ReadNotificationRequest {
|
||||
ReadNotificationRequest({
|
||||
this.notificationIds,});
|
||||
|
||||
ReadNotificationRequest.fromJson(dynamic json) {
|
||||
notificationIds = json['notification_ids'] != null ? json['notification_ids'].cast<String>() : [];
|
||||
}
|
||||
List<String>? notificationIds;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['notification_ids'] = notificationIds;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class ReadNotificationResponse {
|
||||
ReadNotificationResponse({
|
||||
this.message,});
|
||||
|
||||
ReadNotificationResponse.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
}
|
||||
String? message;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
88
lib/features/home/data/repository/home_repository_impl.dart
Normal file
88
lib/features/home/data/repository/home_repository_impl.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/banner_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../domain/repository/home_repository.dart';
|
||||
import '../data_source/remote/home_remote_data_source.dart';
|
||||
|
||||
class HomeRepositoryImpl extends HomeRepository {
|
||||
final HomeRemoteDataSource remoteDataSource;
|
||||
|
||||
HomeRepositoryImpl(this.remoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OrdersListResponse>> ordersList(
|
||||
Map<String, dynamic> request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.ordersList(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OrderSingleResponse>> orderSingle(
|
||||
String orderId,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.orderSingle(orderId);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, CommentResponse>> comment(
|
||||
CommentRequest request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.comment(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, NotificationResponse>> notification(
|
||||
Map<String, dynamic> request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.notification(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, ReadNotificationResponse>> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.notificationRead(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<BannerResponse>>> bannersGet() async {
|
||||
try {
|
||||
final response = await remoteDataSource.bannersResponse();
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
30
lib/features/home/domain/repository/home_repository.dart
Normal file
30
lib/features/home/domain/repository/home_repository.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failure.dart';
|
||||
import '../../data/models/banner_response.dart';
|
||||
|
||||
abstract class HomeRepository {
|
||||
Future<Either<Failure, OrdersListResponse>> ordersList(
|
||||
Map<String, dynamic> request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, OrderSingleResponse>> orderSingle(String orderId);
|
||||
|
||||
Future<Either<Failure, CommentResponse>> comment(CommentRequest request);
|
||||
|
||||
Future<Either<Failure, NotificationResponse>> notification(
|
||||
Map<String, dynamic> request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, ReadNotificationResponse>> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, List<BannerResponse>>> bannersGet();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user