Merge pull request #1

feature/livestock
This commit is contained in:
Mojtaba Eshaghi
2025-06-02 09:42:46 +03:30
committed by GitHub
113 changed files with 4517 additions and 457 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

6
assets/icons/search.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.33333 12.6667C10.2789 12.6667 12.6667 10.2789 12.6667 7.33333C12.6667 4.38781 10.2789 2 7.33333 2C4.38781 2 2 4.38781 2 7.33333C2 10.2789 4.38781 12.6667 7.33333 12.6667Z"
stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.0001 13.9996L11.1001 11.0996" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 524 B

View File

@@ -0,0 +1,4 @@
<svg width="29" height="34" viewBox="0 0 29 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.4673 16.3731H26.3971C23.8763 14.6 20.6141 12.5924 20.111 7.84137C20.2039 7.44288 20.2533 7.02873 20.2533 6.60332C20.2533 3.50891 17.6579 1 14.456 1C11.2541 1 8.65872 3.50842 8.65872 6.60332C8.65872 6.97439 8.69628 7.33714 8.76795 7.68814C8.4101 11.1815 6.41078 14.0767 2.76998 16.3727H2.53224C1.68951 16.3727 1 17.0556 1 17.8902V31.4824C1 32.3171 1.68951 33 2.53224 33H26.4678C27.3105 33 28 32.3171 28 31.4824V17.8902C28 17.0556 27.31 16.3731 26.4673 16.3731ZM14.4995 8.63394C13.3671 8.63394 12.4493 7.72486 12.4493 6.60332C12.4493 5.48177 13.3671 4.57269 14.4995 4.57269C15.6319 4.57269 16.5497 5.48177 16.5497 6.60332C16.5497 7.72486 15.6319 8.63394 14.4995 8.63394Z"
fill="#D9F7F0" stroke="#005C45" stroke-miterlimit="10"/>
</svg>

After

Width:  |  Height:  |  Size: 857 B

24
assets/icons/virtual.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/vec/search.svg.vec Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/vec/virtual.svg.vec Normal file

Binary file not shown.

View File

@@ -1,7 +1,9 @@
import 'package:rasadyar_auth/auth.dart';
import 'package:rasadyar_app/presentation/routes/app_pages.dart';
import 'package:rasadyar_auth/data/models/local/module/module_model.dart';
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_inspection/inspection.dart';
import 'package:rasadyar_livestock/presentation/routes/app_pages.dart';
class AuthService extends GetxService {
var tokenService = Get.find<TokenStorageService>();
@@ -13,25 +15,19 @@ class AuthService extends GetxService {
super.onInit();
ever(tokenService.accessToken, (callback) {
iLog('Access token callback: $callback, value: ${tokenService.accessToken.value}');
accessRes.value = (callback != null);
});
ever(tokenService.refreshToken, (callback) {
fLog('Refresh token callback: $callback, value: ${tokenService.refreshToken.value}');
refAccessRes.value = (callback != null);
});
everAll([accessRes, refAccessRes], (_) {
if (accessRes.value && refAccessRes.value) {
Get.offAndToNamed(InspectionRoutes.inspection);
fLog('Both accessToken and refreshToken are available: accessToken=${tokenService.accessToken.value}, refreshToken=${tokenService.refreshToken.value}');
} else {
fLog('One or both tokens are missing: accessToken=${tokenService.accessToken.value}, refreshToken=${tokenService.refreshToken.value}');
var targetPage = getTargetPage(tokenService.appModule.value);
Get.offAndToNamed(targetPage);
}
});
}
}
}

View File

@@ -2,9 +2,7 @@ import 'package:flutter/material.dart';
import 'package:rasadyar_app/presentation/routes/app_pages.dart';
import 'package:rasadyar_auth/auth.dart';
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
import 'package:rasadyar_core/core.dart';
import 'infrastructure/di/di.dart';
import 'infrastructure/service/auth_service.dart';

View File

@@ -1,6 +1,7 @@
import 'package:flutter/animation.dart';
import 'package:rasadyar_app/presentation/routes/app_pages.dart';
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_inspection/inspection.dart';
class SplashLogic extends GetxController with GetTickerProviderStateMixin {
late final AnimationController scaleController;
@@ -8,6 +9,8 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin {
Rxn<Animation<double>> scaleAnimation = Rxn();
Rxn<Animation<double>> rotationAnimation = Rxn();
var tokenService = Get.find<TokenStorageService>();
@override
void onInit() {
super.onInit();
@@ -53,8 +56,9 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin {
@override
void onReady() {
super.onReady();
Future.delayed(const Duration(seconds: 1), () {
Get.offAllNamed(InspectionRoutes.inspection);
Future.delayed(const Duration(seconds: 1), () async {
var module = tokenService.appModule.value;
Get.offAndToNamed(getTargetPage(module));
});
}

View File

@@ -20,11 +20,10 @@ class SplashPage extends GetView<SplashLogic> {
scale: data.value!,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 1),
child: Image.asset(
Assets.imagesInnerSplash,
child: Assets.images.innerSplash.image(
width: 190,
height: 190,
),
)
),
);
}, controller.scaleAnimation),
@@ -35,7 +34,7 @@ class SplashPage extends GetView<SplashLogic> {
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 1),
child: Image.asset(Assets.imagesOutterSplash),
child: Assets.images.outterSplash.image()
),
);
}, controller.rotationAnimation),

View File

@@ -1,9 +1,12 @@
import 'package:rasadyar_app/presentation/pages/splash/logic.dart';
import 'package:rasadyar_app/presentation/pages/splash/view.dart';
import 'package:rasadyar_app/presentation/pages/system_design/system_design.dart';
import 'package:rasadyar_auth/auth.dart';
import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart';
import 'package:rasadyar_auth/presentation/routes/pages.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_inspection/inspection.dart';
import 'package:rasadyar_livestock/presentation/routes/app_pages.dart';
part 'app_paths.dart';
@@ -23,6 +26,18 @@ sealed class AppPages {
...InspectionPages.pages,
...AuthPages.pages,
...LiveStockPages.pages,
];
}
String getTargetPage(Module? value) {
eLog('getTargetPage: $value');
switch (value) {
case Module.inspection:
return InspectionRoutes.inspection;
case Module.liveStocks:
return LiveStockRoutes.init;
default:
return InspectionRoutes.inspection;
}
}

View File

@@ -16,7 +16,9 @@ class Assets {
static const String iconsFilter = 'assets/icons/filter.svg';
static const String iconsGps = 'assets/icons/gps.svg';
static const String iconsInformation = 'assets/icons/information.svg';
static const String iconsInspection = 'assets/icons/inspection.svg';
static const String iconsKey = 'assets/icons/key.svg';
static const String iconsLiveStock = 'assets/icons/liveStock.svg';
static const String iconsLogout = 'assets/icons/logout.svg';
static const String iconsMap = 'assets/icons/map.svg';
static const String iconsMapMarker = 'assets/icons/map_marker.svg';
@@ -28,6 +30,7 @@ class Assets {
static const String iconsReceiptDiscount = 'assets/icons/receipt_discount.svg';
static const String iconsScan = 'assets/icons/scan.svg';
static const String iconsScanBarcode = 'assets/icons/scan_barcode.svg';
static const String iconsSearch = 'assets/icons/search.svg';
static const String iconsSecurityTime = 'assets/icons/security_time.svg';
static const String iconsSetting = 'assets/icons/setting.svg';
static const String iconsTagUser = 'assets/icons/tag_user.svg';
@@ -36,37 +39,5 @@ class Assets {
static const String iconsUserSquare = 'assets/icons/user_square.svg';
static const String imagesInnerSplash = 'assets/images/inner_splash.webp';
static const String imagesOutterSplash = 'assets/images/outter_splash.webp';
static const String vecAddSvg = 'assets/vec/add.svg.vec';
static const String vecArrowLeftSvg = 'assets/vec/arrow_left.svg.vec';
static const String vecArrowRightSvg = 'assets/vec/arrow_right.svg.vec';
static const String vecBgHeaderUserProfileSvg = 'assets/vec/bg_header_user_profile.svg.vec';
static const String vecCalendarSearchSvg = 'assets/vec/calendar_search.svg.vec';
static const String vecCalendarSvg = 'assets/vec/calendar.svg.vec';
static const String vecCallSvg = 'assets/vec/call.svg.vec';
static const String vecDiagramSvg = 'assets/vec/diagram.svg.vec';
static const String vecDownloadSvg = 'assets/vec/download.svg.vec';
static const String vecEditSvg = 'assets/vec/edit.svg.vec';
static const String vecExcelDownloadSvg = 'assets/vec/excel_download.svg.vec';
static const String vecFilterSvg = 'assets/vec/filter.svg.vec';
static const String vecGpsSvg = 'assets/vec/gps.svg.vec';
static const String vecInformationSvg = 'assets/vec/information.svg.vec';
static const String vecKeySvg = 'assets/vec/key.svg.vec';
static const String vecLogoutSvg = 'assets/vec/logout.svg.vec';
static const String vecMapMarkerSvg = 'assets/vec/map_marker.svg.vec';
static const String vecMapSvg = 'assets/vec/map.svg.vec';
static const String vecMessageAddSvg = 'assets/vec/message_add.svg.vec';
static const String vecPdfDownloadSvg = 'assets/vec/pdf_download.svg.vec';
static const String vecPictureFrameSvg = 'assets/vec/picture_frame.svg.vec';
static const String vecProfileCircleSvg = 'assets/vec/profile_circle.svg.vec';
static const String vecProfileUserSvg = 'assets/vec/profile_user.svg.vec';
static const String vecReceiptDiscountSvg = 'assets/vec/receipt_discount.svg.vec';
static const String vecScanBarcodeSvg = 'assets/vec/scan_barcode.svg.vec';
static const String vecScanSvg = 'assets/vec/scan.svg.vec';
static const String vecSecurityTimeSvg = 'assets/vec/security_time.svg.vec';
static const String vecSettingSvg = 'assets/vec/setting.svg.vec';
static const String vecTagUserSvg = 'assets/vec/tag_user.svg.vec';
static const String vecTrashSvg = 'assets/vec/trash.svg.vec';
static const String vecUserSquareSvg = 'assets/vec/user_square.svg.vec';
static const String vecUserSvg = 'assets/vec/user.svg.vec';
}

View File

@@ -5,3 +5,4 @@ library;
export 'data/services/auth_middelware.dart';
export 'data/di/auth_di.dart';
export 'data/models/local/module/module_model.dart';

View File

@@ -11,6 +11,7 @@ GetIt diAuth = GetIt.instance;
Future<void> setupAuthDI() async {
diAuth.registerLazySingleton(() => DioRemoteManager());
final manager = diAuth.get<DioRemoteManager>();
final dioRemote = await manager.setEnvironment(ApiEnvironment.dam);
diAuth.registerCachedFactory<AuthRepositoryImpl>(

View File

@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart';
import 'package:rasadyar_core/core.dart';
part 'module_model.freezed.dart';
@freezed
abstract class ModuleModel with _$ModuleModel{
const factory ModuleModel({
required String title,
required String icon,
required Module module,
}) = _ModuleModel;
}

View File

@@ -0,0 +1,148 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'module_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ModuleModel {
String get title; String get icon; Module get module;
/// Create a copy of ModuleModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ModuleModelCopyWith<ModuleModel> get copyWith => _$ModuleModelCopyWithImpl<ModuleModel>(this as ModuleModel, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ModuleModel&&(identical(other.title, title) || other.title == title)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.module, module) || other.module == module));
}
@override
int get hashCode => Object.hash(runtimeType,title,icon,module);
@override
String toString() {
return 'ModuleModel(title: $title, icon: $icon, module: $module)';
}
}
/// @nodoc
abstract mixin class $ModuleModelCopyWith<$Res> {
factory $ModuleModelCopyWith(ModuleModel value, $Res Function(ModuleModel) _then) = _$ModuleModelCopyWithImpl;
@useResult
$Res call({
String title, String icon, Module module
});
}
/// @nodoc
class _$ModuleModelCopyWithImpl<$Res>
implements $ModuleModelCopyWith<$Res> {
_$ModuleModelCopyWithImpl(this._self, this._then);
final ModuleModel _self;
final $Res Function(ModuleModel) _then;
/// Create a copy of ModuleModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? title = null,Object? icon = null,Object? module = null,}) {
return _then(_self.copyWith(
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
as String,module: null == module ? _self.module : module // ignore: cast_nullable_to_non_nullable
as Module,
));
}
}
/// @nodoc
class _ModuleModel implements ModuleModel {
const _ModuleModel({required this.title, required this.icon, required this.module});
@override final String title;
@override final String icon;
@override final Module module;
/// Create a copy of ModuleModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ModuleModelCopyWith<_ModuleModel> get copyWith => __$ModuleModelCopyWithImpl<_ModuleModel>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ModuleModel&&(identical(other.title, title) || other.title == title)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.module, module) || other.module == module));
}
@override
int get hashCode => Object.hash(runtimeType,title,icon,module);
@override
String toString() {
return 'ModuleModel(title: $title, icon: $icon, module: $module)';
}
}
/// @nodoc
abstract mixin class _$ModuleModelCopyWith<$Res> implements $ModuleModelCopyWith<$Res> {
factory _$ModuleModelCopyWith(_ModuleModel value, $Res Function(_ModuleModel) _then) = __$ModuleModelCopyWithImpl;
@override @useResult
$Res call({
String title, String icon, Module module
});
}
/// @nodoc
class __$ModuleModelCopyWithImpl<$Res>
implements _$ModuleModelCopyWith<$Res> {
__$ModuleModelCopyWithImpl(this._self, this._then);
final _ModuleModel _self;
final $Res Function(_ModuleModel) _then;
/// Create a copy of ModuleModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? title = null,Object? icon = null,Object? module = null,}) {
return _then(_ModuleModel(
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
as String,module: null == module ? _self.module : module // ignore: cast_nullable_to_non_nullable
as Module,
));
}
}
// dart format on

View File

