feat : first step request tagging
This commit is contained in:
@@ -44,6 +44,12 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="com.yalantis.ucrop.UCropActivity"
|
||||||
|
android:screenOrientation="fullSensor"
|
||||||
|
android:theme="@style/Ucrop.CropTheme"/>
|
||||||
|
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
7
android/app/src/main/res/values-v35/styles.xml
Normal file
7
android/app/src/main/res/values-v35/styles.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Ucrop.CropTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
|
|
||||||
@@ -16,7 +16,6 @@ class CustomNavigationObserver extends NavigatorObserver {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void didPush(Route route, Route? previousRoute) async {
|
void didPush(Route route, Route? previousRoute) async {
|
||||||
super.didPush(route, previousRoute);
|
|
||||||
final routeName = route.settings.name;
|
final routeName = route.settings.name;
|
||||||
if (!_isWorkDone && (routeName == ChickenRoutes.init || routeName == ChickenRoutes.auth)) {
|
if (!_isWorkDone && (routeName == ChickenRoutes.init || routeName == ChickenRoutes.auth)) {
|
||||||
_isWorkDone = true;
|
_isWorkDone = true;
|
||||||
@@ -31,6 +30,7 @@ class CustomNavigationObserver extends NavigatorObserver {
|
|||||||
_isWorkDone = true;
|
_isWorkDone = true;
|
||||||
await setupLiveStockDI();
|
await setupLiveStockDI();
|
||||||
}
|
}
|
||||||
|
super.didPush(route, previousRoute);
|
||||||
tLog('CustomNavigationObserver: didPush - $routeName');
|
tLog('CustomNavigationObserver: didPush - $routeName');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
import 'package:rasadyar_inspection/inspection.dart';
|
||||||
|
import 'package:rasadyar_livestock/injection/live_stock_di.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/routes/app_pages.dart';
|
||||||
|
|
||||||
class ModulesLogic extends GetxController {
|
class ModulesLogic extends GetxController {
|
||||||
TokenStorageService tokenService = Get.find<TokenStorageService>();
|
TokenStorageService tokenService = Get.find<TokenStorageService>();
|
||||||
|
RxBool isLoading = false.obs;
|
||||||
|
|
||||||
List<ModuleModel> moduleList = [
|
List<ModuleModel> moduleList = [
|
||||||
ModuleModel(title: 'بازرسی', icon: Assets.icons.inspection.path, module: Module.inspection),
|
ModuleModel(title: 'بازرسی', icon: Assets.icons.inspection.path, module: Module.inspection),
|
||||||
@@ -25,4 +30,23 @@ class ModulesLogic extends GetxController {
|
|||||||
tokenService.saveModule(module);
|
tokenService.saveModule(module);
|
||||||
tokenService.appModule.value = module;
|
tokenService.appModule.value = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> navigateToModule(Module module) async {
|
||||||
|
if (module == Module.inspection) {
|
||||||
|
Get.offAllNamed(InspectionRoutes.init);
|
||||||
|
} else if (module == Module.liveStocks) {
|
||||||
|
await setupLiveStockDI();
|
||||||
|
Get.offAllNamed(LiveStockRoutes.init);
|
||||||
|
} else if (module == Module.chicken) {
|
||||||
|
Get.offAllNamed(ChickenRoutes.init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTapCard(Module module, int index) async {
|
||||||
|
isLoading.value = true;
|
||||||
|
selectedIndex.value = index;
|
||||||
|
saveModule(module);
|
||||||
|
await Future.delayed(Duration(milliseconds: 800)); // Simulate loading delay
|
||||||
|
navigateToModule(module);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rasadyar_chicken/chicken.dart';
|
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
import 'package:rasadyar_inspection/inspection.dart';
|
|
||||||
|
|
||||||
import 'logic.dart';
|
import 'logic.dart';
|
||||||
|
|
||||||
@@ -16,39 +15,38 @@ class ModulesPage extends GetView<ModulesLogic> {
|
|||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
backgroundColor: AppColor.blueNormal,
|
backgroundColor: AppColor.blueNormal,
|
||||||
),
|
),
|
||||||
body: GridView.builder(
|
body: Stack(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
|
fit: StackFit.expand,
|
||||||
|
alignment: Alignment.center,
|
||||||
itemBuilder: (context, index) {
|
children: [
|
||||||
final module = controller.moduleList[index];
|
GridView.builder(
|
||||||
return CardIcon(
|
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
|
||||||
title: module.title,
|
itemBuilder: (context, index) {
|
||||||
icon: module.icon,
|
final module = controller.moduleList[index];
|
||||||
onTap: () {
|
return CardIcon(
|
||||||
controller.selectedIndex.value = index;
|
title: module.title,
|
||||||
controller.saveModule(module.module);
|
icon: module.icon,
|
||||||
|
onTap: () => controller.onTapCard(module.module, index),
|
||||||
// Navigate to the appropriate route based on the selected module
|
);
|
||||||
switch (module.module) {
|
|
||||||
case Module.inspection:
|
|
||||||
Get.toNamed(InspectionRoutes.init);
|
|
||||||
break;
|
|
||||||
case Module.liveStocks:
|
|
||||||
//TODO: Implement liveStocks module navigation
|
|
||||||
case Module.chicken:
|
|
||||||
Get.toNamed(ChickenRoutes.init);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
},
|
crossAxisCount: 3,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
mainAxisSpacing: 10,
|
||||||
crossAxisCount: 3,
|
crossAxisSpacing: 10,
|
||||||
mainAxisSpacing: 10,
|
),
|
||||||
crossAxisSpacing: 10,
|
physics: BouncingScrollPhysics(),
|
||||||
),
|
itemCount: controller.moduleList.length,
|
||||||
physics: BouncingScrollPhysics(),
|
),
|
||||||
itemCount: controller.moduleList.length,
|
ObxValue((loading) {
|
||||||
|
if (!controller.isLoading.value) return SizedBox.shrink();
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey.withValues(alpha: 0.5),
|
||||||
|
child: Center(
|
||||||
|
child: CupertinoActivityIndicator(color: AppColor.greenNormal, radius: 30),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}, controller.isLoading),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export 'package:dio/dio.dart';
|
|||||||
export 'package:flutter_localizations/flutter_localizations.dart';
|
export 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
export 'package:flutter_map/flutter_map.dart';
|
export 'package:flutter_map/flutter_map.dart';
|
||||||
export 'package:flutter_map_animations/flutter_map_animations.dart';
|
export 'package:flutter_map_animations/flutter_map_animations.dart';
|
||||||
|
export 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
|
||||||
export 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
export 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
export 'package:flutter_screenutil/flutter_screenutil.dart';
|
export 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
export 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
export 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
@@ -21,6 +22,7 @@ export 'package:get/get.dart' hide FormData, MultipartFile, Response;
|
|||||||
export 'package:get_it/get_it.dart';
|
export 'package:get_it/get_it.dart';
|
||||||
//local storage
|
//local storage
|
||||||
export 'package:hive_ce_flutter/hive_flutter.dart';
|
export 'package:hive_ce_flutter/hive_flutter.dart';
|
||||||
|
export 'package:image_cropper/image_cropper.dart';
|
||||||
///image picker
|
///image picker
|
||||||
export 'package:image_picker/image_picker.dart';
|
export 'package:image_picker/image_picker.dart';
|
||||||
//encryption
|
//encryption
|
||||||
@@ -36,7 +38,6 @@ export 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
|||||||
export 'package:rasadyar_core/presentation/common/common.dart';
|
export 'package:rasadyar_core/presentation/common/common.dart';
|
||||||
export 'package:rasadyar_core/presentation/utils/utils.dart';
|
export 'package:rasadyar_core/presentation/utils/utils.dart';
|
||||||
export 'package:rasadyar_core/presentation/widget/widget.dart';
|
export 'package:rasadyar_core/presentation/widget/widget.dart';
|
||||||
export 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
|
|
||||||
|
|
||||||
//models
|
//models
|
||||||
export 'data/model/model.dart';
|
export 'data/model/model.dart';
|
||||||
@@ -57,5 +58,4 @@ export 'utils/map_utils.dart';
|
|||||||
export 'utils/network/network.dart';
|
export 'utils/network/network.dart';
|
||||||
export 'utils/route_utils.dart';
|
export 'utils/route_utils.dart';
|
||||||
export 'utils/separator_input_formatter.dart';
|
export 'utils/separator_input_formatter.dart';
|
||||||
|
|
||||||
export 'utils/utils.dart';
|
export 'utils/utils.dart';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class AppInterceptor extends Interceptor {
|
|||||||
final RefreshTokenCallback? refreshTokenCallback;
|
final RefreshTokenCallback? refreshTokenCallback;
|
||||||
final SaveTokenCallback saveTokenCallback;
|
final SaveTokenCallback saveTokenCallback;
|
||||||
final ClearTokenCallback clearTokenCallback;
|
final ClearTokenCallback clearTokenCallback;
|
||||||
late final Dio dio;
|
late Dio dio;
|
||||||
dynamic authArguments;
|
dynamic authArguments;
|
||||||
static Completer<String?>? _refreshCompleter;
|
static Completer<String?>? _refreshCompleter;
|
||||||
static bool _isRefreshing = false;
|
static bool _isRefreshing = false;
|
||||||
@@ -44,7 +44,7 @@ class AppInterceptor extends Interceptor {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
|
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||||
if (err.response?.statusCode == 401) {
|
if (err.response?.statusCode == 401 && err.response?.data['detail'] != "No active account found with the given credentials") {
|
||||||
final retryResult = await _handleUnauthorizedError(err);
|
final retryResult = await _handleUnauthorizedError(err);
|
||||||
if (retryResult != null) {
|
if (retryResult != null) {
|
||||||
handler.resolve(retryResult);
|
handler.resolve(retryResult);
|
||||||
@@ -104,6 +104,7 @@ class AppInterceptor extends Interceptor {
|
|||||||
return dio.fetch(newOptions);
|
return dio.fetch(newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO
|
||||||
void _handleRefreshFailure() {
|
void _handleRefreshFailure() {
|
||||||
ApiHandler.cancelAllRequests("Token refresh failed");
|
ApiHandler.cancelAllRequests("Token refresh failed");
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class DioRemote implements IHttpClient {
|
|||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
dio = Dio(BaseOptions(baseUrl: baseUrl ?? ''));
|
dio = Dio(BaseOptions(baseUrl: baseUrl ?? ''));
|
||||||
if (interceptors != null) {
|
if (interceptors != null) {
|
||||||
|
interceptors!.dio = dio;
|
||||||
dio.interceptors.add(interceptors!);
|
dio.interceptors.add(interceptors!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -725,6 +725,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.4"
|
version: "4.5.4"
|
||||||
|
image_cropper:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: image_cropper
|
||||||
|
sha256: "4e9c96c029eb5a23798da1b6af39787f964da6ffc78fd8447c140542a9f7c6fc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.0"
|
||||||
|
image_cropper_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_cropper_for_web
|
||||||
|
sha256: fd81ebe36f636576094377aab32673c4e5d1609b32dec16fad98d2b71f1250a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
|
image_cropper_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_cropper_platform_interface
|
||||||
|
sha256: "6ca6b81769abff9a4dcc3bbd3d75f5dfa9de6b870ae9613c8cd237333a4283af"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.1.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ dependencies:
|
|||||||
|
|
||||||
##image_picker
|
##image_picker
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
|
image_cropper: ^9.1.0
|
||||||
|
|
||||||
#UI
|
#UI
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
|
|||||||
@@ -32,22 +32,16 @@ class DioErrorHandler {
|
|||||||
_errorSnackBar(
|
_errorSnackBar(
|
||||||
error.response?.data.keys.first == 'is_user'
|
error.response?.data.keys.first == 'is_user'
|
||||||
? 'کاربر با این شماره تلفن وجود ندارد'
|
? 'کاربر با این شماره تلفن وجود ندارد'
|
||||||
: error.response?.data[error.response?.data.keys.first] ??
|
: '${error.response?.statusCode} - ${error.response?.data[error.response?.data.keys.first]}' ??
|
||||||
'خطا در برقراری ارتباط با سرور',
|
'خطا در برقراری ارتباط با سرور',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetSnackBar _errorSnackBar(String message) {
|
GetSnackBar _errorSnackBar(String message) {
|
||||||
return GetSnackBar(
|
return GetSnackBar(
|
||||||
titleText: Text(
|
titleText: Text('خطا', style: AppFonts.yekan14.copyWith(color: Colors.white)),
|
||||||
'خطا',
|
messageText: Text(message, style: AppFonts.yekan12.copyWith(color: Colors.white)),
|
||||||
style: AppFonts.yekan14.copyWith(color: Colors.white),
|
|
||||||
),
|
|
||||||
messageText: Text(
|
|
||||||
message,
|
|
||||||
style: AppFonts.yekan12.copyWith(color: Colors.white),
|
|
||||||
),
|
|
||||||
backgroundColor: AppColor.error,
|
backgroundColor: AppColor.error,
|
||||||
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ class AuthRemoteDataSourceImp extends AuthRemoteDataSource {
|
|||||||
'${_BASE_URL}login/',
|
'${_BASE_URL}login/',
|
||||||
data: authRequest,
|
data: authRequest,
|
||||||
fromJson: AuthResponseModel.fromJson,
|
fromJson: AuthResponseModel.fromJson,
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
);
|
);
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,27 +11,17 @@ GetIt get diLiveStock => GetIt.instance;
|
|||||||
Future<void> setupLiveStockDI() async {
|
Future<void> setupLiveStockDI() async {
|
||||||
diLiveStock.registerSingleton(DioErrorHandler());
|
diLiveStock.registerSingleton(DioErrorHandler());
|
||||||
|
|
||||||
|
|
||||||
final tokenService = Get.find<TokenStorageService>();
|
final tokenService = Get.find<TokenStorageService>();
|
||||||
|
|
||||||
|
|
||||||
if (tokenService.baseurl.value == null) {
|
if (tokenService.baseurl.value == null) {
|
||||||
await tokenService.saveBaseUrl('https://api.dam.rasadyar.net/');
|
await tokenService.saveBaseUrl('https://api.dam.rasadyar.net/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First register AppInterceptor with lazy callbacks
|
||||||
diLiveStock.registerLazySingleton<AuthRemoteDataSource>(
|
|
||||||
() => AuthRemoteDataSourceImp(diLiveStock.get<DioRemote>()),
|
|
||||||
);
|
|
||||||
|
|
||||||
diLiveStock.registerLazySingleton<AuthRepository>(
|
|
||||||
() => AuthRepositoryImp(diLiveStock.get<AuthRemoteDataSource>()),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
diLiveStock.registerLazySingleton<AppInterceptor>(
|
diLiveStock.registerLazySingleton<AppInterceptor>(
|
||||||
() => AppInterceptor(
|
() => AppInterceptor(
|
||||||
refreshTokenCallback: () async {
|
refreshTokenCallback: () async {
|
||||||
|
// Use lazy access to avoid circular dependency
|
||||||
final authRepository = diLiveStock.get<AuthRepository>();
|
final authRepository = diLiveStock.get<AuthRepository>();
|
||||||
final hasAuthenticated = await authRepository.hasAuthenticated();
|
final hasAuthenticated = await authRepository.hasAuthenticated();
|
||||||
if (hasAuthenticated) {
|
if (hasAuthenticated) {
|
||||||
@@ -53,13 +43,26 @@ Future<void> setupLiveStockDI() async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Register DioRemote with the interceptor
|
||||||
diLiveStock.registerLazySingleton<DioRemote>(
|
diLiveStock.registerLazySingleton<DioRemote>(
|
||||||
() => DioRemote(
|
() => DioRemote(
|
||||||
baseUrl: tokenService.baseurl.value,
|
baseUrl: tokenService.baseurl.value,
|
||||||
interceptors: diLiveStock.get<AppInterceptor>(),
|
// interceptors: diLiveStock.get<AppInterceptor>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Initialize DioRemote
|
||||||
await diLiveStock.get<DioRemote>().init();
|
await diLiveStock.get<DioRemote>().init();
|
||||||
|
|
||||||
|
// Now register the data source and repository
|
||||||
|
diLiveStock.registerLazySingleton<AuthRemoteDataSource>(
|
||||||
|
() => AuthRemoteDataSourceImp(diLiveStock.get<DioRemote>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
diLiveStock.registerLazySingleton<AuthRepository>(
|
||||||
|
() => AuthRepositoryImp(diLiveStock.get<AuthRemoteDataSource>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
diLiveStock.registerLazySingleton<ImagePicker>(() => ImagePicker());
|
||||||
|
await diLiveStock.allReady();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class AuthLogic extends GetxController with GetTickerProviderStateMixin {
|
|||||||
final loginRequestModel = _buildLoginRequest();
|
final loginRequestModel = _buildLoginRequest();
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await safeCall<AuthResponseModel?>(
|
await safeCall<AuthResponseModel?>(
|
||||||
call: () async => authRepository.login(authRequest: loginRequestModel.toJson()),
|
call: () async => await authRepository.login(authRequest: loginRequestModel.toJson()),
|
||||||
onSuccess: (result) async {
|
onSuccess: (result) async {
|
||||||
await tokenStorageService.saveModule(_module);
|
await tokenStorageService.saveModule(_module);
|
||||||
await tokenStorageService.saveRefreshToken(result?.refresh ?? '');
|
await tokenStorageService.saveRefreshToken(result?.refresh ?? '');
|
||||||
|
|||||||
@@ -17,27 +17,29 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
children: [
|
children: [
|
||||||
Assets.vec.bgAuthSvg.svg(fit: BoxFit.fill),
|
Assets.vec.bgAuthSvg.svg(fit: BoxFit.fill),
|
||||||
|
|
||||||
Padding(
|
Center(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.r),
|
child: Padding(
|
||||||
child: FadeTransition(
|
padding: EdgeInsets.symmetric(horizontal: 10.r),
|
||||||
opacity: controller.textAnimation,
|
child: FadeTransition(
|
||||||
child: Column(
|
opacity: controller.textAnimation,
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
spacing: 12,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
spacing: 12,
|
||||||
Text(
|
children: [
|
||||||
'به سامانه رصدیار خوش آمدید!',
|
Text(
|
||||||
textAlign: TextAlign.right,
|
'به سامانه رصدیار خوش آمدید!',
|
||||||
style: AppFonts.yekan25Bold.copyWith(color: Colors.white),
|
textAlign: TextAlign.right,
|
||||||
),
|
style: AppFonts.yekan25Bold.copyWith(color: Colors.white),
|
||||||
Text(
|
),
|
||||||
'سامانه رصد و پایش زنجیره تامین، تولید و توزیع کالا های اساسی',
|
Text(
|
||||||
textAlign: TextAlign.center,
|
'سامانه رصد و پایش زنجیره تامین، تولید و توزیع کالا های اساسی',
|
||||||
style: AppFonts.yekan16.copyWith(color: Colors.white),
|
textAlign: TextAlign.center,
|
||||||
),
|
style: AppFonts.yekan16.copyWith(color: Colors.white),
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/widgets/base_page/logic.dart';
|
||||||
|
|
||||||
class MapLogic extends GetxController {
|
class MapLogic extends GetxController {
|
||||||
|
|
||||||
var ss = Get.find<DraggableBottomSheetController>();
|
var ss = Get.find<DraggableBottomSheetController>();
|
||||||
|
BaseLogic baseLogic = Get.find<BaseLogic>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
import 'package:rasadyar_livestock/presentation/page/map/widget/map_widget/view.dart';
|
import 'package:rasadyar_livestock/presentation/page/map/widget/map_widget/view.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/widgets/base_page/view.dart';
|
||||||
|
|
||||||
import 'logic.dart';
|
import 'logic.dart';
|
||||||
|
|
||||||
@@ -9,6 +10,158 @@ class MapPage extends GetView<MapLogic> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(body: Stack(children: [MapWidget()]));
|
return BasePage(
|
||||||
|
hasSearch: true,
|
||||||
|
hasFilter: true,
|
||||||
|
hasBack: false,
|
||||||
|
defaultSearch: false,
|
||||||
|
filteringWidget: filterWidget(showIndex: 3.obs, filterIndex: 5.obs),
|
||||||
|
widgets: [MapWidget()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget filterWidget({required RxInt filterIndex, required RxInt showIndex}) {
|
||||||
|
return BaseBottomSheet(
|
||||||
|
height: Get.height * 0.5,
|
||||||
|
child: Container(color: Colors.red),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseBottomSheet searchWidget() {
|
||||||
|
return BaseBottomSheet(
|
||||||
|
height: Get.height * 0.85,
|
||||||
|
rootChild: Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
spacing: 12,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: RTextField(
|
||||||
|
height: 40,
|
||||||
|
borderColor: AppColor.blackLight,
|
||||||
|
suffixIcon: ObxValue(
|
||||||
|
(data) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: (data.value == null)
|
||||||
|
? Assets.vec.searchSvg.svg(
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
controller.baseLogic.searchTextController.clear();
|
||||||
|
controller.baseLogic.searchValue.value = null;
|
||||||
|
controller.baseLogic.isSearchSelected.value = false;
|
||||||
|
//controller.mapLogic.hasFilterOrSearch.value = false;
|
||||||
|
//controller.searchedPoultryLocation.value = Resource.initial();
|
||||||
|
},
|
||||||
|
enableFeedback: true,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
iconSize: 24,
|
||||||
|
splashRadius: 50,
|
||||||
|
icon: Assets.vec.closeCircleSvg.svg(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
controller.baseLogic.searchValue,
|
||||||
|
),
|
||||||
|
hintText: 'جستجو کنید ...',
|
||||||
|
hintStyle: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
|
||||||
|
filledColor: Colors.white,
|
||||||
|
filled: true,
|
||||||
|
controller: controller.baseLogic.searchTextController,
|
||||||
|
onChanged: (val) => controller.baseLogic.searchValue.value = val,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
child: Assets.vec.mapSvg.svg(
|
||||||
|
width: 24.w,
|
||||||
|
height: 24.h,
|
||||||
|
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
/* Expanded(
|
||||||
|
child: ObxValue((rxData) {
|
||||||
|
final resource = rxData.value;
|
||||||
|
final status = resource.status;
|
||||||
|
final items = resource.data;
|
||||||
|
final message = resource.message ?? 'خطا در بارگذاری';
|
||||||
|
|
||||||
|
if (status == ResourceStatus.initial) {
|
||||||
|
return Center(child: Text('ابتدا جستجو کنید'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ResourceStatus.loading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ResourceStatus.error) {
|
||||||
|
return Center(child: Text(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items == null || items.isEmpty) {
|
||||||
|
return Center(child: EmptyWidget());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.separated(
|
||||||
|
itemCount: items.length,
|
||||||
|
separatorBuilder: (context, index) => SizedBox(height: 8),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = items[index]; // اگر item استفاده نمیشه، میتونه حذف بشه
|
||||||
|
return ListItem2(
|
||||||
|
index: index,
|
||||||
|
labelColor: AppColor.blueLight,
|
||||||
|
labelIcon: Assets.vec.cowSvg.path,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.unitName ?? 'N/A',
|
||||||
|
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item.user?.fullname ?? '',
|
||||||
|
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDarkHover),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'جوجه ریزی فعال',
|
||||||
|
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
(item.hatching != null && item.hatching!.isNotEmpty)
|
||||||
|
? 'دارد'
|
||||||
|
: 'ندراد',
|
||||||
|
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDarkHover),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}, controller.searchedPoultryLocation),
|
||||||
|
),*/
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/routes/app_pages.dart';
|
||||||
|
|
||||||
import 'logic.dart';
|
import 'logic.dart';
|
||||||
|
|
||||||
@@ -172,8 +173,40 @@ class MapWidget extends GetView<MapWidgetLogic> {
|
|||||||
.map(
|
.map(
|
||||||
(element) => Marker(
|
(element) => Marker(
|
||||||
point: element,
|
point: element,
|
||||||
child: FaIcon(FontAwesomeIcons.locationPin, color: AppColor.error),
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Get.bottomSheet(
|
||||||
|
detailsBottomSheet(),
|
||||||
|
isScrollControlled: true,
|
||||||
|
isDismissible: true,
|
||||||
|
ignoreSafeArea: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: FaIcon(FontAwesomeIcons.locationPin, color: AppColor.error),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
Widget detailsBottomSheet() {
|
||||||
|
return BaseBottomSheet(
|
||||||
|
height: 250.h,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: 20,
|
||||||
|
children: [
|
||||||
|
Text('مشخصات محل', style: AppFonts.yekan16Bold),
|
||||||
|
// Add more details here
|
||||||
|
RElevated(
|
||||||
|
text: 'ایجاد بازرسی',
|
||||||
|
width: Get.width,
|
||||||
|
height: 40.h,
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed(LiveStockRoutes.requestTagging);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,32 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
import 'package:rasadyar_livestock/presentation/page/root/logic.dart';
|
import 'package:rasadyar_livestock/injection/live_stock_di.dart';
|
||||||
|
|
||||||
class RequestTaggingLogic extends GetxController {
|
class RequestTaggingLogic extends GetxController {
|
||||||
|
RxInt currentIndex = 0.obs;
|
||||||
|
final int maxStep = 3;
|
||||||
|
|
||||||
|
RxBool nextButtonEnabled = true.obs;
|
||||||
|
|
||||||
|
final TextEditingController phoneController = TextEditingController();
|
||||||
|
final TextEditingController fullNameController = TextEditingController();
|
||||||
|
final TextEditingController addressController = TextEditingController();
|
||||||
|
|
||||||
|
ImagePicker imagePicker = diLiveStock.get<ImagePicker>();
|
||||||
|
Rxn<XFile> rancherImage = Rxn<XFile>(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
setUpTextControllerListeners();
|
||||||
|
setUpNextButtonListeners();
|
||||||
|
ever(rancherImage, (callback) {
|
||||||
|
setUpNextButtonListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final TextEditingController phoneController = TextEditingController();
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
super.onReady();
|
super.onReady();
|
||||||
@@ -14,4 +36,68 @@ final TextEditingController phoneController = TextEditingController();
|
|||||||
void onClose() {
|
void onClose() {
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onNext() {
|
||||||
|
if (currentIndex.value < maxStep) {
|
||||||
|
if (currentIndex.value == 0) {}
|
||||||
|
currentIndex.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onPrevious() {
|
||||||
|
if (currentIndex.value > 0) {
|
||||||
|
currentIndex.value--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUpNextButtonListeners() {
|
||||||
|
if (currentIndex.value == 0) {
|
||||||
|
nextButtonEnabled.value =
|
||||||
|
phoneController.text.isNotEmpty &&
|
||||||
|
fullNameController.text.isNotEmpty &&
|
||||||
|
addressController.text.isNotEmpty &&
|
||||||
|
rancherImage.value != null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUpTextControllerListeners() {
|
||||||
|
phoneController.addListener(setUpNextButtonListeners);
|
||||||
|
fullNameController.addListener(setUpNextButtonListeners);
|
||||||
|
addressController.addListener(setUpNextButtonListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pickImage() async {
|
||||||
|
rancherImage.value = await imagePicker.pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
imageQuality: 60,
|
||||||
|
maxWidth: 1080,
|
||||||
|
maxHeight: 720,
|
||||||
|
);
|
||||||
|
|
||||||
|
getFileSizeInKB(rancherImage.value?.path ?? '', tag: 'Picked');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> cropImage() async {
|
||||||
|
if (rancherImage.value == null) return;
|
||||||
|
|
||||||
|
final CroppedFile? cropped = await ImageCropper().cropImage(
|
||||||
|
sourcePath: rancherImage.value!.path,
|
||||||
|
maxWidth: 1080,
|
||||||
|
maxHeight: 720,
|
||||||
|
compressQuality: 60,
|
||||||
|
);
|
||||||
|
if (cropped == null) return;
|
||||||
|
rancherImage.value = XFile(cropped.path);
|
||||||
|
|
||||||
|
getFileSizeInKB(rancherImage.value?.path ?? '', tag: 'Cropped');
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFileSizeInKB(String filePath, {String? tag}) {
|
||||||
|
final file = File(filePath);
|
||||||
|
final bytes = file.lengthSync();
|
||||||
|
var size = (bytes / 1024).ceil();
|
||||||
|
iLog('${tag ?? 'Picked'} image Size: $size');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
@@ -19,91 +21,272 @@ class RequestTaggingPage extends GetView<RequestTaggingLogic> {
|
|||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||||
child: Column(
|
child: ObxValue((index) {
|
||||||
children: [
|
return Column(
|
||||||
RTextField(
|
children: [
|
||||||
controller: controller.phoneController,
|
Expanded(child: _buildStep(index.value)),
|
||||||
label: 'تلفن دامدار',
|
nextOrPreviousWidget(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}, controller.currentIndex),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Row nextOrPreviousWidget() {
|
||||||
|
return Row(
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
ObxValue(
|
||||||
|
(data) => Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: RElevated(
|
||||||
|
height: 40.h,
|
||||||
|
enabled: data.value,
|
||||||
|
onPressed: controller.onNext,
|
||||||
|
child: Text('بعدی'),
|
||||||
|
backgroundColor: AppColor.blueNormal,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
controller.nextButtonEnabled,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ROutlinedElevated(
|
||||||
|
enabled: controller.currentIndex.value > 0,
|
||||||
|
onPressed: controller.onPrevious,
|
||||||
|
child: Text('قبلی'),
|
||||||
|
borderColor: AppColor.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
SizedBox(
|
Widget _buildStep(int index) {
|
||||||
width: Get.width,
|
switch (index) {
|
||||||
height: 356,
|
case 0:
|
||||||
child: Card(
|
return firstStepWidget();
|
||||||
color: Colors.white,
|
default:
|
||||||
child: Padding(
|
return Center(
|
||||||
padding: const EdgeInsets.all(16.0),
|
child: Text(
|
||||||
child: Column(
|
'مرحله $index در دست توسعه است',
|
||||||
children: [
|
style: AppFonts.yekan16.copyWith(color: AppColor.redNormal),
|
||||||
Expanded(
|
textDirection: TextDirection.rtl,
|
||||||
child: Container(
|
),
|
||||||
width: Get.width,
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
Widget firstStepWidget() {
|
||||||
color: AppColor.lightGreyNormal,
|
return Form(
|
||||||
borderRadius: BorderRadius.circular(8),
|
child: Column(
|
||||||
),
|
spacing: 16,
|
||||||
child: Center(
|
children: [
|
||||||
child: Assets.images.placeHolder.image(
|
Container(
|
||||||
height: 150,
|
padding: EdgeInsets.all(8),
|
||||||
width: 200,
|
decoration: BoxDecoration(
|
||||||
),
|
border: Border.all(color: AppColor.lightGreyNormal, width: 1),
|
||||||
),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
|
child: Column(
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
Row(children: [Text('اطلاعات دامدار', style: AppFonts.yekan16Bold)]),
|
||||||
|
RTextField(controller: controller.fullNameController, label: 'نام ونام خانوادگی'),
|
||||||
|
RTextField(controller: controller.phoneController, label: 'تلفن'),
|
||||||
|
RTextField(controller: controller.addressController, label: 'ادرس'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
width: Get.width,
|
||||||
|
height: 356.h,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(8.r),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: AppColor.lightGreyNormal, width: 1.w),
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Row(children: [Text('تصویر دامدار', style: AppFonts.yekan16Bold)]),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
width: Get.width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.lightGreyNormal,
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
),
|
),
|
||||||
SizedBox(height: 15),
|
child: Center(
|
||||||
Container(
|
child: ObxValue((tmpImage) {
|
||||||
width: Get.width,
|
if (tmpImage.value == null) {
|
||||||
height: 40,
|
return Assets.vec.placeHolderSvg.svg(height: 150.h, width: 200.w);
|
||||||
clipBehavior: Clip.antiAlias,
|
} else {
|
||||||
decoration: ShapeDecoration(
|
return Image.file(File(tmpImage.value!.path), fit: BoxFit.cover);
|
||||||
color: AppColor.blueNormal,
|
}
|
||||||
shape: RoundedRectangleBorder(
|
}, controller.rancherImage),
|
||||||
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
await controller.pickImage();
|
||||||
|
await showCropDialog();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: Get.width,
|
||||||
|
height: 40.h,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
color: AppColor.blueNormal,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(10.r),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(' دوربین', style: AppFonts.yekan14.copyWith(color: Colors.white)),
|
||||||
|
Icon(CupertinoIcons.arrow_up_doc, color: Colors.white),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
Spacer(),
|
Spacer(),
|
||||||
|
|
||||||
|
/* RElevated(
|
||||||
|
text: 'ارسال تصویر گله',
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed(LiveStockRoutes.tagging);
|
||||||
|
},
|
||||||
|
height: 40,
|
||||||
|
isFullWidth: true,
|
||||||
|
backgroundColor: AppColor.greenNormal,
|
||||||
|
textStyle: AppFonts.yekan16.copyWith(color: Colors.white),
|
||||||
|
),*/
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showCropDialog() async {
|
||||||
|
await Get.dialog(
|
||||||
|
Dialog(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'آیا نیازی به برش تصویر دارید؟',
|
||||||
|
style: AppFonts.yekan16Bold,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
|
||||||
RElevated(
|
const SizedBox(height: 24),
|
||||||
text: 'ارسال تصویر گله',
|
|
||||||
onPressed: () {
|
Row(
|
||||||
Get.toNamed(LiveStockRoutes.tagging);
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
},
|
spacing: 12.w,
|
||||||
height: 40,
|
children: [
|
||||||
isFullWidth: true,
|
RElevated(
|
||||||
backgroundColor: AppColor.greenNormal,
|
height: 40.h,
|
||||||
textStyle: AppFonts.yekan16.copyWith(color: Colors.white),
|
onPressed: () async {
|
||||||
),
|
Get.back();
|
||||||
],
|
await controller.cropImage();
|
||||||
|
},
|
||||||
|
child: const Text('بله'),
|
||||||
|
),
|
||||||
|
ROutlinedElevated(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('خیر'),
|
||||||
|
borderColor: AppColor.error,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Column secondStepWidget() {
|
||||||
|
return 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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class RootLogic extends GetxController {
|
|||||||
|
|
||||||
ProfilePage(),
|
ProfilePage(),
|
||||||
];
|
];
|
||||||
RxInt currentIndex = 1.obs;
|
RxInt currentIndex = 0.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:rasadyar_livestock/presentation/page/root/logic.dart';
|
|||||||
import 'package:rasadyar_livestock/presentation/page/root/view.dart';
|
import 'package:rasadyar_livestock/presentation/page/root/view.dart';
|
||||||
import 'package:rasadyar_livestock/presentation/page/tagging/logic.dart';
|
import 'package:rasadyar_livestock/presentation/page/tagging/logic.dart';
|
||||||
import 'package:rasadyar_livestock/presentation/page/tagging/view.dart';
|
import 'package:rasadyar_livestock/presentation/page/tagging/view.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/widgets/base_page/logic.dart';
|
||||||
import 'package:rasadyar_livestock/presentation/widgets/captcha/logic.dart';
|
import 'package:rasadyar_livestock/presentation/widgets/captcha/logic.dart';
|
||||||
|
|
||||||
part 'app_routes.dart';
|
part 'app_routes.dart';
|
||||||
@@ -38,6 +39,7 @@ sealed class LiveStockPages {
|
|||||||
Get.lazyPut(() => ProfileLogic());
|
Get.lazyPut(() => ProfileLogic());
|
||||||
Get.lazyPut(() => ProfileLogic());
|
Get.lazyPut(() => ProfileLogic());
|
||||||
Get.lazyPut(() => MapWidgetLogic());
|
Get.lazyPut(() => MapWidgetLogic());
|
||||||
|
Get.lazyPut(() => BaseLogic());
|
||||||
}),
|
}),
|
||||||
children: [
|
children: [
|
||||||
/*GetPage(
|
/*GetPage(
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/widgets/base_page/logic.dart';
|
||||||
|
|
||||||
|
RAppBar liveStockAppBar({
|
||||||
|
bool hasBack = true,
|
||||||
|
bool hasFilter = true,
|
||||||
|
bool hasSearch = true,
|
||||||
|
bool isBase = false,
|
||||||
|
VoidCallback? onBackPressed,
|
||||||
|
GestureTapCallback? onFilterTap,
|
||||||
|
GestureTapCallback? onSearchTap,
|
||||||
|
}) {
|
||||||
|
return RAppBar(
|
||||||
|
hasBack: isBase == true ? false : hasBack,
|
||||||
|
onBackPressed: onBackPressed,
|
||||||
|
leadingWidth: 155,
|
||||||
|
leading: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Text('رصددام', style: AppFonts.yekan16Bold.copyWith(color: Colors.white)),
|
||||||
|
Assets.vec.appBarInspectionSvg.svg(width: 24, height: 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
additionalActions: [
|
||||||
|
if (!isBase && hasSearch) searchWidget(onSearchTap),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
if (!isBase && hasFilter) filterWidget(onFilterTap),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureDetector filterWidget(GestureTapCallback? onFilterTap) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onFilterTap,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
children: [
|
||||||
|
Assets.vec.filterOutlineSvg.svg(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||||
|
),
|
||||||
|
Obx(() {
|
||||||
|
final controller = Get.find<BaseLogic>();
|
||||||
|
return Visibility(
|
||||||
|
visible: controller.isFilterSelected.value,
|
||||||
|
child: Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureDetector searchWidget(GestureTapCallback? onSearchTap) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onSearchTap,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
children: [
|
||||||
|
Assets.vec.searchSvg.svg(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||||
|
),
|
||||||
|
Obx(() {
|
||||||
|
final controller = Get.find<BaseLogic>();
|
||||||
|
return Visibility(
|
||||||
|
visible: controller.searchValue.value != null,
|
||||||
|
child: Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
|
class BaseLogic extends GetxController {
|
||||||
|
final RxBool isFilterSelected = false.obs;
|
||||||
|
final RxBool isSearchSelected = false.obs;
|
||||||
|
final TextEditingController searchTextController = TextEditingController();
|
||||||
|
final RxnString searchValue = RxnString();
|
||||||
|
|
||||||
|
void setSearchCallback(void Function(String)? onSearchChanged) {
|
||||||
|
debounce<String?>(searchValue, (val) {
|
||||||
|
if (val != null && val.trim().isNotEmpty) {
|
||||||
|
onSearchChanged?.call(val);
|
||||||
|
}
|
||||||
|
}, time: const Duration(milliseconds: 600));
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleFilter() {
|
||||||
|
isFilterSelected.value = !isFilterSelected.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleSearch() {
|
||||||
|
isSearchSelected.value = !isSearchSelected.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
143
packages/livestock/lib/presentation/widgets/base_page/view.dart
Normal file
143
packages/livestock/lib/presentation/widgets/base_page/view.dart
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/widgets/app_bar/i_app_bar.dart';
|
||||||
|
import 'package:rasadyar_livestock/presentation/widgets/search.dart';
|
||||||
|
|
||||||
|
import 'logic.dart';
|
||||||
|
|
||||||
|
class BasePage extends StatefulWidget {
|
||||||
|
const BasePage({
|
||||||
|
super.key,
|
||||||
|
this.routes,
|
||||||
|
required this.widgets,
|
||||||
|
this.routesWidget,
|
||||||
|
this.floatingActionButtonLocation,
|
||||||
|
this.floatingActionButton,
|
||||||
|
this.onSearchChanged,
|
||||||
|
this.hasBack = true,
|
||||||
|
this.hasFilter = true,
|
||||||
|
this.hasSearch = true,
|
||||||
|
this.isBase = false,
|
||||||
|
this.defaultSearch = true,
|
||||||
|
this.onBackPressed,
|
||||||
|
this.onFilterTap,
|
||||||
|
this.onSearchTap,
|
||||||
|
this.filteringWidget,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<String>? routes;
|
||||||
|
final Widget? routesWidget;
|
||||||
|
final bool defaultSearch;
|
||||||
|
final List<Widget> widgets;
|
||||||
|
final FloatingActionButtonLocation? floatingActionButtonLocation;
|
||||||
|
final Widget? floatingActionButton;
|
||||||
|
final Widget? filteringWidget;
|
||||||
|
final void Function(String?)? onSearchChanged;
|
||||||
|
final bool hasBack;
|
||||||
|
final bool hasFilter;
|
||||||
|
final bool hasSearch;
|
||||||
|
final bool isBase;
|
||||||
|
final VoidCallback? onBackPressed;
|
||||||
|
final GestureTapCallback? onFilterTap;
|
||||||
|
final GestureTapCallback? onSearchTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BasePage> createState() => _BasePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BasePageState extends State<BasePage> {
|
||||||
|
BaseLogic get controller => Get.find<BaseLogic>();
|
||||||
|
Worker? filterWorker;
|
||||||
|
bool _isBottomSheetOpen = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
/* filterWorker = ever(controller.isFilterSelected, (bool isSelected) {
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
if (isSelected && widget.filteringWidget != null) {
|
||||||
|
// بررسی اینکه آیا bottomSheet از قبل باز است یا نه
|
||||||
|
if (_isBottomSheetOpen) {
|
||||||
|
controller.isFilterSelected.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی اینکه آیا route فعلی current است یا نه
|
||||||
|
if (ModalRoute.of(context)?.isCurrent != true) {
|
||||||
|
controller.isFilterSelected.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isBottomSheetOpen = true;
|
||||||
|
Get.bottomSheet(
|
||||||
|
widget.filteringWidget!,
|
||||||
|
isScrollControlled: true,
|
||||||
|
isDismissible: true,
|
||||||
|
enableDrag: true,
|
||||||
|
).then((_) {
|
||||||
|
// تنظیم مقدار به false بعد از بسته شدن bottomSheet
|
||||||
|
if (mounted) {
|
||||||
|
_isBottomSheetOpen = false;
|
||||||
|
controller.isFilterSelected.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
filterWorker?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFilterTap() {
|
||||||
|
if (widget.hasFilter && widget.filteringWidget != null) {
|
||||||
|
final currentRoute = ModalRoute.of(context);
|
||||||
|
if (currentRoute?.isCurrent != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Get.bottomSheet(
|
||||||
|
widget.filteringWidget!,
|
||||||
|
isScrollControlled: true,
|
||||||
|
isDismissible: true,
|
||||||
|
enableDrag: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
onPopInvokedWithResult: (didPop, result) => widget.onBackPressed,
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: AppColor.bgLight,
|
||||||
|
appBar: liveStockAppBar(
|
||||||
|
hasBack: widget.isBase ? false : widget.hasBack,
|
||||||
|
onBackPressed: widget.onBackPressed,
|
||||||
|
hasFilter: widget.hasFilter,
|
||||||
|
hasSearch: widget.hasSearch,
|
||||||
|
isBase: widget.isBase,
|
||||||
|
onFilterTap: widget.hasFilter ? _onFilterTap : null,
|
||||||
|
onSearchTap: widget.hasSearch ? () => controller.toggleSearch() : null,
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
//widget.routesWidget != null ? widget.routesWidget! : buildPageRoute(widget.routes!),
|
||||||
|
if (!widget.isBase && widget.hasSearch && widget.defaultSearch) ...{
|
||||||
|
SearchWidget(onSearchChanged: widget.onSearchChanged),
|
||||||
|
},
|
||||||
|
...widget.widgets,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButtonLocation:
|
||||||
|
widget.floatingActionButtonLocation ?? FloatingActionButtonLocation.startFloat,
|
||||||
|
floatingActionButton: widget.floatingActionButton,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
packages/livestock/lib/presentation/widgets/search.dart
Normal file
82
packages/livestock/lib/presentation/widgets/search.dart
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
|
import 'base_page/logic.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class SearchWidget extends StatefulWidget {
|
||||||
|
const SearchWidget({super.key, this.onSearchChanged});
|
||||||
|
|
||||||
|
final void Function(String?)? onSearchChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SearchWidget> createState() => _SearchWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchWidgetState extends State<SearchWidget> {
|
||||||
|
late final BaseLogic controller;
|
||||||
|
final TextEditingController textEditingController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
controller = Get.find<BaseLogic>();
|
||||||
|
controller.setSearchCallback(widget.onSearchChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ObxValue((data) {
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
height: data.value ? 40 : 0,
|
||||||
|
child: Visibility(
|
||||||
|
visible: data.value,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: RTextField(
|
||||||
|
height: 40,
|
||||||
|
borderColor: AppColor.blackLight,
|
||||||
|
suffixIcon: ObxValue(
|
||||||
|
(data) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: (data.value == null)
|
||||||
|
? Assets.vec.searchSvg.svg(
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
textEditingController.clear();
|
||||||
|
controller.searchValue.value = null;
|
||||||
|
controller.isSearchSelected.value = false;
|
||||||
|
widget.onSearchChanged?.call(null);
|
||||||
|
},
|
||||||
|
enableFeedback: true,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
iconSize: 24,
|
||||||
|
splashRadius: 50,
|
||||||
|
icon: Assets.vec.closeCircleSvg.svg(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
controller.searchValue,
|
||||||
|
),
|
||||||
|
hintText: 'جستجو کنید ...',
|
||||||
|
hintStyle: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
|
||||||
|
filledColor: Colors.white,
|
||||||
|
filled: true,
|
||||||
|
controller: textEditingController,
|
||||||
|
onChanged: (val) => controller.searchValue.value = val,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}, controller.isSearchSelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
pubspec.lock
24
pubspec.lock
@@ -749,6 +749,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.4"
|
version: "4.5.4"
|
||||||
|
image_cropper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_cropper
|
||||||
|
sha256: "4e9c96c029eb5a23798da1b6af39787f964da6ffc78fd8447c140542a9f7c6fc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.0"
|
||||||
|
image_cropper_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_cropper_for_web
|
||||||
|
sha256: fd81ebe36f636576094377aab32673c4e5d1609b32dec16fad98d2b71f1250a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
|
image_cropper_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_cropper_platform_interface
|
||||||
|
sha256: "6ca6b81769abff9a4dcc3bbd3d75f5dfa9de6b870ae9613c8cd237333a4283af"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.1.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user