@@ -1,3 +1,4 @@
import 'package:rasadyar_auth/auth.dart';
import 'package:rasadyar_core/core.dart';
part 'user_local_model.g.dart';
@@ -15,12 +16,16 @@ class UserLocalModel extends HiveObject {
@HiveField(4)
String? name;
@HiveField(5)
Module? module;
UserLocalModel({
this.username,
this.password,
this.token,
this.refreshToken,
this.name,
this.module,
});
UserLocalModel copyWith({
@@ -29,6 +34,7 @@ class UserLocalModel extends HiveObject {
String? token,
String? refreshToken,
String? name,
Module? module,
}) {
return UserLocalModel(
username: username ?? this.username,
@@ -36,6 +42,15 @@ class UserLocalModel extends HiveObject {
token: token ?? this.token,
refreshToken: refreshToken ?? this.refreshToken,
name: name ?? this.name,
module: module ?? this.module,
);
}
}
@HiveType(typeId: 1)
enum Module {
@HiveField(0)
liveStocks,
@HiveField(1)
inspection,
}

View File

@@ -22,13 +22,14 @@ class UserLocalModelAdapter extends TypeAdapter<UserLocalModel> {
token: fields[2] as String?,
refreshToken: fields[3] as String?,
name: fields[4] as String?,
module: fields[5] as Module?,
);
}
@override
void write(BinaryWriter writer, UserLocalModel obj) {
writer
..writeByte(5)
..writeByte(6)
..writeByte(0)
..write(obj.username)
..writeByte(1)
@@ -38,7 +39,9 @@ class UserLocalModelAdapter extends TypeAdapter<UserLocalModel> {
..writeByte(3)
..write(obj.refreshToken)
..writeByte(4)
..write(obj.name);
..write(obj.name)
..writeByte(5)
..write(obj.module);
}
@override
@@ -51,3 +54,40 @@ class UserLocalModelAdapter extends TypeAdapter<UserLocalModel> {
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class ModuleAdapter extends TypeAdapter<Module> {
@override
final typeId = 1;
@override
Module read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return Module.liveStocks;
case 1:
return Module.inspection;
default:
return Module.liveStocks;
}
}
@override
void write(BinaryWriter writer, Module obj) {
switch (obj) {
case Module.liveStocks:
writer.writeByte(0);
case Module.inspection:
writer.writeByte(1);
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ModuleAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -1,69 +1,59 @@
import 'dart:convert';
import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart';
import 'package:rasadyar_auth/hive_registrar.g.dart';
import 'package:rasadyar_core/core.dart';
class TokenStorageService extends GetxService {
static const String _boxName = 'secureBox';
static const String _accessTokenKey = 'accessToken';
static const String _refreshTokenKey = 'refreshToken';
static const String _moduleKey = 'moduleSelected';
final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
final HiveLocalStorage _localStorage = diCore.get<HiveLocalStorage>();
RxnString accessToken = RxnString();
RxnString refreshToken = RxnString();
RxnString tsss = RxnString();
Rxn<Module> appModule = Rxn(null);
Future<void> init() async {
await Hive.initFlutter();
Hive.registerAdapters();
final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key');
final encryptionKey =
encryptedKey != null
? base64Url.decode(encryptedKey)
: Hive.generateSecureKey();
final encryptionKey = encryptedKey != null ? base64Url.decode(encryptedKey) : Hive.generateSecureKey();
if (encryptedKey == null) {
await _secureStorage.write(
key: 'hive_enc_key',
value: base64UrlEncode(encryptionKey),
);
await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey));
}
await _localStorage.init();
await _localStorage.openBox(
_boxName,
encryptionCipher: HiveAesCipher(encryptionKey),
);
await _localStorage.openBox(_boxName, encryptionCipher: HiveAesCipher(encryptionKey));
accessToken.value = _localStorage.read<String?>(
boxName: _boxName,
key: _accessTokenKey,
);
refreshToken.value = _localStorage.read<String?>(
boxName: _boxName,
key: _refreshTokenKey,
);
accessToken.value = _localStorage.read<String?>(boxName: _boxName, key: _accessTokenKey);
refreshToken.value = _localStorage.read<String?>(boxName: _boxName, key: _refreshTokenKey);
appModule.value = _localStorage.read<Module?>(boxName: _boxName, key: _moduleKey);
}
Future<void> saveAccessToken(String token) async {
await _localStorage.save(
boxName: _boxName,
key: _accessTokenKey,
value: token,
);
await _localStorage.save(boxName: _boxName, key: _accessTokenKey, value: token);
accessToken.value = token;
accessToken.refresh();
}
Future<void> saveRefreshToken(String token) async {
await _localStorage.save(
boxName: _boxName,
key: _refreshTokenKey,
value: token,
);
await _localStorage.save(boxName: _boxName, key: _refreshTokenKey, value: token);
refreshToken.value = token;
refreshToken.refresh();
}
Future<void> saveModule(Module input) async {
await _localStorage.save(boxName: _boxName, key: _moduleKey, value: input);
appModule.value = input;
appModule.refresh();
}
Future<void> deleteTokens() async {
await _localStorage.clear(_boxName);
accessToken.value = null;

View File

@@ -0,0 +1,61 @@
import 'package:flutter/foundation.dart';
import 'package:rasadyar_auth/auth.dart';
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
import 'package:rasadyar_core/core.dart';
import '../models/response/auth/auth_response_model.dart';
import '../services/token_storage_service.dart';
Future<void> safeCall<T>({
required AppAsyncCallback<T> call,
Function(T result)? onSuccess,
ErrorCallback? onError,
VoidCallback? onComplete,
bool showLoading = false,
bool showError = false,
bool showSuccess = false,
bool showToast = false,
bool showSnackBar = false,
Function()? onShowLoading,
Function()? onHideLoading,
Function()? onShowSuccessMessage,
Function()? onShowErrorMessage,
}) {
final authRepository = diAuth.get<AuthRepositoryImpl>();
TokenStorageService tokenStorageService = Get.find<TokenStorageService>();
return gSafeCall(
call: call,
onSuccess: onSuccess,
onError: onError,
onComplete: onComplete,
showLoading: showLoading,
showError: showError,
showSuccess: showSuccess,
showToast: showToast,
showSnackBar: showSnackBar,
onShowLoading: onShowLoading,
onHideLoading: onHideLoading,
onShowSuccessMessage: onShowSuccessMessage,
onShowErrorMessage: onShowErrorMessage,
retryOnAuthError: true,
onTokenRefresh: () {
var token = tokenStorageService.refreshToken.value;
authRepository
.loginWithRefreshToken(authRequest: {"refresh_token": token})
.then((value) async {
if (value is AuthResponseModel) {
await tokenStorageService.saveAccessToken(value.access!);
} else {
throw Exception("Failed to refresh token");
}
})
.catchError((error) {
if (kDebugMode) {
print('Error during token refresh: $error');
}
throw error;
});
},
);
}

View File

@@ -8,11 +8,14 @@ import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart
extension HiveRegistrar on HiveInterface {
void registerAdapters() {
registerAdapter(UserLocalModelAdapter());
registerAdapter(ModuleAdapter());
}
}
extension IsolatedHiveRegistrar on IsolatedHiveInterface {
void registerAdapters() {
registerAdapter(ModuleAdapter());
registerAdapter(UserLocalModelAdapter());
}
}

View File

@@ -7,9 +7,12 @@ import 'package:rasadyar_auth/data/models/request/login_request/login_request_mo
import 'package:rasadyar_auth/data/models/response/auth/auth_response_model.dart';
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
import 'package:rasadyar_auth/data/utils/safe_call.dart';
import 'package:rasadyar_auth/presentation/widget/captcha/logic.dart';
import 'package:rasadyar_core/core.dart';
import '../../../data/models/local/user_local/user_local_model.dart';
enum AuthType { useAndPass, otp }
enum AuthStatus { init }
@@ -42,6 +45,8 @@ class AuthLogic extends GetxController {
AuthRepositoryImpl authRepository = diAuth.get<AuthRepositoryImpl>();
final Module _module = Get.arguments;
void startTimer() {
_timer?.cancel();
secondsRemaining.value = 120;
@@ -68,13 +73,12 @@ class AuthLogic extends GetxController {
@override
void onInit() {
super.onInit();
tokenStorageService.init();
}
@override
void onReady() {
// TODO: implement onReady
super.onReady();
iLog('module111 : ${_module.toString()}');
}
@override
@@ -106,15 +110,15 @@ class AuthLogic extends GetxController {
Future<void> submitLoginForm() async {
if (!_isFormValid()) return;
iLog('module222 : ${_module.toString()}');
final loginRequestModel = _buildLoginRequest();
isLoading.value = true;
await safeCall<AuthResponseModel?>(
call: () => authRepository.login(authRequest: loginRequestModel.toJson()),
onSuccess: (result) async {
await tokenStorageService.saveRefreshToken(result!.refresh!);
await tokenStorageService.saveAccessToken(result!.access!);
//Get.offAndToNamed(Routes.home);
await tokenStorageService.saveModule(_module);
await tokenStorageService.saveRefreshToken(result?.refresh ?? '');
await tokenStorageService.saveAccessToken(result?.access ?? '');
},
onError: (error, stackTrace) {
if (error is DioException) {

View File

@@ -109,7 +109,10 @@ class AuthPage extends GetView<AuthLogic> {
},
prefixIcon: Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 6, 8),
child: vecWidget(Assets.vecCallSvg),
child: Assets.vec.callSvg.svg(
width: 12,
height: 12,
),
),
suffixIcon:
phoneController.value.text
@@ -168,7 +171,10 @@ class AuthPage extends GetView<AuthLogic> {
labelStyle: AppFonts.yekan13,
prefixIcon: Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
child: vecWidget(Assets.vecKeySvg),
child: Assets.vec.keySvg.svg(
width: 12,
height: 12,
),
),
boxConstraints: const BoxConstraints(
maxHeight: 34,

View File

@@ -1,7 +1,16 @@
import 'package:rasadyar_auth/data/models/local/module/module_model.dart';
import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart';
import 'package:rasadyar_core/core.dart';
class ModulesLogic extends GetxController {
List<ModuleModel> moduleList=[
ModuleModel(title: 'بازرسی', icon: Assets.icons.inspection.path, module: Module.inspection),
ModuleModel(title: 'دام', icon: Assets.icons.liveStock.path, module: Module.liveStocks),
];
RxnInt selectedIndex = RxnInt(null);
@override
@@ -9,6 +18,9 @@ class ModulesLogic extends GetxController {
super.onReady();
}
@override
void onClose() {
super.onClose();

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rasadyar_auth/presentation/routes/pages.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
@@ -8,8 +9,37 @@ class ModulesPage extends GetView<ModulesLogic> {
@override
Widget build(BuildContext context) {
final ModulesLogic logic = Get.put(ModulesLogic());
return Scaffold(
appBar: AppBar(
title: Text(
'انتخاب سامانه',
style: AppFonts.yekan18.copyWith(color: Colors.white),
),
centerTitle: true,
backgroundColor: AppColor.blueNormal,
),
body: GridView.builder(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
return Container();
itemBuilder: (context, index) {
final module = controller.moduleList[index];
return CardIcon(
title: module.title,
icon: module.icon,
onTap: () {
controller.selectedIndex.value = index;
Get.toNamed(AuthPaths.auth, arguments: module.module);
},
);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
physics: BouncingScrollPhysics(),
itemCount: controller.moduleList.length,
),
);
}
}

View File

@@ -1,3 +1,5 @@
import 'package:rasadyar_auth/presentation/pages/modules/logic.dart';
import 'package:rasadyar_auth/presentation/pages/modules/view.dart';
import 'package:rasadyar_auth/presentation/widget/captcha/logic.dart';
import 'package:rasadyar_core/core.dart';
@@ -12,10 +14,9 @@ sealed class AuthPages {
static List<GetPage> pages = [
GetPage(
name: AuthPaths.moduleList,
page: () => AuthPage(),
page: () => ModulesPage(),
binding: BindingsBuilder(() {
Get.lazyPut(() => AuthLogic());
Get.lazyPut(() => CaptchaWidgetLogic());
Get.lazyPut(() => ModulesLogic());
}),
),

View File

@@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
import 'package:rasadyar_auth/data/di/auth_di.dart';
import 'package:rasadyar_auth/data/models/response/captcha/captcha_response_model.dart';
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
import 'package:rasadyar_auth/data/utils/safe_call.dart';
import 'package:rasadyar_core/core.dart';
class CaptchaWidgetLogic extends GetxController
with StateMixin<CaptchaResponseModel> {
class CaptchaWidgetLogic extends GetxController with StateMixin<CaptchaResponseModel> {
Rx<TextEditingController> textController = TextEditingController().obs;
RxnString captchaKey = RxnString();
GlobalKey<FormState> formKey = GlobalKey<FormState>();

View File

@@ -9,7 +9,10 @@ class LogoWidget extends StatelessWidget {
return Column(
children: [
Row(),
Image.asset(Assets.imagesInnerSplash, width: 120, height: 120),
Assets.images.innerSplash.image(
width: 150,
height: 150,
),
Text(
'سامانه رصدیار',
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyNormal),

View File

@@ -0,0 +1 @@
{"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"],"packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf":["packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -1,5 +1,6 @@
library;
//other packages
export 'package:flutter_localizations/flutter_localizations.dart';
export 'package:flutter_map/flutter_map.dart';
@@ -7,6 +8,7 @@ export 'package:flutter_map_animations/flutter_map_animations.dart';
export 'package:flutter_rating_bar/flutter_rating_bar.dart';
export 'package:flutter_slidable/flutter_slidable.dart';
export 'package:font_awesome_flutter/font_awesome_flutter.dart';
export 'package:hive_ce_flutter/hive_flutter.dart';
//freezed
export 'package:freezed_annotation/freezed_annotation.dart';
export 'package:geolocator/geolocator.dart';

View File

@@ -107,6 +107,15 @@ class AppFonts {
height: _height,
);
static const TextStyle yekan8= TextStyle(
// Rounded from 10.24
fontFamily: yekan,
fontWeight: regular,
fontSize: 8,
height: _height,
);
static const TextStyle yekan61Bold = TextStyle(
fontFamily: yekan,
fontWeight: bold, // Use bold weight

View File

@@ -0,0 +1,486 @@
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
/// *****************************************************
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart' as _svg;
import 'package:vector_graphics/vector_graphics.dart' as _vg;
class $AssetsIconsGen {
const $AssetsIconsGen();
/// File path: assets/icons/add.svg
SvgGenImage get add => const SvgGenImage('assets/icons/add.svg');
/// File path: assets/icons/arrow_left.svg
SvgGenImage get arrowLeft => const SvgGenImage('assets/icons/arrow_left.svg');
/// File path: assets/icons/arrow_right.svg
SvgGenImage get arrowRight => const SvgGenImage('assets/icons/arrow_right.svg');
/// File path: assets/icons/bg_header_user_profile.svg
SvgGenImage get bgHeaderUserProfile => const SvgGenImage('assets/icons/bg_header_user_profile.svg');
/// File path: assets/icons/calendar.svg
SvgGenImage get calendar => const SvgGenImage('assets/icons/calendar.svg');
/// File path: assets/icons/calendar_search.svg
SvgGenImage get calendarSearch => const SvgGenImage('assets/icons/calendar_search.svg');
/// File path: assets/icons/call.svg
SvgGenImage get call => const SvgGenImage('assets/icons/call.svg');
/// File path: assets/icons/diagram.svg
SvgGenImage get diagram => const SvgGenImage('assets/icons/diagram.svg');
/// File path: assets/icons/download.svg
SvgGenImage get download => const SvgGenImage('assets/icons/download.svg');
/// File path: assets/icons/edit.svg
SvgGenImage get edit => const SvgGenImage('assets/icons/edit.svg');
/// File path: assets/icons/excel_download.svg
SvgGenImage get excelDownload => const SvgGenImage('assets/icons/excel_download.svg');
/// File path: assets/icons/filter.svg
SvgGenImage get filter => const SvgGenImage('assets/icons/filter.svg');
/// File path: assets/icons/gps.svg
SvgGenImage get gps => const SvgGenImage('assets/icons/gps.svg');
/// File path: assets/icons/information.svg
SvgGenImage get information => const SvgGenImage('assets/icons/information.svg');
/// File path: assets/icons/inspection.svg
SvgGenImage get inspection => const SvgGenImage('assets/icons/inspection.svg');
/// File path: assets/icons/key.svg
SvgGenImage get key => const SvgGenImage('assets/icons/key.svg');
/// File path: assets/icons/liveStock.svg
SvgGenImage get liveStock => const SvgGenImage('assets/icons/liveStock.svg');
/// File path: assets/icons/logout.svg
SvgGenImage get logout => const SvgGenImage('assets/icons/logout.svg');
/// File path: assets/icons/map.svg
SvgGenImage get map => const SvgGenImage('assets/icons/map.svg');
/// File path: assets/icons/map_marker.svg
SvgGenImage get mapMarker => const SvgGenImage('assets/icons/map_marker.svg');
/// File path: assets/icons/message_add.svg
SvgGenImage get messageAdd => const SvgGenImage('assets/icons/message_add.svg');
/// File path: assets/icons/pdf_download.svg
SvgGenImage get pdfDownload => const SvgGenImage('assets/icons/pdf_download.svg');
/// File path: assets/icons/picture_frame.svg
SvgGenImage get pictureFrame => const SvgGenImage('assets/icons/picture_frame.svg');
/// File path: assets/icons/place_holder.svg
SvgGenImage get placeHolder => const SvgGenImage('assets/icons/place_holder.svg');
/// File path: assets/icons/profile_circle.svg
SvgGenImage get profileCircle => const SvgGenImage('assets/icons/profile_circle.svg');
/// File path: assets/icons/profile_user.svg
SvgGenImage get profileUser => const SvgGenImage('assets/icons/profile_user.svg');
/// File path: assets/icons/receipt_discount.svg
SvgGenImage get receiptDiscount => const SvgGenImage('assets/icons/receipt_discount.svg');
/// File path: assets/icons/scan.svg
SvgGenImage get scan => const SvgGenImage('assets/icons/scan.svg');
/// File path: assets/icons/scan_barcode.svg
SvgGenImage get scanBarcode => const SvgGenImage('assets/icons/scan_barcode.svg');
/// File path: assets/icons/search.svg
SvgGenImage get search => const SvgGenImage('assets/icons/search.svg');
/// File path: assets/icons/security_time.svg
SvgGenImage get securityTime => const SvgGenImage('assets/icons/security_time.svg');
/// File path: assets/icons/setting.svg
SvgGenImage get setting => const SvgGenImage('assets/icons/setting.svg');
/// File path: assets/icons/tag_label.svg
SvgGenImage get tagLabel => const SvgGenImage('assets/icons/tag_label.svg');
/// File path: assets/icons/tag_user.svg
SvgGenImage get tagUser => const SvgGenImage('assets/icons/tag_user.svg');
/// File path: assets/icons/trash.svg
SvgGenImage get trash => const SvgGenImage('assets/icons/trash.svg');
/// File path: assets/icons/user.svg
SvgGenImage get user => const SvgGenImage('assets/icons/user.svg');
/// File path: assets/icons/user_square.svg
SvgGenImage get userSquare => const SvgGenImage('assets/icons/user_square.svg');
/// File path: assets/icons/virtual.svg
SvgGenImage get virtual => const SvgGenImage('assets/icons/virtual.svg');
/// List of all assets
List<SvgGenImage> get values => [
add,
arrowLeft,
arrowRight,
bgHeaderUserProfile,
calendar,
calendarSearch,
call,
diagram,
download,
edit,
excelDownload,
filter,
gps,
information,
inspection,
key,
liveStock,
logout,
map,
mapMarker,
messageAdd,
pdfDownload,
pictureFrame,
placeHolder,
profileCircle,
profileUser,
receiptDiscount,
scan,
scanBarcode,
search,
securityTime,
setting,
tagLabel,
tagUser,
trash,
user,
userSquare,
virtual,
];
}
class $AssetsImagesGen {
const $AssetsImagesGen();
/// File path: assets/images/inner_splash.webp
AssetGenImage get innerSplash => const AssetGenImage('assets/images/inner_splash.webp');
/// File path: assets/images/outter_splash.webp
AssetGenImage get outterSplash => const AssetGenImage('assets/images/outter_splash.webp');
/// File path: assets/images/place_holder.png
AssetGenImage get placeHolder => const AssetGenImage('assets/images/place_holder.png');
/// List of all assets
List<AssetGenImage> get values => [innerSplash, outterSplash, placeHolder];
}
class $AssetsVecGen {
const $AssetsVecGen();
/// File path: assets/vec/add.svg.vec
SvgGenImage get addSvg => const SvgGenImage.vec('assets/vec/add.svg.vec');
/// File path: assets/vec/arrow_left.svg.vec
SvgGenImage get arrowLeftSvg => const SvgGenImage.vec('assets/vec/arrow_left.svg.vec');
/// File path: assets/vec/arrow_right.svg.vec
SvgGenImage get arrowRightSvg => const SvgGenImage.vec('assets/vec/arrow_right.svg.vec');
/// File path: assets/vec/bg_header_user_profile.svg.vec
SvgGenImage get bgHeaderUserProfileSvg => const SvgGenImage.vec('assets/vec/bg_header_user_profile.svg.vec');
/// File path: assets/vec/calendar.svg.vec
SvgGenImage get calendarSvg => const SvgGenImage.vec('assets/vec/calendar.svg.vec');
/// File path: assets/vec/calendar_search.svg.vec
SvgGenImage get calendarSearchSvg => const SvgGenImage.vec('assets/vec/calendar_search.svg.vec');
/// File path: assets/vec/call.svg.vec
SvgGenImage get callSvg => const SvgGenImage.vec('assets/vec/call.svg.vec');
/// File path: assets/vec/diagram.svg.vec
SvgGenImage get diagramSvg => const SvgGenImage.vec('assets/vec/diagram.svg.vec');
/// File path: assets/vec/download.svg.vec
SvgGenImage get downloadSvg => const SvgGenImage.vec('assets/vec/download.svg.vec');
/// File path: assets/vec/edit.svg.vec
SvgGenImage get editSvg => const SvgGenImage.vec('assets/vec/edit.svg.vec');
/// File path: assets/vec/excel_download.svg.vec
SvgGenImage get excelDownloadSvg => const SvgGenImage.vec('assets/vec/excel_download.svg.vec');
/// File path: assets/vec/filter.svg.vec
SvgGenImage get filterSvg => const SvgGenImage.vec('assets/vec/filter.svg.vec');
/// File path: assets/vec/gps.svg.vec
SvgGenImage get gpsSvg => const SvgGenImage.vec('assets/vec/gps.svg.vec');
/// File path: assets/vec/information.svg.vec
SvgGenImage get informationSvg => const SvgGenImage.vec('assets/vec/information.svg.vec');
/// File path: assets/vec/inspection.svg.vec
SvgGenImage get inspectionSvg => const SvgGenImage.vec('assets/vec/inspection.svg.vec');
/// File path: assets/vec/key.svg.vec
SvgGenImage get keySvg => const SvgGenImage.vec('assets/vec/key.svg.vec');
/// File path: assets/vec/liveStock.svg.vec
SvgGenImage get liveStockSvg => const SvgGenImage.vec('assets/vec/liveStock.svg.vec');
/// File path: assets/vec/logout.svg.vec
SvgGenImage get logoutSvg => const SvgGenImage.vec('assets/vec/logout.svg.vec');
/// File path: assets/vec/map.svg.vec
SvgGenImage get mapSvg => const SvgGenImage.vec('assets/vec/map.svg.vec');
/// File path: assets/vec/map_marker.svg.vec
SvgGenImage get mapMarkerSvg => const SvgGenImage.vec('assets/vec/map_marker.svg.vec');
/// File path: assets/vec/message_add.svg.vec
SvgGenImage get messageAddSvg => const SvgGenImage.vec('assets/vec/message_add.svg.vec');
/// File path: assets/vec/pdf_download.svg.vec
SvgGenImage get pdfDownloadSvg => const SvgGenImage.vec('assets/vec/pdf_download.svg.vec');
/// File path: assets/vec/picture_frame.svg.vec
SvgGenImage get pictureFrameSvg => const SvgGenImage.vec('assets/vec/picture_frame.svg.vec');
/// File path: assets/vec/place_holder.svg.vec
SvgGenImage get placeHolderSvg => const SvgGenImage.vec('assets/vec/place_holder.svg.vec');
/// File path: assets/vec/profile_circle.svg.vec
SvgGenImage get profileCircleSvg => const SvgGenImage.vec('assets/vec/profile_circle.svg.vec');
/// File path: assets/vec/profile_user.svg.vec
SvgGenImage get profileUserSvg => const SvgGenImage.vec('assets/vec/profile_user.svg.vec');
/// File path: assets/vec/receipt_discount.svg.vec
SvgGenImage get receiptDiscountSvg => const SvgGenImage.vec('assets/vec/receipt_discount.svg.vec');
/// File path: assets/vec/scan.svg.vec
SvgGenImage get scanSvg => const SvgGenImage.vec('assets/vec/scan.svg.vec');
/// File path: assets/vec/scan_barcode.svg.vec
SvgGenImage get scanBarcodeSvg => const SvgGenImage.vec('assets/vec/scan_barcode.svg.vec');
/// File path: assets/vec/search.svg.vec
SvgGenImage get searchSvg => const SvgGenImage.vec('assets/vec/search.svg.vec');
/// File path: assets/vec/security_time.svg.vec
SvgGenImage get securityTimeSvg => const SvgGenImage.vec('assets/vec/security_time.svg.vec');
/// File path: assets/vec/setting.svg.vec
SvgGenImage get settingSvg => const SvgGenImage.vec('assets/vec/setting.svg.vec');
/// File path: assets/vec/tag_label.svg.vec
SvgGenImage get tagLabelSvg => const SvgGenImage.vec('assets/vec/tag_label.svg.vec');
/// File path: assets/vec/tag_user.svg.vec
SvgGenImage get tagUserSvg => const SvgGenImage.vec('assets/vec/tag_user.svg.vec');
/// File path: assets/vec/trash.svg.vec
SvgGenImage get trashSvg => const SvgGenImage.vec('assets/vec/trash.svg.vec');
/// File path: assets/vec/user.svg.vec
SvgGenImage get userSvg => const SvgGenImage.vec('assets/vec/user.svg.vec');
/// File path: assets/vec/user_square.svg.vec
SvgGenImage get userSquareSvg => const SvgGenImage.vec('assets/vec/user_square.svg.vec');
/// File path: assets/vec/virtual.svg.vec
SvgGenImage get virtualSvg => const SvgGenImage.vec('assets/vec/virtual.svg.vec');
/// List of all assets
List<SvgGenImage> get values => [
addSvg,
arrowLeftSvg,
arrowRightSvg,
bgHeaderUserProfileSvg,
calendarSvg,
calendarSearchSvg,
callSvg,
diagramSvg,
downloadSvg,
editSvg,
excelDownloadSvg,
filterSvg,
gpsSvg,
informationSvg,
inspectionSvg,
keySvg,
liveStockSvg,
logoutSvg,
mapSvg,
mapMarkerSvg,
messageAddSvg,
pdfDownloadSvg,
pictureFrameSvg,
placeHolderSvg,
profileCircleSvg,
profileUserSvg,
receiptDiscountSvg,
scanSvg,
scanBarcodeSvg,
searchSvg,
securityTimeSvg,
settingSvg,
tagLabelSvg,
tagUserSvg,
trashSvg,
userSvg,
userSquareSvg,
virtualSvg,
];
}
class Assets {
const Assets._();
static const $AssetsIconsGen icons = $AssetsIconsGen();
static const $AssetsImagesGen images = $AssetsImagesGen();
static const $AssetsVecGen vec = $AssetsVecGen();
}
class AssetGenImage {
const AssetGenImage(this._assetName, {this.size, this.flavors = const {}});
final String _assetName;
final Size? size;
final Set<String> flavors;
Image image({
Key? key,
AssetBundle? bundle,
ImageFrameBuilder? frameBuilder,
ImageErrorWidgetBuilder? errorBuilder,
String? semanticLabel,
bool excludeFromSemantics = false,
double? scale,
double? width,
double? height,
Color? color,
Animation<double>? opacity,
BlendMode? colorBlendMode,
BoxFit? fit,
AlignmentGeometry alignment = Alignment.center,
ImageRepeat repeat = ImageRepeat.noRepeat,
Rect? centerSlice,
bool matchTextDirection = false,
bool gaplessPlayback = true,
bool isAntiAlias = false,
String? package,
FilterQuality filterQuality = FilterQuality.medium,
int? cacheWidth,
int? cacheHeight,
}) {
return Image.asset(
_assetName,
key: key,
bundle: bundle,
frameBuilder: frameBuilder,
errorBuilder: errorBuilder,
semanticLabel: semanticLabel,
excludeFromSemantics: excludeFromSemantics,
scale: scale,
width: width,
height: height,
color: color,
opacity: opacity,
colorBlendMode: colorBlendMode,
fit: fit,
alignment: alignment,
repeat: repeat,
centerSlice: centerSlice,
matchTextDirection: matchTextDirection,
gaplessPlayback: gaplessPlayback,
isAntiAlias: isAntiAlias,
package: package,
filterQuality: filterQuality,
cacheWidth: cacheWidth,
cacheHeight: cacheHeight,
);
}
ImageProvider provider({AssetBundle? bundle, String? package}) {
return AssetImage(_assetName, bundle: bundle, package: package);
}
String get path => _assetName;
String get keyName => _assetName;
}
class SvgGenImage {
const SvgGenImage(this._assetName, {this.size, this.flavors = const {}}) : _isVecFormat = false;
const SvgGenImage.vec(this._assetName, {this.size, this.flavors = const {}}) : _isVecFormat = true;
final String _assetName;
final Size? size;
final Set<String> flavors;
final bool _isVecFormat;
_svg.SvgPicture svg({
Key? key,
bool matchTextDirection = false,
AssetBundle? bundle,
String? package,
double? width,
double? height,
BoxFit fit = BoxFit.contain,
AlignmentGeometry alignment = Alignment.center,
bool allowDrawingOutsideViewBox = false,
WidgetBuilder? placeholderBuilder,
String? semanticsLabel,
bool excludeFromSemantics = false,
_svg.SvgTheme? theme,
ColorFilter? colorFilter,
Clip clipBehavior = Clip.hardEdge,
@deprecated Color? color,
@deprecated BlendMode colorBlendMode = BlendMode.srcIn,
@deprecated bool cacheColorFilter = false,
}) {
final _svg.BytesLoader loader;
if (_isVecFormat) {
loader = _vg.AssetBytesLoader(_assetName, assetBundle: bundle, packageName: package);
} else {
loader = _svg.SvgAssetLoader(_assetName, assetBundle: bundle, packageName: package, theme: theme);
}
return _svg.SvgPicture(
loader,
key: key,
matchTextDirection: matchTextDirection,
width: width,
height: height,
fit: fit,
alignment: alignment,
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox,
placeholderBuilder: placeholderBuilder,
semanticsLabel: semanticsLabel,
excludeFromSemantics: excludeFromSemantics,
colorFilter: colorFilter ?? (color == null ? null : ColorFilter.mode(color, colorBlendMode)),
clipBehavior: clipBehavior,
cacheColorFilter: cacheColorFilter,
);
}
String get path => _assetName;
String get keyName => _assetName;
}

View File

@@ -1,3 +1,3 @@
export 'app_color.dart';
export 'app_fonts.dart';
export 'assets.dart';
export 'assets.gen.dart';

View File

@@ -0,0 +1,15 @@
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
/// *****************************************************
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
class FontFamily {
FontFamily._();
/// Font family: yekan
static const String yekan = 'yekan';
}

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
extension ColorUtils on Color {
Color _darken([double amount = 0.1]) {
assert(amount >= 0 && amount <= 1, 'مقدار تیرگی باید بین 0 و 1 باشد');
assert(amount >= 0 && amount <= 1, 'Amount must be between 0 and 1');
final hslColor = HSLColor.fromColor(this);
final newLightness = (hslColor.lightness - amount).clamp(0.0, 1.0);
final hslDarkerColor = hslColor.withLightness(newLightness);

View File

@@ -8,9 +8,12 @@ class RAppBar extends StatelessWidget implements PreferredSizeWidget {
final String title;
final Color backgroundColor;
final Color iconColor;
final bool hasBack;
final bool centerTitle;
final TextStyle? titleTextStyle;
final VoidCallback? onBackPressed;
final List<Widget>? additionalActions;
final int? leadingWidth;
final Widget? leading;
const RAppBar({
@@ -22,6 +25,9 @@ class RAppBar extends StatelessWidget implements PreferredSizeWidget {
this.onBackPressed,
this.additionalActions,
this.leading,
this.hasBack = true,
this.centerTitle = false,
this.leadingWidth
});
@override
@@ -32,17 +38,21 @@ class RAppBar extends StatelessWidget implements PreferredSizeWidget {
elevation: 0,
excludeHeaderSemantics: true,
scrolledUnderElevation: 0,
centerTitle: centerTitle,
titleTextStyle:
titleTextStyle ??
AppFonts.yekan16.copyWith(color:Colors.white),
title: Text(title),
leadingWidth: leadingWidth?.toDouble(),
leading:leading!=null ? Padding(
padding: const EdgeInsets.only(right: 16),
child: leading,
) : null,
titleSpacing: 8,
actions: [
if (additionalActions != null) ...additionalActions!,
if(hasBack)...{
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: IconButton(
@@ -51,6 +61,9 @@ class RAppBar extends StatelessWidget implements PreferredSizeWidget {
color: iconColor,
),
),
}
],
);

View File

@@ -60,16 +60,17 @@ class BottomNavigation1Item extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
vecWidget(
icon,
SvgGenImage.vec(icon).svg(
width: 32,
height: 32,
color: isSelected ? AppColor.blueNormal : Colors.white,
colorFilter: ColorFilter.mode(
isSelected ? AppColor.blueNormal : Colors.white,
BlendMode.srcIn)
),
const SizedBox(height: 5),
Text(
label,
style: AppFonts.yekan14.copyWith(
style: AppFonts.yekan10.copyWith(
color: isSelected ? AppColor.blueNormal : Colors.white,
),
),

View File

@@ -3,7 +3,7 @@ import 'package:rasadyar_core/core.dart';
class WaveBottomNavigationItem {
final String title;
final String icon;
final Widget icon;
WaveBottomNavigationItem({required this.title, required this.icon});
}
@@ -112,12 +112,7 @@ class _WaveBottomNavigationState extends State<WaveBottomNavigation> {
children: [
Tooltip(
message: item.title,
child: vecWidget(
item.icon,
color: Colors.white,
width: 32,
height: 32,
),
child: item.icon
),
/* Visibility(

View File

@@ -0,0 +1,60 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rasadyar_core/presentation/common/app_color.dart';
class BaseBottomSheet extends StatelessWidget {
const BaseBottomSheet({super.key, required this.child, this.height, this.bgColor});
final Widget child;
final double? height;
final Color? bgColor;
@override
Widget build(BuildContext context) {
return Container(
height: height ?? MediaQuery.of(context).size.height * 0.85,
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 20),
decoration: BoxDecoration(
color:bgColor?? Colors.white,
borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 8,
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
height: 20,
child: Stack(
alignment: AlignmentDirectional.center,
children: [
Container(
height: 3,
width: 50,
decoration: BoxDecoration(color: AppColor.darkGreyDark, borderRadius: BorderRadius.circular(8)),
),
Positioned(
left: 0,
child: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(CupertinoIcons.clear_circled),
),
),
],
),
),
SizedBox(height: 2),
child,
],
),
),
);
}
}

View File

@@ -49,7 +49,7 @@ class RElevated extends StatelessWidget {
),
minimumSize: Size(isFullWidth ? double.infinity : width, height),
padding: EdgeInsets.zero,
textStyle: textStyle ?? AppFonts.yekan24,
textStyle: textStyle ?? AppFonts.yekan18,
),
child:
isLoading

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/presentation/common/app_color.dart';
import 'package:rasadyar_core/presentation/common/assets.dart';
import 'package:rasadyar_core/presentation/utils/color_utils.dart';
import 'package:rasadyar_core/presentation/widget/vec_widget.dart';
import '../../common/assets.gen.dart';
class RFab extends StatefulWidget {
final VoidCallback? onPressed;
@@ -22,7 +22,7 @@ class RFab extends StatefulWidget {
RFab.smallAdd({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecAddSvg),
icon: Assets.vec.addSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.greenNormal,
key: key,
);
@@ -30,7 +30,7 @@ class RFab extends StatefulWidget {
RFab.add({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecAddSvg),
icon: Assets.vec.addSvg.svg(width: 40, height: 40),
backgroundColor: AppColor.greenNormal,
key: key,
);
@@ -41,7 +41,7 @@ class RFab extends StatefulWidget {
RFab.smallEdit({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecEditSvg),
icon: Assets.vec.addSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -49,7 +49,7 @@ class RFab extends StatefulWidget {
RFab.edit({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecEditSvg),
icon: Assets.vec.addSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -60,7 +60,7 @@ class RFab extends StatefulWidget {
RFab.smallDelete({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecTrashSvg),
icon: Assets.vec.trashSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.redNormal,
key: key,
);
@@ -68,7 +68,7 @@ class RFab extends StatefulWidget {
RFab.delete({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecTrashSvg),
icon: Assets.vec.trashSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.redNormal,
key: key,
);
@@ -79,7 +79,7 @@ class RFab extends StatefulWidget {
RFab.smallAction({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecScanSvg),
icon: Assets.vec.scanSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -87,7 +87,7 @@ class RFab extends StatefulWidget {
RFab.action({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecScanSvg),
icon: Assets.vec.scanSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -98,7 +98,7 @@ class RFab extends StatefulWidget {
RFab.smallFilter({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecFilterSvg),
icon: Assets.vec.scanSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -106,7 +106,7 @@ class RFab extends StatefulWidget {
RFab.filter({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecFilterSvg),
icon: Assets.vec.scanSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -117,7 +117,7 @@ class RFab extends StatefulWidget {
RFab.smallDownload({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecDownloadSvg),
icon: Assets.vec.downloadSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -125,7 +125,7 @@ class RFab extends StatefulWidget {
RFab.download({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecDownloadSvg),
icon: Assets.vec.downloadSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -136,7 +136,7 @@ class RFab extends StatefulWidget {
RFab.smallExcel({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecDownloadSvg),
icon: Assets.vec.excelDownloadSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.greenDark,
key: key,
);
@@ -144,7 +144,7 @@ class RFab extends StatefulWidget {
RFab.excel({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecDownloadSvg),
icon: Assets.vec.excelDownloadSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.greenDark,
key: key,
);
@@ -155,7 +155,7 @@ class RFab extends StatefulWidget {
RFab.smallBack({required VoidCallback? onPressed, Key? key})
: this.small(
onPressed: onPressed,
icon: vecWidget(Assets.vecArrowLeftSvg),
icon: Assets.vec.arrowLeftSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);
@@ -163,7 +163,7 @@ class RFab extends StatefulWidget {
RFab.back({required VoidCallback? onPressed, Key? key})
: this(
onPressed: onPressed,
icon: vecWidget(Assets.vecArrowLeftSvg),
icon: Assets.vec.arrowLeftSvg.svg(width: 20, height: 20),
backgroundColor: AppColor.blueNormal,
key: key,
);

View File

@@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/presentation/common/app_color.dart';
import 'package:rasadyar_core/presentation/common/assets.dart';
import 'package:rasadyar_core/presentation/common/assets.gen.dart';
import 'package:rasadyar_core/presentation/utils/color_utils.dart';
import 'package:rasadyar_core/presentation/widget/vec_widget.dart';
class RFabOutlined extends StatefulWidget {
final Widget icon;
@@ -69,10 +68,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.greenNormal,
borderColor: AppColor.greenNormal,
icon: vecWidget2(
Assets.vecAddSvg,
color: AppColor.greenNormal,
icon: Assets.vec.addSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -81,12 +83,14 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.greenNormal,
icon: vecWidget(
Assets.vecAddSvg,
color:
icon: Assets.vec.addSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -96,12 +100,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.greenNormal,
borderColor: AppColor.greenNormal,
icon: vecWidget(
Assets.vecAddSvg,
color:
icon: Assets.vec.addSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -110,12 +115,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.greenNormal,
icon: vecWidget(
Assets.vecAddSvg,
color:
icon: Assets.vec.addSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -128,12 +134,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecEditSvg,
color:
icon: Assets.vec.editSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -142,12 +149,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecEditSvg,
color:
icon: Assets.vec.editSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -157,12 +165,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecEditSvg,
color:
icon: Assets.vec.editSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -171,12 +180,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecEditSvg,
color:
icon: Assets.vec.editSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -189,12 +199,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.redNormal,
borderColor: AppColor.redNormal,
icon: vecWidget(
Assets.vecTrashSvg,
color:
icon: Assets.vec.trashSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.redNormal
: AppColor.redNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -203,12 +214,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.redNormal,
icon: vecWidget(
Assets.vecTrashSvg,
color:
icon: Assets.vec.trashSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.redNormal
: AppColor.redNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -218,12 +230,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.redNormal,
borderColor: AppColor.redNormal,
icon: vecWidget(
Assets.vecTrashSvg,
color:
icon: Assets.vec.trashSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.redNormal
: AppColor.redNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -232,12 +245,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.redNormal,
icon: vecWidget(
Assets.vecTrashSvg,
color:
icon: Assets.vec.trashSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.redNormal
: AppColor.redNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -250,12 +264,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecScanSvg,
color:
icon: Assets.vec.scanSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -264,12 +279,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecScanSvg,
color:
icon: Assets.vec.scanSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -279,12 +295,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecScanSvg,
color:
icon: Assets.vec.scanSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -293,12 +310,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecScanSvg,
color:
icon: Assets.vec.scanSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -311,12 +329,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecFilterSvg,
color:
icon: Assets.vec.filterSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -325,12 +344,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecFilterSvg,
color:
icon: Assets.vec.filterSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -340,12 +360,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecFilterSvg,
color:
icon: Assets.vec.filterSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -354,12 +375,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecFilterSvg,
color:
icon: Assets.vec.filterSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -372,12 +394,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.downloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -386,12 +409,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.downloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -401,12 +425,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.downloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -415,12 +440,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.downloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -433,12 +459,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.greenDark,
borderColor: AppColor.greenDark,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.excelDownloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -447,12 +474,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.greenDark,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.excelDownloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -462,12 +490,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.greenDark,
borderColor: AppColor.greenDark,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.excelDownloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -476,12 +505,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.greenDark,
icon: vecWidget(
Assets.vecDownloadSvg,
color:
icon: Assets.vec.excelDownloadSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.greenDark
: AppColor.greenDark.disabledColor,
? AppColor.greenNormal
: AppColor.greenNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -494,12 +524,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecArrowLeftSvg,
color:
icon: Assets.vec.arrowLeftSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -508,12 +539,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecArrowLeftSvg,
color:
icon: Assets.vec.arrowLeftSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -523,12 +555,13 @@ class RFabOutlined extends StatefulWidget {
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
borderColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecArrowLeftSvg,
color:
icon: Assets.vec.arrowLeftSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -537,12 +570,13 @@ class RFabOutlined extends StatefulWidget {
key: key,
onPressed: onPressed,
backgroundColor: AppColor.blueNormal,
icon: vecWidget(
Assets.vecArrowLeftSvg,
color:
icon: Assets.vec.arrowLeftSvg.svg(
colorFilter: ColorFilter.mode(
onPressed != null
? AppColor.blueNormal
: AppColor.blueNormal.disabledColor,
BlendMode.srcIn,
),
),
);
@@ -551,6 +585,7 @@ class RFabOutlined extends StatefulWidget {
class _RFabOutlinedState extends State<RFabOutlined> {
bool isOnPressed = false;
@override
Widget build(BuildContext context) {
return OutlinedButton(
@@ -598,8 +633,7 @@ class _RFabOutlinedState extends State<RFabOutlined> {
),
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
child: widget.icon
child: widget.icon,
);
}
}

View File

@@ -32,10 +32,10 @@ class ROutlinedElevatedIcon extends StatefulWidget {
Widget? icon;
@override
State<ROutlinedElevatedIcon> createState() => _ROutlinedElevatedStateIcon();
State<ROutlinedElevatedIcon> createState() => _ROutlinedElevatedIconState();
}
class _ROutlinedElevatedStateIcon extends State<ROutlinedElevatedIcon> {
class _ROutlinedElevatedIconState extends State<ROutlinedElevatedIcon> {
@override
Widget build(BuildContext context) {
return OutlinedButton.icon(

View File

@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
class CardIcon extends StatelessWidget {
const CardIcon({
super.key,
required this.title,
required this.icon,
this.onTap,
});
final String title;
final String icon;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: AppColor.blueNormal, width: 1),
),
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.white,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgGenImage(icon).svg(width: 50, height: 50),
const SizedBox(height: 8),
Text(
title,
style: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,112 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
class CustomChip extends StatelessWidget {
final bool isSelected;
final String title;
final int index;
final Function(int) onTap;
final Color selectedColor;
final Color unSelectedColor;
const CustomChip({
super.key,
this.isSelected = false,
required this.title,
required this.index,
required this.onTap,
this.selectedColor = AppColor.blueNormal,
this.unSelectedColor = AppColor.whiteGreyNormal,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onTap.call(index),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: isSelected ? selectedColor : unSelectedColor,
borderRadius: BorderRadius.circular(8),
border:
isSelected
? Border.fromBorderSide(BorderSide.none)
: Border.all(width: 0.25, color: const Color(0xFFB0B0B0)),
),
child: Text(
title,
textAlign: TextAlign.center,
style:
isSelected
? AppFonts.yekan10.copyWith(color: AppColor.whiteLight)
: AppFonts.yekan10,
),
),
);
}
}
class RFilterChips extends StatelessWidget {
const RFilterChips({
super.key,
this.isSelected = false,
required this.title,
required this.index,
required this.onTap,
this.selectedColor = AppColor.blueNormal,
this.unSelectedColor = AppColor.whiteGreyDark,
});
final bool isSelected;
final String title;
final int index;
final Function(int) onTap;
final Color selectedColor;
final Color unSelectedColor;
@override
/* Widget build(BuildContext context) {
return FilterChip(
labelStyle: isSelected
? AppFonts.yekan10.copyWith(color: AppColor.mediumGreyDarkActive)
: AppFonts.yekan10,
label: Text(
title,
textAlign: TextAlign.center),
selected: isSelected,
showCheckmark: false, // مخفی‌کردن چک‌مارک پیش‌فرض
avatar: isSelected
? Icon(
Icons.star, // آیکون دلخواه به‌جای چک‌مارک
size: 18,
color: Colors.orange,
)
: null,
selectedColor: selectedColor,
onSelected: (bool selected) {
onTap.call(index);
},
);
}*/
Widget build(BuildContext context) {
return RawChip(
label: Text(title),
labelStyle: isSelected
? AppFonts.yekan10.copyWith(color: AppColor.mediumGreyDarkActive)
: AppFonts.yekan10,
selected: isSelected,
onSelected: (bool selected) => onTap(index),
backgroundColor: Colors.grey[200],
selectedColor: selectedColor,
showCheckmark: false,
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(width: 1, color: isSelected? selectedColor :unSelectedColor),
),
deleteIcon: Icon(CupertinoIcons.clear_circled),
onDeleted: isSelected ? () => onTap(index) : null,
);
}
}

View File

@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'draggable_bottom_sheet_controller.dart';
class DraggableBottomSheet2 extends GetView<DraggableBottomSheetController> {
final Color? backgroundColor;
const DraggableBottomSheet2({super.key, this.backgroundColor = Colors.white});
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (controller.isVisible.value && !controller.isVisible.value) {
controller.show();
}
});
return ObxValue((data) {
return Stack(
children: [
// پس‌زمینه تیره
Positioned.fill(
child: GestureDetector(
onTap: () {},
child: Container(color: Colors.black54),
),
),
// محتوای BottomSheet
AnimatedPositioned(
duration: Duration(milliseconds: 300),
curve: Curves.easeOut,
left: 0,
right: 0,
bottom: 0,
child: GestureDetector(
onVerticalDragUpdate: (details) {
controller.updateHeight(details.primaryDelta);
},
child: Container(
height: 350,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 10,
),
],
),
child: Column(
children: [
GestureDetector(
onTap: () {},
child: Container(
padding: EdgeInsets.all(10),
child: Icon(Icons.drag_handle),
),
),
Expanded(
child: controller.items[data.value],
),
],
),
),
),
),
],
);
}, controller.currentIndex);
}
}

View File

@@ -129,7 +129,8 @@ import 'draggable_bottom_sheet.dart';
class DraggableBottomSheetController extends GetxController {
final RxBool isVisible = false.obs;
final RxDouble currentHeight = 200.0.obs;
RxList<Widget> items = <Widget>[].obs;
RxInt currentIndex = 0.obs;
late double initialHeight;
late double minHeight;
late double maxHeight;
@@ -158,9 +159,5 @@ class DraggableBottomSheetController extends GetxController {
}
}
@override
void onInit() {
super.onInit();
}
}

View File

@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import '../../common/app_color.dart';
import '../../common/app_fonts.dart' show AppFonts;
enum InputType { text, number, email, password }
class TextFiledFixedHint extends StatefulWidget {
const TextFiledFixedHint({
super.key,
required this.hintText,
required this.onChanged,
this.inputType = InputType.text,
this.initialValue,
this.controller,
this.keyboardType,
this.textInputAction,
this.enabled,
this.readOnly,
this.maxLines,
this.minLines,
});
final String hintText;
final InputType inputType;
final ValueChanged<String>? onChanged;
final String? initialValue;
final TextEditingController? controller;
final TextInputType? keyboardType;
final TextInputAction? textInputAction;
final bool? enabled;
final bool? readOnly;
final int? maxLines;
final int? minLines;
@override
State<TextFiledFixedHint> createState() => _TextFiledFixedHintState();
}
class _TextFiledFixedHintState extends State<TextFiledFixedHint> {
TextEditingController? tmpController;
@override
void initState() {
super.initState();
if (widget.controller == null) {
tmpController = TextEditingController(text: widget.initialValue);
if (widget.initialValue != null) {
tmpController?.text = widget.initialValue!;
}
} else {
tmpController = widget.controller;
}
}
@override
Widget build(BuildContext context) {
return Container(
height: 40,
width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: AppColor.darkGreyLight),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 3),
child: Text(widget.hintText, style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyNormalActive)),
),
Expanded(
child: TextField(
controller: tmpController,
keyboardType: widget.inputType == InputType.number ? TextInputType.number : widget.keyboardType,
textInputAction: widget.textInputAction,
onChanged: widget.onChanged,
enabled: widget.enabled,
readOnly: widget.readOnly ?? false,
maxLines: widget.maxLines ?? 1,
minLines: widget.minLines ?? 1,
cursorHeight: 25,
textAlignVertical: TextAlignVertical.top,
obscureText: widget.inputType == InputType.password,
textAlign: widget.inputType == InputType.number ? TextAlign.start : TextAlign.start,
textDirection: widget.inputType == InputType.number ? TextDirection.ltr : TextDirection.rtl,
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyNormalActive),
decoration: InputDecoration(border: InputBorder.none),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
class CustomMarker {
final LatLng point;
final VoidCallback? onTap;
final int? id;
CustomMarker({ this.id, required this.point, this.onTap});
}

View File

@@ -0,0 +1,170 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:rasadyar_core/utils/logger_utils.dart';
import 'custom_marker.dart';
enum ErrorLocationType { serviceDisabled, permissionDenied, none }
class MapWidgetLogic extends GetxController with GetTickerProviderStateMixin {
Rx<LatLng> currentLocation = LatLng(35.824891, 50.948025).obs;
String tileType = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
RxList<CustomMarker> markers = <CustomMarker>[].obs;
RxList<LatLng> allMarkers = <LatLng>[].obs;
Rx<MapController> mapController = MapController().obs;
RxList<ErrorLocationType> errorLocationType = RxList();
late final AnimatedMapController animatedMapController;
Timer? _debounceTimer;
RxBool isLoading = false.obs;
@override
void onInit() {
super.onInit();
animatedMapController = AnimatedMapController(
vsync: this,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
cancelPreviousAnimations: true,
);
locationServiceEnabled().then((value) {
if (!value) {
errorLocationType.add(ErrorLocationType.serviceDisabled);
}
});
checkPermission().then((value) {
if (!value) {
errorLocationType.add(ErrorLocationType.permissionDenied);
}
});
listenToLocationServiceStatus().listen((event) {
if (!event) {
errorLocationType.add(ErrorLocationType.serviceDisabled);
} else {
errorLocationType.remove(ErrorLocationType.serviceDisabled);
}
});
}
@override
void onReady() {
super.onReady();
determineCurrentPosition();
}
@override
void onClose() {
super.onClose();
_debounceTimer?.cancel();
animatedMapController.dispose();
mapController.close();
}
Stream<bool> listenToLocationServiceStatus() {
return Geolocator.getServiceStatusStream().map((status) {
return status == ServiceStatus.enabled;
});
}
Future<bool> locationServiceEnabled() async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return false;
}
return true;
}
Future<bool> checkPermission({bool request = false}) async {
try {
final LocationPermission permission = await Geolocator.checkPermission();
switch (permission) {
case LocationPermission.denied:
final LocationPermission requestResult =
await Geolocator.requestPermission();
return requestResult != LocationPermission.denied &&
requestResult != LocationPermission.deniedForever;
case LocationPermission.deniedForever:
return request ? await Geolocator.openAppSettings() : false;
case LocationPermission.always:
case LocationPermission.whileInUse:
return true;
default:
return false;
}
} catch (e) {
eLog(e);
return await Geolocator.openLocationSettings();
}
}
Future<void> determineCurrentPosition() async {
final position = await Geolocator.getCurrentPosition(
locationSettings: AndroidSettings(accuracy: LocationAccuracy.best),
);
final latLng = LatLng(position.latitude, position.longitude);
currentLocation.value = latLng;
markers.add(
CustomMarker(id: -1, point: latLng, ),
);
animatedMapController.animateTo(
dest: latLng,
zoom: 18,
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 1500),
);
}
void debouncedUpdateVisibleMarkers({required LatLng center}) {
_debounceTimer?.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 300), () {
final filtered = filterNearbyMarkers({
'markers': allMarkers,
'centerLat': center.latitude,
'centerLng': center.longitude,
'radius': 1000.0,
});
// markers.addAll(filtered);
});
}
List<LatLng> filterNearbyMarkers(Map<String, dynamic> args) {
final List<LatLng> rawMarkers = args['markers'];
final double centerLat = args['centerLat'];
final double centerLng = args['centerLng'];
final double radiusInMeters = args['radius'];
final center = LatLng(centerLat, centerLng);
final distance = Distance();
return rawMarkers
.where((marker) => distance(center, marker) <= radiusInMeters)
.toList();
}
void addMarker(CustomMarker marker) {
markers.add(marker);
}
void setMarkers(List<CustomMarker> newMarkers) {
markers.value = newMarkers;
}
void clearMarkers() {
markers.clear();
}
}

View File

@@ -0,0 +1,207 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:rasadyar_core/presentation/common/app_color.dart';
import 'package:rasadyar_core/presentation/common/app_fonts.dart';
import 'package:rasadyar_core/presentation/common/assets.gen.dart';
import 'package:rasadyar_core/presentation/widget/buttons/elevated.dart';
import 'package:rasadyar_core/presentation/widget/buttons/fab.dart';
import 'package:rasadyar_core/presentation/widget/buttons/outline_elevated.dart';
import 'logic.dart';
class MapWidget extends GetView<MapWidgetLogic> {
final VoidCallback? initOnTap;
final Widget? initMarkerWidget;
final Widget markerWidget;
const MapWidget({
this.initOnTap,
this.initMarkerWidget,
required this.markerWidget,
super.key,
});
@override
Widget build(BuildContext context) {
return Stack(
children: [
ObxValue((errorType) {
if (errorType.isNotEmpty) {
if (errorType.contains(ErrorLocationType.serviceDisabled)) {
Future.microtask(() {
Get.defaultDialog(
title: 'خطا',
content: const Text('سرویس مکان‌یابی غیرفعال است'),
cancel: ROutlinedElevated(
text: 'بررسی مجدد',
width: 120,
textStyle: AppFonts.yekan16,
onPressed: () async {
var service = await controller.locationServiceEnabled();
if (service) {
controller.errorLocationType.remove(
ErrorLocationType.serviceDisabled,
);
Get.back();
}
// Don't call Get.back() if service is still disabled
},
),
confirm: RElevated(
text: 'روشن کردن',
textStyle: AppFonts.yekan16,
width: 120,
onPressed: () async {
var res = await Geolocator.openLocationSettings();
if (res) {
var service = await controller.locationServiceEnabled();
if (service) {
controller.errorLocationType.remove(
ErrorLocationType.serviceDisabled,
);
Get.back();
}
}
},
),
contentPadding: EdgeInsets.all(8),
onWillPop: () async {
return controller.errorLocationType.isEmpty;
},
barrierDismissible: false,
);
});
} else {
Future.microtask(() {
Get.defaultDialog(
title: 'خطا',
content: const Text(' دسترسی به سرویس مکان‌یابی غیرفعال است'),
cancel: ROutlinedElevated(
text: 'بررسی مجدد',
width: 120,
textStyle: AppFonts.yekan16,
onPressed: () async {
await controller.checkPermission();
},
),
confirm: RElevated(
text: 'اجازه دادن',
textStyle: AppFonts.yekan16,
width: 120,
onPressed: () async {
var res = await controller.checkPermission(request: true);
if (res) {
controller.errorLocationType.remove(
ErrorLocationType.permissionDenied,
);
Get.back();
}
},
),
contentPadding: EdgeInsets.all(8),
onWillPop: () async {
return controller.errorLocationType.isEmpty;
},
barrierDismissible: false,
);
});
}
}
return const SizedBox.shrink();
}, controller.errorLocationType),
_buildMap(),
_buildGpsButton(),
_buildFilterButton(),
],
);
}
Widget _buildMap() {
return ObxValue((currentLocation) {
return FlutterMap(
mapController: controller.animatedMapController.mapController,
options: MapOptions(
initialCenter: currentLocation.value,
initialZoom: 18,
onPositionChanged: (camera, hasGesture) {
if (hasGesture) {
controller.debouncedUpdateVisibleMarkers(center: camera.center);
}
//controller.debouncedUpdateVisibleMarkers(center: camera.center);
},
),
children: [
TileLayer(urlTemplate: controller.tileType),
ObxValue((markers) {
return MarkerLayer(
markers:
markers
.map(
(e) => Marker(
point: e.point,
child: GestureDetector(
onTap: e.id != -1 ? e.onTap : initOnTap,
child:
e.id != -1
? markerWidget
: initMarkerWidget ?? SizedBox.shrink(),
),
),
)
.toList(),
);
}, controller.markers),
],
);
}, controller.currentLocation);
}
Widget _buildGpsButton() {
return Positioned(
right: 10,
bottom: 83,
child: ObxValue((data) {
return RFab.small(
backgroundColor: AppColor.greenNormal,
isLoading: data.value,
icon: Assets.vec.gpsSvg.svg(),
onPressed: () async {
controller.isLoading.value = true;
await controller.determineCurrentPosition();
controller.isLoading.value = false;
},
);
}, controller.isLoading),
);
}
Widget _buildFilterButton() {
return Positioned(
right: 10,
bottom: 30,
child: RFab.small(
backgroundColor: AppColor.blueNormal,
icon: Assets.vec.filterSvg.svg(width: 24, height: 24),
onPressed: () {},
),
);
}
/*Marker markerWidget({required LatLng marker, required VoidCallback onTap}) {
return Marker(
point: marker,
child: GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: SizedBox(
width: 36,
height: 36,
child: Assets.vec.mapMarkerSvg.svg(width: 30, height: 30),
),
),
);
}*/
}

View File

@@ -0,0 +1,143 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rasadyar_core/presentation/common/app_color.dart';
import 'package:rasadyar_core/presentation/common/app_fonts.dart';
class OverlayDropdownWidget<T> extends StatefulWidget {
final List<T> items;
final T? selectedItem;
final T? initialValue;
final Widget Function(T item) itemBuilder;
final Widget Function(T? selected) labelBuilder;
final void Function(T selected)? onChanged;
final EdgeInsets? contentPadding;
const OverlayDropdownWidget({
super.key,
required this.items,
required this.itemBuilder,
required this.labelBuilder,
this.initialValue,
this.onChanged,
this.selectedItem,
this.contentPadding
});
@override
State<OverlayDropdownWidget<T>> createState() => _OverlayDropdownState<T>();
}
class _OverlayDropdownState<T> extends State<OverlayDropdownWidget<T>> {
final GlobalKey _key = GlobalKey();
OverlayEntry? _overlayEntry;
final RxBool _isOpen = false.obs;
T? selectedItem ;
@override
void initState() {
super.initState();
selectedItem = widget.selectedItem ?? widget.initialValue;
}
void _showOverlay() {
final renderBox = _key.currentContext!.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
_overlayEntry = OverlayEntry(
builder:
(_) => GestureDetector(
onTap: _removeOverlay,
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: offset.dy + size.height + 4,
width: size.width,
child: Material(
elevation: 4,
borderRadius: BorderRadius.circular(8),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: AppColor.darkGreyLight),
borderRadius: BorderRadius.circular(8),
),
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children:
widget.items.map((item) {
return InkWell(
onTap: () {
widget.onChanged?.call(item);
setState(() {
selectedItem = item;
});
_removeOverlay();
},
child: Padding(
padding:widget.contentPadding?? const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: widget.itemBuilder(item),
),
);
}).toList(),
),
),
),
),
],
),
),
);
Overlay.of(context).insert(_overlayEntry!);
_isOpen.value = true;
}
void _removeOverlay() {
_overlayEntry?.remove();
_overlayEntry = null;
_isOpen.value = false;
}
@override
void dispose() {
_removeOverlay();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
key: _key,
onTap: () {
_isOpen.value ? _removeOverlay() : _showOverlay();
},
child: Container(
height: 40,
width: Get.width,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: AppColor.darkGreyLight),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
widget.labelBuilder(selectedItem),
Icon(
_isOpen.value
? CupertinoIcons.chevron_up
: CupertinoIcons.chevron_down,
),
],
),
),
);
}
}

View File

@@ -22,7 +22,7 @@ SvgPicture vecWidget(
}
Widget vecWidgetWithOnTap({
required String assets,
required Widget child,
required VoidCallback onTap,
double? width,
double? height,
@@ -31,14 +31,7 @@ Widget vecWidgetWithOnTap({
}) {
return InkWell(
onTap: onTap,
child: SvgPicture(
AssetBytesLoader(assets),
width: width,
height: height,
fit: fit ?? BoxFit.contain,
colorFilter:
color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null,
),
child: child
);
}

View File

@@ -6,6 +6,7 @@ export 'buttons/outline_elevated.dart';
export 'buttons/outline_elevated_icon.dart';
export 'buttons/text_button.dart';
export 'draggable_bottom_sheet/draggable_bottom_sheet.dart';
export 'draggable_bottom_sheet/draggable_bottom_sheet2.dart';
export 'draggable_bottom_sheet/draggable_bottom_sheet_controller.dart';
export 'draggable_bottom_sheet/bottom_sheet_manger.dart';
export 'inputs/r_input.dart';
@@ -14,3 +15,8 @@ export 'pagination/show_more.dart';
export 'tabs/new_tab.dart';
export 'tabs/tab.dart';
export 'vec_widget.dart';
export 'card/card_with_icon_with_border.dart';
export 'chips/r_chips.dart';
export 'overlay_dropdown_widget/view.dart';
export 'inputs/input_fixed_hint.dart';
export 'bottom_sheet/base_bottom_sheet.dart';

View File

@@ -1,14 +1,17 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:rasadyar_core/core.dart';
typedef AsyncCallback<T> = Future<T> Function();
import '../core.dart';
typedef AppAsyncCallback<T> = Future<T> Function();
typedef ErrorCallback = void Function(dynamic error, StackTrace? stackTrace);
typedef VoidCallback = void Function();
// تعریف دقیق تابع safeCall
Future<void> safeCall<T>({
required AsyncCallback<T> call,
/// this is global safe call function
/// A utility function to safely cal l an asynchronous function with error
/// handling and optional loading, success, and error messages.
///
Future<void> gSafeCall<T>({
required AppAsyncCallback<T> call,
Function(T result)? onSuccess,
ErrorCallback? onError,
VoidCallback? onComplete,
@@ -17,6 +20,8 @@ Future<void> safeCall<T>({
bool showSuccess = false,
bool showToast = false,
bool showSnackBar = false,
bool retryOnAuthError = false,
Function()? onTokenRefresh,
Function()? onShowLoading,
Function()? onHideLoading,
Function()? onShowSuccessMessage,
@@ -34,18 +39,34 @@ Future<void> safeCall<T>({
}
onSuccess?.call(result);
} catch (error, stackTrace) {
if (retryOnAuthError && isTokenExpiredError(error)) {
try {
await onTokenRefresh?.call();
final retryResult = await call();
if (showSuccess) {
(onShowSuccessMessage ?? _defaultShowSuccessMessage)();
}
onSuccess?.call(retryResult);
return;
} catch (retryError, retryStackTrace) {
if (showError) {
(onShowErrorMessage ?? _defaultShowErrorMessage)();
}
onError?.call(retryError, retryStackTrace);
if (kDebugMode) {
print('safeCall retry error: $retryError\n$retryStackTrace');
}
}
} else {
if (showError) {
(onShowErrorMessage ?? _defaultShowErrorMessage)();
}
onError?.call(error, stackTrace);
if (kDebugMode) {
print('safeCall error: $error\n$stackTrace');
}
}
} finally {
if (showLoading) {
(onHideLoading ?? _defaultHideLoading)();
@@ -70,3 +91,7 @@ void _defaultShowSuccessMessage() {
void _defaultShowErrorMessage() {
// پیاده‌سازی پیش‌فرض
}
bool isTokenExpiredError(dynamic error) {
return error is DioException && error.response?.statusCode == 401;
}

View File

@@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.4.4"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args:
dependency: transitive
description:
@@ -153,6 +161,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
color:
dependency: transitive
description:
name: color
sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb
url: "https://pub.dev"
source: hosted
version: "3.0.0"
convert:
dependency: transitive
description:
@@ -262,6 +278,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_gen_core:
dependency: transitive
description:
name: flutter_gen_core
sha256: "3eaa2d3d8be58267ac4cd5e215ac965dd23cae0410dc073de2e82e227be32bfc"
url: "https://pub.dev"
source: hosted
version: "5.10.0"
flutter_gen_runner:
dependency: "direct main"
description:
name: flutter_gen_runner
sha256: e74b4ead01df3e8f02e73a26ca856759dbbe8cb3fd60941ba9f4005cd0cd19c9
url: "https://pub.dev"
source: hosted
version: "5.10.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -493,6 +525,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
hashcodes:
dependency: transitive
description:
name: hashcodes
sha256: "80f9410a5b3c8e110c4b7604546034749259f5d6dcca63e0d3c17c9258f1a651"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
hive_ce:
dependency: "direct main"
description:
@@ -541,6 +581,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
image_size_getter:
dependency: transitive
description:
name: image_size_getter
sha256: "9a299e3af2ebbcfd1baf21456c3c884037ff524316c97d8e56035ea8fdf35653"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
intl:
dependency: "direct main"
description:
@@ -885,6 +933,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62
url: "https://pub.dev"
source: hosted
version: "6.0.2"
pretty_dio_logger:
dependency: "direct main"
description:

View File

@@ -36,6 +36,9 @@ dependencies:
flutter_svg: ^2.0.17
font_awesome_flutter: ^10.8.0
#Generator
flutter_gen_runner: ^5.10.0
##state manger
get: ^4.7.2
@@ -76,6 +79,7 @@ dev_dependencies:
freezed: ^3.0.3
json_serializable: ^6.9.4
##test
mocktail: ^1.0.4
get_test: ^4.0.1
@@ -85,3 +89,9 @@ dev_dependencies:
flutter:
uses-material-design: true
assets:
- assets/
- assets/vec/
- assets/icons/

View File

@@ -0,0 +1,112 @@
import 'package:flutter/foundation.dart';
import 'package:rasadyar_core/core.dart';
import 'i_local_storage.dart';
class HiveLocalStorage implements ILocalStorage {
HiveLocalStorage() {
Hive.initFlutter();
}
final Map<String, Box> _boxes = {};
@override
Future init() async => await Hive.initFlutter();
@override
Future<void> openBox<T>(
String boxName, {
HiveCipher? encryptionCipher,
bool crashRecovery = true,
String? path,
Uint8List? bytes,
String? collection,
}) async {
if (!_boxes.containsKey(boxName)) {
final box = await Hive.openBox<T>(
boxName,
encryptionCipher: encryptionCipher,
crashRecovery: crashRecovery,
);
_boxes[boxName] = box;
}
}
@override
T? read<T>({required String boxName, required String key}) {
try {
Box? box = getBox(boxName);
return box?.get(key) as T?;
} on Exception catch (e) {
eLog(e);
return null;
}
}
@override
Future<void> add({required String boxName, required dynamic value}) async {
Box<dynamic>? box = getBox(boxName);
await box?.add(value);
}
@override
Future<void> addAll({
required String boxName,
required Iterable values,
}) async {
Box<dynamic>? box = getBox(boxName);
await box?.addAll(values);
}
Box<T>? getBox<T>(String boxName) {
final box = _boxes[boxName];
if (box is Box<T>) {
return box;
} else {
throw Exception('Box $boxName is not of exist');
}
}
@override
Future<void> clear(String boxName) async {
await _boxes[boxName]?.clear();
}
@override
Future<void> close(String boxName) async => await _boxes[boxName]?.close();
@override
Future<void> deleteValue({
required String boxName,
required String key,
}) async {
Box<dynamic>? box = getBox(boxName);
await box?.delete(key);
}
@override
Future<void> save({
required String boxName,
required String key,
required value,
}) async {
Box<dynamic>? box = getBox(boxName);
await box?.put(key, value);
}
@override
Future<void> saveAll({required String boxName, required Map entries}) async {
Box<dynamic>? box = getBox(boxName);
await box?.putAll(entries);
}
@override
Future<void> saveAt({
required String boxName,
required int index,
required value,
}) async {
Box<dynamic>? box = getBox(boxName);
await box?.putAt(index, value);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/foundation.dart';
import 'package:hive_ce/hive.dart';
abstract class ILocalStorage<E> {
Future<void> init();
Future<void> openBox<T>(
String boxName, {
HiveCipher? encryptionCipher,
bool crashRecovery = true,
String? path,
Uint8List? bytes,
String? collection,
});
T? read<T>({required String boxName, required String key});
Future<void> deleteValue({required String boxName, required String key});
Future<void> add({required String boxName, required E value});
Future<void> addAll({required String boxName, required Iterable<E> values});
Future<void> clear(String boxName);
Future<void> close(String boxName);
Future<void> save({
required String boxName,
required String key,
required dynamic value,
});
Future<void> saveAt({
required String boxName,
required int index,
required dynamic value,
});
Future<void> saveAll({
required String boxName,
required Map<dynamic, E> entries,
});
}

View File

@@ -0,0 +1,23 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'interfaces/i_form_data.dart';
class DioFormData implements IFormData {
final FormData _formData = FormData();
@override
void addFile(String field, Uint8List bytes, String filename) {
_formData.files.add(MapEntry(
field,
MultipartFile.fromBytes(bytes, filename: filename),
));
}
@override
void addField(String key, String value) {
_formData.fields.add(MapEntry(key, value));
}
FormData get raw => _formData;
}

View File

@@ -0,0 +1,20 @@
import 'interfaces/i_http_response.dart';
import 'package:dio/dio.dart';
class DioResponse<T> implements IHttpResponse<T> {
final Response<dynamic> _response;
DioResponse(this._response);
@override
T? get data => _response.data;
@override
int get statusCode => _response.statusCode ?? 0;
@override
Map<String, dynamic>? get headers => _response.headers.map;
@override
bool get isSuccessful => statusCode >= 200 && statusCode < 300;
}

View File

@@ -0,0 +1,6 @@
import 'package:flutter/foundation.dart';
abstract class IFormData{
void addFile(String field, Uint8List bytes, String filename);
void addField(String key, String value);
}

View File

@@ -0,0 +1,53 @@
import 'package:dio/dio.dart';
import 'i_form_data.dart';
import 'i_http_response.dart';
abstract class IHttpClient {
Future<void> init();
Future<IHttpResponse<T>> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
ProgressCallback? onReceiveProgress,
});
Future<IHttpResponse<T>> post<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
});
Future<IHttpResponse<T>> put<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
});
Future<IHttpResponse<T>> delete<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
});
Future<IHttpResponse<T>> download<T>(
String url, {
ProgressCallback? onReceiveProgress,
});
Future<IHttpResponse<T>> upload<T>(
String path, {
required IFormData formData,
Map<String, String>? headers,
ProgressCallback? onSendProgress,
});
}

View File

@@ -0,0 +1,6 @@
abstract class IHttpResponse<T> {
T? get data;
int get statusCode;
Map<String, dynamic>? get headers;
bool get isSuccessful;
}

View File

@@ -0,0 +1,4 @@
abstract class IRemote<T>{
Future<T> init();
}

View File

@@ -0,0 +1,18 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:logger/logger.dart';
import 'package:rasadyar_core/core.dart';
void main() {
setUp(() async {
await setupAllCoreProvider();
});
group('di', () {
test('is ready', () {
expect(diCore.isRegistered<Logger>(), isTrue);
expect(diCore.isRegistered<HiveLocalStorage>(), isTrue);
expect(diCore.get<Logger>(), isA<Logger>());
expect(diCore.get<HiveLocalStorage>(), isA<HiveLocalStorage>());
});
});
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rasadyar_core/presentation/common/app_color.dart';
void main() {
group('AppColor', () {
test('blue colors', () {
expect(AppColor.blueLight, const Color(0xFFeaefff));
expect(AppColor.blueLightHover, const Color(0xFFe0e7ff));
expect(AppColor.blueLightActive, const Color(0xFFbecdff));
expect(AppColor.blueNormal, const Color(0xFF2d5fff));
expect(AppColor.blueNormalHover, const Color(0xFF2956e6));
expect(AppColor.blueNormalActive, const Color(0xFF244ccc));
expect(AppColor.blueDark, const Color(0xFF2247bf));
expect(AppColor.blueDarkHover, const Color(0xFF1b3999));
expect(AppColor.blueDarkActive, const Color(0xFF142b73));
expect(AppColor.blueDarker, const Color(0xFF102159));
});
test('green colors', () {
expect(AppColor.greenLight, const Color(0xFFe6faf5));
expect(AppColor.greenLightHover, const Color(0xFFd9f7f0));
expect(AppColor.greenLightActive, const Color(0xFFb0efdf));
expect(AppColor.greenNormal, const Color(0xFF00cc99));
expect(AppColor.greenNormalHover, const Color(0xFF00b88a));
expect(AppColor.greenNormalActive, const Color(0xFF00a37a));
expect(AppColor.greenDark, const Color(0xFF009973));
expect(AppColor.greenDarkHover, const Color(0xFF007a5c));
expect(AppColor.greenDarkActive, const Color(0xFF005c45));
expect(AppColor.greenDarker, const Color(0xFF004736));
});
test('category colors', () {
expect(AppColor.confirm, AppColor.greenNormalActive);
expect(AppColor.warning, AppColor.yellowNormal);
expect(AppColor.error, AppColor.redNormal);
expect(AppColor.info, AppColor.tealNormal);
});
});
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rasadyar_core/presentation/utils/color_utils.dart';
void main() {
group('ColorUtils extension', () {
const baseColor = Color(0xFF42A5F5);
test('disabledColor returns color with alpha 38', () {
Color disabled = baseColor.disabledColor;
expect(disabled.a, 0.14901960784313725);
expect(disabled.r, baseColor.r);
expect(disabled.g, baseColor.g);
expect(disabled.b, baseColor.b);
});
test('hoverColor returns color darkened by 0.5', () {
Color hover = baseColor.hoverColor;
final expected =
HSLColor.fromColor(
baseColor,
).withLightness((HSLColor.fromColor(baseColor).lightness - 0.5).clamp(0.0, 1.0)).toColor();
expect(hover.g, expected.g);
});
test('pressedColor returns color darkened by 0.1', () {
Color pressed = baseColor.pressedColor;
final expected =
HSLColor.fromColor(
baseColor,
).withLightness((HSLColor.fromColor(baseColor).lightness - 0.1).clamp(0.0, 1.0)).toColor();
expect(pressed.r, expected.r);
});
});
}

View File

@@ -0,0 +1,53 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:rasadyar_core/presentation/utils/list_extensions.dart';
void main(){
group('toggle test', (){
List<int> list = [1, 2, 3];
test('should remove item if it exists', () {
list.toggle(2);
expect(list, [1, 3]);
});
test('should add item if it not exists', () {
list.toggle(10);
expect(list, [1, 3 , 10]);
});
});
group('insert to first item' ,(){
List<int> list = [1, 2, 3];
test('should insert item at start if it does not exist', () {
list.ensureContainsAtStart(0);
expect(list, [0, 1, 2, 3]);
});
test('should not insert item at start if it already exists', () {
list.ensureContainsAtStart(2);
expect(list, [0, 1, 2, 3]);
});
});
group('clear and add item' ,(){
List<int> list = [1, 2, 3];
test('must be clear and add item ', () {
list.resetWith(20);
expect(list, [20]);
});
});
}

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
class ActionLogic extends GetxController with GetTickerProviderStateMixin {
late Rx<SlidableController> slidController;
bool showSlideHint = true;
@@ -15,11 +15,11 @@ class ActionLogic extends GetxController with GetTickerProviderStateMixin {
'خروج از سامانه',
];
List<String> headersIcons = [
Assets.vecProfileUserSvg,
Assets.vecCalendarSearchSvg,
Assets.vecDiagramSvg,
Assets.vecLogoutSvg,
List<Widget> headersIcons = [
Assets.vec.profileUserSvg.svg(),
Assets.vec.calendarSearchSvg.svg(),
Assets.vec.diagramSvg.svg(),
Assets.vec.logoutSvg.svg(),
];
RxList<bool> supervisionHistoryList = [false, false, false, false].obs;
@@ -46,7 +46,6 @@ class ActionLogic extends GetxController with GetTickerProviderStateMixin {
void onInit() {
super.onInit();
slidController = SlidableController(this).obs;
}
@override
@@ -69,7 +68,6 @@ class ActionLogic extends GetxController with GetTickerProviderStateMixin {
showSlideHint = !showSlideHint;
}
void updateSelectedIndex(int index) {
if (index == selectedIndex.value) {
return;

View File

@@ -3,7 +3,6 @@ import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_core/presentation/widget/buttons/elevated.dart';
import 'logic.dart';
@@ -161,13 +160,13 @@ class ActionPage extends GetView<ActionLogic> {
mainAxisAlignment: MainAxisAlignment.end,
children: [
vecWidgetWithOnTap(
assets: Assets.vecPdfDownloadSvg,
child: Assets.vec.pdfDownloadSvg.svg(),
onTap: () {},
width: 64,
height: 64,
),
vecWidgetWithOnTap(
assets: Assets.vecExcelDownloadSvg,
child: Assets.vec.excelDownloadSvg.svg(),
onTap: () {},
width: 64,
height: 64,
@@ -220,7 +219,7 @@ class ActionPage extends GetView<ActionLogic> {
}
Widget headerWidget({
required String icon,
required Widget icon,
required String title,
required VoidCallback onTap,
bool isSelected = false,
@@ -240,12 +239,7 @@ class ActionPage extends GetView<ActionLogic> {
borderRadius: BorderRadius.circular(8),
),
),
child: vecWidget(
icon,
width: 40,
height: 40,
color: AppColor.blueNormal,
),
child: icon,
),
Text(
title,
@@ -279,7 +273,10 @@ class ActionPage extends GetView<ActionLogic> {
padding: EdgeInsets.all(16),
borderRadius: BorderRadius.circular(8),
autoClose: true,
child: vecWidget(Assets.vecTrashSvg, width: 24, height: 24),
child: Assets.vec.trashSvg.svg(
width: 24,
height: 24,
),
),
],
),
@@ -372,7 +369,10 @@ class ActionPage extends GetView<ActionLogic> {
bottomRight: Radius.circular(8),
),
autoClose: true,
child: vecWidget(Assets.vecEditSvg, width: 24, height: 24),
child: Assets.vec.trashSvg.svg(
width: 24,
height: 24,
),
),
CustomSlidableAction(
onPressed: (context) {},
@@ -384,7 +384,10 @@ class ActionPage extends GetView<ActionLogic> {
bottomLeft: Radius.circular(8),
),
autoClose: true,
child: vecWidget(Assets.vecTrashSvg, width: 24, height: 24),
child: Assets.vec.trashSvg.svg(
width: 24,
height: 24,
),
),
],
),
@@ -492,7 +495,7 @@ class ActionPage extends GetView<ActionLogic> {
spacing: 12,
children: [
vecWidgetWithOnTap(
assets: Assets.vecEditSvg,
child: Assets.vec.editSvg.svg(),
onTap: () {},
width: 24,
height: 24,
@@ -506,7 +509,7 @@ class ActionPage extends GetView<ActionLogic> {
),
),
vecWidgetWithOnTap(
assets: Assets.vecTrashSvg,
child: Assets.vec.trashSvg.svg(),
width: 24,
height: 24,
color: AppColor.redNormal,

View File

@@ -14,11 +14,13 @@ class AddMobileInspectorPage extends GetView<AddMobileInspectorLogic> {
backgroundColor: AppColor.bgLight,
appBar: RAppBar(
title: 'افزودن بازرس همراه',
leading: vecWidget(
Assets.vecMessageAddSvg,
color: Colors.white,
leading: Assets.vec.messageAddSvg.svg(
width: 16,
height: 16,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
additionalActions: [
RFab.smallAdd(onPressed: () => controller.countInspector.value++),

View File

@@ -13,11 +13,13 @@ class AddSupervisionPage extends GetView<AddSupervisionLogic> {
backgroundColor: AppColor.lightGreyLight,
appBar: RAppBar(
title: 'ایجاد بازرسی',
leading: vecWidget(
Assets.vecMessageAddSvg,
color: Colors.white,
leading: Assets.vec.messageAddSvg.svg(
width: 16,
height: 16,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
),
body: Column(

View File

@@ -12,11 +12,13 @@ class DisplayInformationPage extends GetView<DisplayInformationLogic> {
backgroundColor: AppColor.bgLight,
appBar: RAppBar(
title: 'نمایش اطلاعات',
leading: vecWidget(
Assets.vecMessageAddSvg,
color: AppColor.blueNormal,
leading: Assets.vec.messageAddSvg.svg(
width: 16,
height: 16,
colorFilter: ColorFilter.mode(
AppColor.blueNormal,
BlendMode.srcIn,
),
),
),

View File

@@ -74,7 +74,7 @@ class SupervisionFilterPage extends GetView<InspectorFilterLogic> {
return RFab.small(
backgroundColor: AppColor.greenNormal,
isLoading: data.value,
icon: vecWidget(Assets.vecGpsSvg),
icon: Assets.vec.gpsSvg.svg(),
onPressed: () async {
controller.isLoading.value = true;
await controller.determineCurrentPosition();
@@ -91,7 +91,7 @@ class SupervisionFilterPage extends GetView<InspectorFilterLogic> {
bottom: 30,
child: RFab.small(
backgroundColor: AppColor.blueNormal,
icon: vecWidget(Assets.vecFilterSvg, width: 24, height: 24),
icon: Assets.vec.filterSvg.svg(width: 24,height: 24),
onPressed: () => controller.filterBottomSheetController.toggle(),
),
);
@@ -107,7 +107,10 @@ Marker markerWidget({required LatLng marker, required VoidCallback onTap}) {
child: SizedBox(
width: 36,
height: 36,
child: vecWidget(Assets.vecMapMarkerSvg,width: 30,height: 30,),
child:Assets.vec.mapMarkerSvg.svg(
width: 30,
height: 30,
)
),
),
);
@@ -318,7 +321,7 @@ Widget markerDetailsWidget() {
),
Spacer(),
vecWidgetWithOnTap(
assets: Assets.vecMapSvg,
child: Assets.vec.mapSvg.svg(),
onTap: () {
Get.toNamed(InspectionRoutes.inspectionLocationDetails);
},
@@ -327,7 +330,7 @@ Widget markerDetailsWidget() {
color: AppColor.blueNormal,
),
vecWidgetWithOnTap(
assets: Assets.vecMessageAddSvg,
child: Assets.vec.messageAddSvg.svg(),
width: 24,
height: 24,
color: AppColor.greenNormal,
@@ -337,7 +340,7 @@ Widget markerDetailsWidget() {
),
vecWidgetWithOnTap(
assets: Assets.vecSecurityTimeSvg,
child: Assets.vec.securityTimeSvg.svg(),
color: AppColor.warning,
height: 24,
width: 24,
@@ -531,7 +534,7 @@ Widget selectedLocationWidget({
bottomRight: Radius.circular(8),
topRight: Radius.circular(8),
),
child: vecWidget(Assets.vecMapSvg, width: 24, height: 24),
child: Assets.vec.mapSvg.svg( width: 24, height: 24),
),
CustomSlidableAction(
onPressed: (context) {
@@ -539,7 +542,7 @@ Widget selectedLocationWidget({
},
backgroundColor: AppColor.greenNormal,
padding: EdgeInsets.all(16),
child: vecWidget(Assets.vecMessageAddSvg),
child: Assets.vec.messageAddSvg.svg(),
),
CustomSlidableAction(
onPressed: (context) {},
@@ -549,8 +552,8 @@ Widget selectedLocationWidget({
bottomLeft: Radius.circular(8),
topLeft: Radius.circular(8),
),
child: vecWidget(Assets.vecSecurityTimeSvg),
),
child: Assets.vec.securityTimeSvg.svg()),
],
),
child: GestureDetector(
@@ -598,7 +601,7 @@ Widget selectedLocationWidget({
),
],
),
vecWidget(Assets.vecScanBarcodeSvg),
Assets.vec.scanBarcodeSvg.svg(),
],
),
),

View File

@@ -18,8 +18,7 @@ class ProfilePage extends GetView<ProfileLogic> {
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
vecWidget(Assets.vecBgHeaderUserProfileSvg, fit: BoxFit.cover),
Assets.vec.bgHeaderUserProfileSvg.svg(),
Positioned(
bottom: -20,
left: 0,
@@ -105,7 +104,7 @@ class ProfilePage extends GetView<ProfileLogic> {
onPressed: () {
data.value = 0;
},
icon: Assets.vecProfileUserSvg,
icon: Assets.vec.profileUserSvg.path,
selected: data.value == 0,
),
cardActionWidget(
@@ -113,7 +112,7 @@ class ProfilePage extends GetView<ProfileLogic> {
onPressed: () {
data.value = 1;
},
icon: Assets.vecInformationSvg,
icon: Assets.vec.informationSvg.path,
selected: data.value == 1,
),
cardActionWidget(
@@ -121,7 +120,7 @@ class ProfilePage extends GetView<ProfileLogic> {
onPressed: () {
data.value = 2;
},
icon: Assets.vecReceiptDiscountSvg,
icon: Assets.vec.receiptDiscountSvg.path,
selected: data.value == 2,
),
],
@@ -129,9 +128,7 @@ class ProfilePage extends GetView<ProfileLogic> {
);
}, controller.selectedInformationType),
SizedBox(
height: 100,
)
SizedBox(height: 100),
],
),
),
@@ -159,34 +156,38 @@ class ProfilePage extends GetView<ProfileLogic> {
itemList(
title: 'نام و نام خانوادگی',
content: 'آیدا گل محمدی',
icon: Assets.vecUserSvg,
icon: Assets.vec.userSvg.path,
),
itemList(
title: 'موبایل',
content: '09302654896',
icon: Assets.vecCallSvg,
icon: Assets.vec.callSvg.path,
),
itemList(
title: 'کدملی',
content: 'نا مشخص',
icon: Assets.vecTagUserSvg,
icon: Assets.vec.tagUserSvg.path,
),
itemList(
title: 'شماره شناسنامه',
content: 'نا مشخص',
icon: Assets.vecUserSquareSvg,
icon: Assets.vec.userSquareSvg.path,
),
itemList(
title: 'تاریخ تولد',
content: '1404/10/12',
icon: Assets.vecCalendarSvg,
icon: Assets.vec.calendarSvg.path,
),
itemList(
title: 'استان',
content: 'لرستان',
icon: Assets.vecPictureFrameSvg,
icon: Assets.vec.pictureFrameSvg.path,
),
itemList(
title: 'شهر',
content: 'خرم آباد',
icon: Assets.vec.mapSvg.path,
),
itemList(title: 'شهر', content: 'خرم آباد', icon: Assets.vecMapSvg),
],
);
}
@@ -201,11 +202,10 @@ class ProfilePage extends GetView<ProfileLogic> {
if (icon != null)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: vecWidget(
icon,
child: SvgGenImage.vec(icon).svg(
width: 20,
height: 20,
color: AppColor.blueNormal,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
Text(title, style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal)),
@@ -238,11 +238,13 @@ class ProfilePage extends GetView<ProfileLogic> {
borderRadius: BorderRadius.circular(8),
),
),
child: vecWidget(
icon,
child: SvgGenImage.vec(icon).svg(
width: 40,
height: 40,
color: selected ? AppColor.blueNormalActive : AppColor.blueNormal,
colorFilter: ColorFilter.mode(
selected ? AppColor.whiteLight : AppColor.blueNormal,
BlendMode.srcIn,
),
),
),
SizedBox(height: 2),

View File

@@ -15,11 +15,13 @@ class RegistrationOfViolationPage
backgroundColor: AppColor.bgLight,
appBar: RAppBar(
title: 'ثبت تخلف',
leading: vecWidget(
Assets.vecMessageAddSvg,
color: Colors.white,
leading: Assets.vec.messageAddSvg.svg(
width: 16,
height: 16,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
additionalActions: [
RFab.smallAdd(onPressed: () => controller.countViolation.value++),

View File

@@ -116,30 +116,54 @@ class RootPage extends GetView<RootLogic> {
),
bottomNavigationBar: WaveBottomNavigation(
items: [
WaveBottomNavigationItem(title: 'خانه', icon: Assets.vecMapSvg),
WaveBottomNavigationItem(title: 'خانه', icon: Assets.vec.mapSvg.svg(
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
)),
WaveBottomNavigationItem(
title: 'عملیات',
icon: Assets.vecUserSvg,
icon: Assets.vec.userSvg.svg(
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
WaveBottomNavigationItem(
title: 'افزودن',
icon: Assets.vecAddSvg,
icon: Assets.vec.addSvg.svg(
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
WaveBottomNavigationItem(
title: 'آمار',
icon: Assets.vecDiagramSvg,
icon: Assets.vec.diagramSvg.svg(width: 32,height: 32,colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),),
),
WaveBottomNavigationItem(
title: 'تماس',
icon: Assets.vecCallSvg,
icon: Assets.vec.callSvg.svg(
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
WaveBottomNavigationItem(
title: 'مکان ',
icon: Assets.vecGpsSvg,
icon: Assets.vec.gpsSvg.svg(
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
WaveBottomNavigationItem(
title: 'تاریخ',
icon: Assets.vecCalendarSvg,
icon: Assets.vec.calendarSvg.svg(
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
],
onPageChanged: (index) {

7
packages/livestock/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View File

@@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,6 @@
/// Support for doing something awesome.
///
/// More dartdocs go here.
library;

View File

@@ -0,0 +1,8 @@
import 'package:rasadyar_core/core.dart';
class MapLogic extends GetxController {
var ss = Get.find<DraggableBottomSheetController>();
}

View File

@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_core/presentation/widget/map/view.dart';
import 'logic.dart';
class MapPage extends GetView<MapLogic> {
MapPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
MapWidget(
markerWidget: Icon(Icons.pin_drop_rounded),
initOnTap: () {
},
initMarkerWidget: Assets.vec.mapMarkerSvg.svg(
width: 30,
height: 30,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,5 @@
import 'package:rasadyar_core/core.dart';
class ProfileLogic extends GetxController {
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class ProfilePage extends GetView<ProfileLogic> {
ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}

View File

@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_livestock/presentation/page/root/logic.dart';
class RequestTaggingLogic extends GetxController {
final TextEditingController phoneController = TextEditingController();
@override
void onReady() {
super.onReady();
}
@override
void onClose() {
super.onClose();
}
}

View File

@@ -0,0 +1,109 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_livestock/presentation/routes/app_pages.dart';
import 'logic.dart';
class RequestTaggingPage extends GetView<RequestTaggingLogic> {
const RequestTaggingPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: RAppBar(
title: 'درخواست پلاک کوبی',
leadingWidth: 40,
leading: Assets.vec.messageAddSvg.svg(width: 12, height: 12),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
child: Column(
children: [
RTextField(
controller: controller.phoneController,
label: 'تلفن دامدار',
),
SizedBox(
width: Get.width,
height: 356,
child: Card(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Expanded(
child: Container(
width: Get.width,
decoration: BoxDecoration(
color: AppColor.lightGreyNormal,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Assets.images.placeHolder.image(
height: 150,
width: 200,
),
),
),
),
SizedBox(height: 15),
Container(
width: Get.width,
height: 40,
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: AppColor.blueNormal,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
' تصویر گله',
style: AppFonts.yekan14.copyWith(
color: Colors.white,
),
),
Icon(
CupertinoIcons.arrow_up_doc,
color: Colors.white,
),
],
),
),
),
],
),
),
),
),
Spacer(),
RElevated(
text: 'ارسال تصویر گله',
onPressed: () {
Get.toNamed(LiveStockRoutes.tagging);
},
height: 40,
isFullWidth: true,
backgroundColor: AppColor.greenNormal,
textStyle: AppFonts.yekan16.copyWith(color: Colors.white),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,9 @@
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_livestock/presentation/page/root/logic.dart';
class RequestsLogic extends GetxController {
RxList<int> filterSelected = <int>[].obs;
RxBool isFilterShowed = false.obs;
}

View File

@@ -0,0 +1,302 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_livestock/presentation/routes/app_pages.dart';
import 'logic.dart';
class RequestsPage extends GetView<RequestsLogic> {
RequestsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: RAppBar(title: 'لیست درخواست‌ها', hasBack: false, centerTitle: true),
body: Column(
children: [
SizedBox(height: 8),
_buildSearchAndFilter(),
_buildFilterWidget(),
_buildListOfContent(),
RElevated(
text: '+ ایجاد درخواست',
width: Get.width - 36,
height: 40,
textStyle: AppFonts.yekan18.copyWith(color: Colors.white),
onPressed: () {
//TODO
},
),
SizedBox(height: 10),
],
),
);
}
Expanded _buildListOfContent() {
return Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: 10,
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 50),
separatorBuilder: (context, index) => SizedBox(height: 6),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Get.toNamed(LiveStockRoutes.requestTagging);
},
child: Container(
width: Get.width,
height: 75,
decoration: BoxDecoration(
color:
index < 3
? AppColor.yellowNormal
: index < 7
? AppColor.greenLightActive
: AppColor.blueLight,
borderRadius: BorderRadius.circular(8),
),
child: Stack(
children: [
Row(
children: [
SizedBox(width: 5),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(8),
bottomRight: Radius.circular(8),
),
border: Border.all(
color:
index < 3
? AppColor.yellowNormal
: index < 7
? AppColor.greenLightActive
: AppColor.blueLight,
width: 2,
),
),
child: Row(
children: [
SizedBox(width: 10),
Text(
'محمد احمدی',
textAlign: TextAlign.center,
style: AppFonts.yekan12.copyWith(
color: AppColor.blueNormal,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 20),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'پنج شنبه 14 اردیبهشت',
textAlign: TextAlign.center,
style: AppFonts.yekan10.copyWith(color: AppColor.darkGreyNormal),
),
Text(
' همدان - نهاوند - روستای - همدان - نهاوند - روستای ',
textAlign: TextAlign.center,
style: AppFonts.yekan10.copyWith(color: AppColor.darkGreyNormal),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
],
),
),
SizedBox(width: 20),
GestureDetector(
onTap: () {
// controller.onTapMap();
},
child: SizedBox(
width: 50,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Assets.vec.mapSvg.svg(
width: 20,
height: 20,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
SizedBox(height: 8),
Text(
'مسیریابی',
textAlign: TextAlign.center,
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
],
),
),
),
SizedBox(width: 20),
],
),
),
),
Container(
width: 20,
child: Center(
child: RotatedBox(
quarterTurns: 3,
child: Text(
index < 3
? ' بازرسی'
: index < 7
? 'اطلاعات'
: 'ارجاع به تعاونی',
style: AppFonts.yekan8,
textAlign: TextAlign.center,
),
),
),
),
],
),
Positioned(
top: 5,
right: 10,
child:Container(
padding: EdgeInsets.all(4),
alignment: AlignmentDirectional.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black54,),
child: Text((index*20).toString(),style: AppFonts.yekan12.copyWith(color: Colors.white),),
),
),
],
),
),
);
},
),
);
}
Row _buildSearchAndFilter() {
return Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: IconButton(
onPressed: () {
controller.isFilterShowed.value = !controller.isFilterShowed.value;
},
style: IconButton.styleFrom(
backgroundColor: AppColor.blueNormal,
fixedSize: Size(40, 40),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
icon: Assets.vec.filterSvg.svg(),
),
),
Expanded(child: _searchWidget()),
],
);
}
ObxValue<RxBool> _buildFilterWidget() {
return ObxValue((data) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
padding: EdgeInsets.only(top: 5),
curve: Curves.easeInOut,
height: data.value ? 45 : 0,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(horizontal: 12),
child: ObxValue((data) {
return Row(
spacing: 12,
children: [
CustomChip(
title: 'انتخاب فیلتر',
index: 0,
isSelected: true,
selectedColor: AppColor.blueNormal,
onTap: (index) {},
),
RFilterChips(
title: 'درخواست‌های من',
index: 1,
isSelected: data.contains(1),
selectedColor: AppColor.yellowNormal,
onTap: (index) {
if (data.contains(1)) {
data.remove(1);
} else {
data.add(1);
}
},
),
RFilterChips(
title: 'در انتظار ثبت ',
index: 2,
selectedColor: AppColor.greenLightActive,
isSelected: data.contains(2),
onTap: (index) {
if (data.contains(2)) {
data.remove(2);
} else {
data.add(2);
}
},
),
RFilterChips(
title: 'ارجاع به تعاونی',
index: 3,
selectedColor: AppColor.blueLightHover,
isSelected: data.contains(3),
onTap: (index) {
if (data.contains(3)) {
data.remove(3);
} else {
data.add(3);
}
},
),
],
);
}, controller.filterSelected),
),
);
}, controller.isFilterShowed);
}
Widget _searchWidget() {
return Padding(
padding: const EdgeInsets.only(right: 8.0, left: 20.0),
child: RTextField(
suffixIcon: Padding(
padding: const EdgeInsets.all(12.0),
child: Assets.vec.searchSvg.svg(
width: 10,
height: 10,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
hintText: 'جستجو',
onChanged: (value) {
//controller.search(value);
},
controller: TextEditingController(),
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More