Merge remote-tracking branch 'origin/refactor_dark_mood_repository'
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:provider/single_child_widget.dart';
|
||||
import 'package:tm_app/core/theme/theme_provider.dart';
|
||||
<<<<<<< HEAD
|
||||
import 'package:tm_app/data/repositories/home_repository.dart';
|
||||
import 'package:tm_app/data/services/home_service.dart';
|
||||
=======
|
||||
import 'package:tm_app/data/repositories/tender_detail_repository.dart';
|
||||
import 'package:tm_app/data/services/tender_detail_service.dart';
|
||||
import 'package:tm_app/view_models/tender_detail_view_model.dart';
|
||||
>>>>>>> origin/refactor_dark_mood_repository
|
||||
|
||||
import '../../data/repositories/auth_repository.dart';
|
||||
import '../../data/services/auth_service.dart';
|
||||
@@ -25,13 +31,16 @@ List<SingleChildWidget> get apiClients {
|
||||
return [
|
||||
Provider(create: (context) => AuthService(networkManager: context.read())),
|
||||
Provider(create: (context) => HomeService(networkManager: context.read())),
|
||||
Provider(create: (context) => TenderDetailService(networkManager: context.read())),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
List<SingleChildWidget> get repositories {
|
||||
return [
|
||||
Provider(create: (context) => AuthRepository(authService: context.read())),
|
||||
Provider(create: (context) => HomeRepository(homeService: context.read())),
|
||||
Provider(create: (context) => TenderDetailRepository(tenderDetailService: context.read())),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -42,6 +51,10 @@ List<SingleChildWidget> get viewModels {
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => HomeViewModel(homeRepository: context.read()),
|
||||
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => TenderDetailViewModel(tenderDetailRepository: context.read()),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tm_app/views/detail/detail_screen.dart';
|
||||
import 'package:tm_app/views/detail/pages/detail_screen.dart';
|
||||
import 'package:tm_app/views/home/pages/home_screen.dart';
|
||||
import 'package:tm_app/views/login/login_screen.dart';
|
||||
import 'package:tm_app/views/login/pages/login_screen.dart';
|
||||
|
||||
import '../../views/profile/profile_screen.dart';
|
||||
import '../../views/tenders/tenders_screen.dart';
|
||||
|
||||
@@ -121,6 +121,7 @@ class AppColors {
|
||||
// Red variations (status colors)
|
||||
static const Color red0 = Color(0xFFF8D6D6);
|
||||
static const Color red10 = Color(0xFFC02525);
|
||||
static const Color red = Color(0xFFFF3B30);
|
||||
|
||||
// Other colors
|
||||
static const Color paleOrange = Color(0xFFfff7ee);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:tm_app/core/utils/result.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_request/tender_detail_request.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
import 'package:tm_app/data/services/tender_detail_service.dart';
|
||||
|
||||
class TenderDetailRepository {
|
||||
TenderDetailRepository({required TenderDetailService tenderDetailService})
|
||||
: _tenderDetailService = tenderDetailService;
|
||||
|
||||
final TenderDetailService _tenderDetailService;
|
||||
|
||||
Future<Result<TenderDetailResponseModel>> getTenderDetail({
|
||||
required TenderDetailRequest request,
|
||||
}) async {
|
||||
return await _tenderDetailService.getTenderDetail(request: request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
const tenderDetailMockData = '''
|
||||
{
|
||||
"data": {
|
||||
"date": "2025-05-21",
|
||||
"status": "Open",
|
||||
"title": "Operation, support and further development of open e-platform Operation, support and further development of open e-platform",
|
||||
"id": "JNDFKMDV-100JF",
|
||||
"approvalDate": "2025-05-21",
|
||||
"submissionDate": "2025-05-21",
|
||||
"client": "Procurement Notice Procurement NoticeProcurement Notice",
|
||||
"deliveryLocation": "Norrbotten County",
|
||||
"referenceNumber": "KLF 2025/120",
|
||||
"country": "UK",
|
||||
"flagAsset": "assets/icons/SE.png",
|
||||
"locationTitle": "Lorem ipsum is amet",
|
||||
"locationDescription": "Luleå Municipality is procuring on behalf of Norrbotten’s e-board (all municipalities in Norrbotten). The procurement includes operation, support, and further development of the existing open e-platform. No additional information, amendments, or answers to questions will be provided after June 16.",
|
||||
"documentName": "PDF, Document",
|
||||
"documentUrl": "https://www.tenders.com",
|
||||
"profileMatch": 0.75,
|
||||
"incompleteResume": true,
|
||||
"incompleteResumeReason": "No experience in e-platform development"
|
||||
}
|
||||
}
|
||||
''';
|
||||
@@ -0,0 +1,16 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'tender_detail_request.freezed.dart';
|
||||
part 'tender_detail_request.g.dart';
|
||||
|
||||
@freezed
|
||||
class TenderDetailRequest with _$TenderDetailRequest {
|
||||
const factory TenderDetailRequest({
|
||||
@JsonKey(name: 'id') required String id,
|
||||
}) = _TenderDetailRequest;
|
||||
|
||||
factory TenderDetailRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$TenderDetailRequestFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// 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 'tender_detail_request.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||
);
|
||||
|
||||
TenderDetailRequest _$TenderDetailRequestFromJson(Map<String, dynamic> json) {
|
||||
return _TenderDetailRequest.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TenderDetailRequest {
|
||||
@JsonKey(name: "id")
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this TenderDetailRequest to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of TenderDetailRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$TenderDetailRequestCopyWith<TenderDetailRequest> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $TenderDetailRequestCopyWith<$Res> {
|
||||
factory $TenderDetailRequestCopyWith(
|
||||
TenderDetailRequest value,
|
||||
$Res Function(TenderDetailRequest) then,
|
||||
) = _$TenderDetailRequestCopyWithImpl<$Res, TenderDetailRequest>;
|
||||
@useResult
|
||||
$Res call({@JsonKey(name: "id") String id});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TenderDetailRequestCopyWithImpl<$Res, $Val extends TenderDetailRequest>
|
||||
implements $TenderDetailRequestCopyWith<$Res> {
|
||||
_$TenderDetailRequestCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of TenderDetailRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? id = null}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
id:
|
||||
null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$TenderDetailRequestImplCopyWith<$Res>
|
||||
implements $TenderDetailRequestCopyWith<$Res> {
|
||||
factory _$$TenderDetailRequestImplCopyWith(
|
||||
_$TenderDetailRequestImpl value,
|
||||
$Res Function(_$TenderDetailRequestImpl) then,
|
||||
) = __$$TenderDetailRequestImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({@JsonKey(name: "id") String id});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$TenderDetailRequestImplCopyWithImpl<$Res>
|
||||
extends _$TenderDetailRequestCopyWithImpl<$Res, _$TenderDetailRequestImpl>
|
||||
implements _$$TenderDetailRequestImplCopyWith<$Res> {
|
||||
__$$TenderDetailRequestImplCopyWithImpl(
|
||||
_$TenderDetailRequestImpl _value,
|
||||
$Res Function(_$TenderDetailRequestImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of TenderDetailRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? id = null}) {
|
||||
return _then(
|
||||
_$TenderDetailRequestImpl(
|
||||
id:
|
||||
null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$TenderDetailRequestImpl implements _TenderDetailRequest {
|
||||
const _$TenderDetailRequestImpl({@JsonKey(name: "id") required this.id});
|
||||
|
||||
factory _$TenderDetailRequestImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$TenderDetailRequestImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: "id")
|
||||
final String id;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TenderDetailRequest(id: $id)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$TenderDetailRequestImpl &&
|
||||
(identical(other.id, id) || other.id == id));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id);
|
||||
|
||||
/// Create a copy of TenderDetailRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TenderDetailRequestImplCopyWith<_$TenderDetailRequestImpl> get copyWith =>
|
||||
__$$TenderDetailRequestImplCopyWithImpl<_$TenderDetailRequestImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$TenderDetailRequestImplToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _TenderDetailRequest implements TenderDetailRequest {
|
||||
const factory _TenderDetailRequest({
|
||||
@JsonKey(name: "id") required final String id,
|
||||
}) = _$TenderDetailRequestImpl;
|
||||
|
||||
factory _TenderDetailRequest.fromJson(Map<String, dynamic> json) =
|
||||
_$TenderDetailRequestImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: "id")
|
||||
String get id;
|
||||
|
||||
/// Create a copy of TenderDetailRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$TenderDetailRequestImplCopyWith<_$TenderDetailRequestImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tender_detail_request.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$TenderDetailRequestImpl _$$TenderDetailRequestImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$TenderDetailRequestImpl(id: json['id'] as String);
|
||||
|
||||
Map<String, dynamic> _$$TenderDetailRequestImplToJson(
|
||||
_$TenderDetailRequestImpl instance,
|
||||
) => <String, dynamic>{'id': instance.id};
|
||||
@@ -0,0 +1,34 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'tender_detail_response_model.freezed.dart';
|
||||
part 'tender_detail_response_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class TenderDetailResponseModel with _$TenderDetailResponseModel {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
const factory TenderDetailResponseModel({
|
||||
required String? date,
|
||||
required String? status,
|
||||
required String? title,
|
||||
required String? id,
|
||||
required String? approvalDate,
|
||||
required String? submissionDate,
|
||||
required String? client,
|
||||
required String? deliveryLocation,
|
||||
required String? referenceNumber,
|
||||
required String? country,
|
||||
required String? flagAsset,
|
||||
required String? locationTitle,
|
||||
required String? locationDescription,
|
||||
required String? documentName,
|
||||
required String? documentUrl,
|
||||
required double? profileMatch,
|
||||
required bool? incompleteResume,
|
||||
required String? incompleteResumeReason,
|
||||
}) = _TenderDetailResponseModel;
|
||||
|
||||
factory TenderDetailResponseModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$TenderDetailResponseModelFromJson(json);
|
||||
}
|
||||
+599
@@ -0,0 +1,599 @@
|
||||
// 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 'tender_detail_response_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||
);
|
||||
|
||||
TenderDetailResponseModel _$TenderDetailResponseModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) {
|
||||
return _TenderDetailResponseModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TenderDetailResponseModel {
|
||||
String? get date => throw _privateConstructorUsedError;
|
||||
String? get status => throw _privateConstructorUsedError;
|
||||
String? get title => throw _privateConstructorUsedError;
|
||||
String? get id => throw _privateConstructorUsedError;
|
||||
String? get approvalDate => throw _privateConstructorUsedError;
|
||||
String? get submissionDate => throw _privateConstructorUsedError;
|
||||
String? get client => throw _privateConstructorUsedError;
|
||||
String? get deliveryLocation => throw _privateConstructorUsedError;
|
||||
String? get referenceNumber => throw _privateConstructorUsedError;
|
||||
String? get country => throw _privateConstructorUsedError;
|
||||
String? get flagAsset => throw _privateConstructorUsedError;
|
||||
String? get locationTitle => throw _privateConstructorUsedError;
|
||||
String? get locationDescription => throw _privateConstructorUsedError;
|
||||
String? get documentName => throw _privateConstructorUsedError;
|
||||
String? get documentUrl => throw _privateConstructorUsedError;
|
||||
double? get profileMatch => throw _privateConstructorUsedError;
|
||||
bool? get incompleteResume => throw _privateConstructorUsedError;
|
||||
String? get incompleteResumeReason => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this TenderDetailResponseModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of TenderDetailResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$TenderDetailResponseModelCopyWith<TenderDetailResponseModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $TenderDetailResponseModelCopyWith<$Res> {
|
||||
factory $TenderDetailResponseModelCopyWith(
|
||||
TenderDetailResponseModel value,
|
||||
$Res Function(TenderDetailResponseModel) then,
|
||||
) = _$TenderDetailResponseModelCopyWithImpl<$Res, TenderDetailResponseModel>;
|
||||
@useResult
|
||||
$Res call({
|
||||
String? date,
|
||||
String? status,
|
||||
String? title,
|
||||
String? id,
|
||||
String? approvalDate,
|
||||
String? submissionDate,
|
||||
String? client,
|
||||
String? deliveryLocation,
|
||||
String? referenceNumber,
|
||||
String? country,
|
||||
String? flagAsset,
|
||||
String? locationTitle,
|
||||
String? locationDescription,
|
||||
String? documentName,
|
||||
String? documentUrl,
|
||||
double? profileMatch,
|
||||
bool? incompleteResume,
|
||||
String? incompleteResumeReason,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TenderDetailResponseModelCopyWithImpl<
|
||||
$Res,
|
||||
$Val extends TenderDetailResponseModel
|
||||
>
|
||||
implements $TenderDetailResponseModelCopyWith<$Res> {
|
||||
_$TenderDetailResponseModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of TenderDetailResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? date = freezed,
|
||||
Object? status = freezed,
|
||||
Object? title = freezed,
|
||||
Object? id = freezed,
|
||||
Object? approvalDate = freezed,
|
||||
Object? submissionDate = freezed,
|
||||
Object? client = freezed,
|
||||
Object? deliveryLocation = freezed,
|
||||
Object? referenceNumber = freezed,
|
||||
Object? country = freezed,
|
||||
Object? flagAsset = freezed,
|
||||
Object? locationTitle = freezed,
|
||||
Object? locationDescription = freezed,
|
||||
Object? documentName = freezed,
|
||||
Object? documentUrl = freezed,
|
||||
Object? profileMatch = freezed,
|
||||
Object? incompleteResume = freezed,
|
||||
Object? incompleteResumeReason = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
date:
|
||||
freezed == date
|
||||
? _value.date
|
||||
: date // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
status:
|
||||
freezed == status
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
title:
|
||||
freezed == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
id:
|
||||
freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
approvalDate:
|
||||
freezed == approvalDate
|
||||
? _value.approvalDate
|
||||
: approvalDate // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
submissionDate:
|
||||
freezed == submissionDate
|
||||
? _value.submissionDate
|
||||
: submissionDate // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
client:
|
||||
freezed == client
|
||||
? _value.client
|
||||
: client // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
deliveryLocation:
|
||||
freezed == deliveryLocation
|
||||
? _value.deliveryLocation
|
||||
: deliveryLocation // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
referenceNumber:
|
||||
freezed == referenceNumber
|
||||
? _value.referenceNumber
|
||||
: referenceNumber // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
country:
|
||||
freezed == country
|
||||
? _value.country
|
||||
: country // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
flagAsset:
|
||||
freezed == flagAsset
|
||||
? _value.flagAsset
|
||||
: flagAsset // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationTitle:
|
||||
freezed == locationTitle
|
||||
? _value.locationTitle
|
||||
: locationTitle // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationDescription:
|
||||
freezed == locationDescription
|
||||
? _value.locationDescription
|
||||
: locationDescription // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
documentName:
|
||||
freezed == documentName
|
||||
? _value.documentName
|
||||
: documentName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
documentUrl:
|
||||
freezed == documentUrl
|
||||
? _value.documentUrl
|
||||
: documentUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
profileMatch:
|
||||
freezed == profileMatch
|
||||
? _value.profileMatch
|
||||
: profileMatch // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
incompleteResume:
|
||||
freezed == incompleteResume
|
||||
? _value.incompleteResume
|
||||
: incompleteResume // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
incompleteResumeReason:
|
||||
freezed == incompleteResumeReason
|
||||
? _value.incompleteResumeReason
|
||||
: incompleteResumeReason // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$TenderDetailResponseModelImplCopyWith<$Res>
|
||||
implements $TenderDetailResponseModelCopyWith<$Res> {
|
||||
factory _$$TenderDetailResponseModelImplCopyWith(
|
||||
_$TenderDetailResponseModelImpl value,
|
||||
$Res Function(_$TenderDetailResponseModelImpl) then,
|
||||
) = __$$TenderDetailResponseModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
String? date,
|
||||
String? status,
|
||||
String? title,
|
||||
String? id,
|
||||
String? approvalDate,
|
||||
String? submissionDate,
|
||||
String? client,
|
||||
String? deliveryLocation,
|
||||
String? referenceNumber,
|
||||
String? country,
|
||||
String? flagAsset,
|
||||
String? locationTitle,
|
||||
String? locationDescription,
|
||||
String? documentName,
|
||||
String? documentUrl,
|
||||
double? profileMatch,
|
||||
bool? incompleteResume,
|
||||
String? incompleteResumeReason,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$TenderDetailResponseModelImplCopyWithImpl<$Res>
|
||||
extends
|
||||
_$TenderDetailResponseModelCopyWithImpl<
|
||||
$Res,
|
||||
_$TenderDetailResponseModelImpl
|
||||
>
|
||||
implements _$$TenderDetailResponseModelImplCopyWith<$Res> {
|
||||
__$$TenderDetailResponseModelImplCopyWithImpl(
|
||||
_$TenderDetailResponseModelImpl _value,
|
||||
$Res Function(_$TenderDetailResponseModelImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of TenderDetailResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? date = freezed,
|
||||
Object? status = freezed,
|
||||
Object? title = freezed,
|
||||
Object? id = freezed,
|
||||
Object? approvalDate = freezed,
|
||||
Object? submissionDate = freezed,
|
||||
Object? client = freezed,
|
||||
Object? deliveryLocation = freezed,
|
||||
Object? referenceNumber = freezed,
|
||||
Object? country = freezed,
|
||||
Object? flagAsset = freezed,
|
||||
Object? locationTitle = freezed,
|
||||
Object? locationDescription = freezed,
|
||||
Object? documentName = freezed,
|
||||
Object? documentUrl = freezed,
|
||||
Object? profileMatch = freezed,
|
||||
Object? incompleteResume = freezed,
|
||||
Object? incompleteResumeReason = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$TenderDetailResponseModelImpl(
|
||||
date:
|
||||
freezed == date
|
||||
? _value.date
|
||||
: date // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
status:
|
||||
freezed == status
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
title:
|
||||
freezed == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
id:
|
||||
freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
approvalDate:
|
||||
freezed == approvalDate
|
||||
? _value.approvalDate
|
||||
: approvalDate // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
submissionDate:
|
||||
freezed == submissionDate
|
||||
? _value.submissionDate
|
||||
: submissionDate // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
client:
|
||||
freezed == client
|
||||
? _value.client
|
||||
: client // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
deliveryLocation:
|
||||
freezed == deliveryLocation
|
||||
? _value.deliveryLocation
|
||||
: deliveryLocation // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
referenceNumber:
|
||||
freezed == referenceNumber
|
||||
? _value.referenceNumber
|
||||
: referenceNumber // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
country:
|
||||
freezed == country
|
||||
? _value.country
|
||||
: country // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
flagAsset:
|
||||
freezed == flagAsset
|
||||
? _value.flagAsset
|
||||
: flagAsset // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationTitle:
|
||||
freezed == locationTitle
|
||||
? _value.locationTitle
|
||||
: locationTitle // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationDescription:
|
||||
freezed == locationDescription
|
||||
? _value.locationDescription
|
||||
: locationDescription // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
documentName:
|
||||
freezed == documentName
|
||||
? _value.documentName
|
||||
: documentName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
documentUrl:
|
||||
freezed == documentUrl
|
||||
? _value.documentUrl
|
||||
: documentUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
profileMatch:
|
||||
freezed == profileMatch
|
||||
? _value.profileMatch
|
||||
: profileMatch // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
incompleteResume:
|
||||
freezed == incompleteResume
|
||||
? _value.incompleteResume
|
||||
: incompleteResume // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
incompleteResumeReason:
|
||||
freezed == incompleteResumeReason
|
||||
? _value.incompleteResumeReason
|
||||
: incompleteResumeReason // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class _$TenderDetailResponseModelImpl implements _TenderDetailResponseModel {
|
||||
const _$TenderDetailResponseModelImpl({
|
||||
required this.date,
|
||||
required this.status,
|
||||
required this.title,
|
||||
required this.id,
|
||||
required this.approvalDate,
|
||||
required this.submissionDate,
|
||||
required this.client,
|
||||
required this.deliveryLocation,
|
||||
required this.referenceNumber,
|
||||
required this.country,
|
||||
required this.flagAsset,
|
||||
required this.locationTitle,
|
||||
required this.locationDescription,
|
||||
required this.documentName,
|
||||
required this.documentUrl,
|
||||
required this.profileMatch,
|
||||
required this.incompleteResume,
|
||||
required this.incompleteResumeReason,
|
||||
});
|
||||
|
||||
factory _$TenderDetailResponseModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$TenderDetailResponseModelImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String? date;
|
||||
@override
|
||||
final String? status;
|
||||
@override
|
||||
final String? title;
|
||||
@override
|
||||
final String? id;
|
||||
@override
|
||||
final String? approvalDate;
|
||||
@override
|
||||
final String? submissionDate;
|
||||
@override
|
||||
final String? client;
|
||||
@override
|
||||
final String? deliveryLocation;
|
||||
@override
|
||||
final String? referenceNumber;
|
||||
@override
|
||||
final String? country;
|
||||
@override
|
||||
final String? flagAsset;
|
||||
@override
|
||||
final String? locationTitle;
|
||||
@override
|
||||
final String? locationDescription;
|
||||
@override
|
||||
final String? documentName;
|
||||
@override
|
||||
final String? documentUrl;
|
||||
@override
|
||||
final double? profileMatch;
|
||||
@override
|
||||
final bool? incompleteResume;
|
||||
@override
|
||||
final String? incompleteResumeReason;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TenderDetailResponseModel(date: $date, status: $status, title: $title, id: $id, approvalDate: $approvalDate, submissionDate: $submissionDate, client: $client, deliveryLocation: $deliveryLocation, referenceNumber: $referenceNumber, country: $country, flagAsset: $flagAsset, locationTitle: $locationTitle, locationDescription: $locationDescription, documentName: $documentName, documentUrl: $documentUrl, profileMatch: $profileMatch, incompleteResume: $incompleteResume, incompleteResumeReason: $incompleteResumeReason)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$TenderDetailResponseModelImpl &&
|
||||
(identical(other.date, date) || other.date == date) &&
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
(identical(other.title, title) || other.title == title) &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.approvalDate, approvalDate) ||
|
||||
other.approvalDate == approvalDate) &&
|
||||
(identical(other.submissionDate, submissionDate) ||
|
||||
other.submissionDate == submissionDate) &&
|
||||
(identical(other.client, client) || other.client == client) &&
|
||||
(identical(other.deliveryLocation, deliveryLocation) ||
|
||||
other.deliveryLocation == deliveryLocation) &&
|
||||
(identical(other.referenceNumber, referenceNumber) ||
|
||||
other.referenceNumber == referenceNumber) &&
|
||||
(identical(other.country, country) || other.country == country) &&
|
||||
(identical(other.flagAsset, flagAsset) ||
|
||||
other.flagAsset == flagAsset) &&
|
||||
(identical(other.locationTitle, locationTitle) ||
|
||||
other.locationTitle == locationTitle) &&
|
||||
(identical(other.locationDescription, locationDescription) ||
|
||||
other.locationDescription == locationDescription) &&
|
||||
(identical(other.documentName, documentName) ||
|
||||
other.documentName == documentName) &&
|
||||
(identical(other.documentUrl, documentUrl) ||
|
||||
other.documentUrl == documentUrl) &&
|
||||
(identical(other.profileMatch, profileMatch) ||
|
||||
other.profileMatch == profileMatch) &&
|
||||
(identical(other.incompleteResume, incompleteResume) ||
|
||||
other.incompleteResume == incompleteResume) &&
|
||||
(identical(other.incompleteResumeReason, incompleteResumeReason) ||
|
||||
other.incompleteResumeReason == incompleteResumeReason));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
date,
|
||||
status,
|
||||
title,
|
||||
id,
|
||||
approvalDate,
|
||||
submissionDate,
|
||||
client,
|
||||
deliveryLocation,
|
||||
referenceNumber,
|
||||
country,
|
||||
flagAsset,
|
||||
locationTitle,
|
||||
locationDescription,
|
||||
documentName,
|
||||
documentUrl,
|
||||
profileMatch,
|
||||
incompleteResume,
|
||||
incompleteResumeReason,
|
||||
);
|
||||
|
||||
/// Create a copy of TenderDetailResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TenderDetailResponseModelImplCopyWith<_$TenderDetailResponseModelImpl>
|
||||
get copyWith => __$$TenderDetailResponseModelImplCopyWithImpl<
|
||||
_$TenderDetailResponseModelImpl
|
||||
>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$TenderDetailResponseModelImplToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _TenderDetailResponseModel implements TenderDetailResponseModel {
|
||||
const factory _TenderDetailResponseModel({
|
||||
required final String? date,
|
||||
required final String? status,
|
||||
required final String? title,
|
||||
required final String? id,
|
||||
required final String? approvalDate,
|
||||
required final String? submissionDate,
|
||||
required final String? client,
|
||||
required final String? deliveryLocation,
|
||||
required final String? referenceNumber,
|
||||
required final String? country,
|
||||
required final String? flagAsset,
|
||||
required final String? locationTitle,
|
||||
required final String? locationDescription,
|
||||
required final String? documentName,
|
||||
required final String? documentUrl,
|
||||
required final double? profileMatch,
|
||||
required final bool? incompleteResume,
|
||||
required final String? incompleteResumeReason,
|
||||
}) = _$TenderDetailResponseModelImpl;
|
||||
|
||||
factory _TenderDetailResponseModel.fromJson(Map<String, dynamic> json) =
|
||||
_$TenderDetailResponseModelImpl.fromJson;
|
||||
|
||||
@override
|
||||
String? get date;
|
||||
@override
|
||||
String? get status;
|
||||
@override
|
||||
String? get title;
|
||||
@override
|
||||
String? get id;
|
||||
@override
|
||||
String? get approvalDate;
|
||||
@override
|
||||
String? get submissionDate;
|
||||
@override
|
||||
String? get client;
|
||||
@override
|
||||
String? get deliveryLocation;
|
||||
@override
|
||||
String? get referenceNumber;
|
||||
@override
|
||||
String? get country;
|
||||
@override
|
||||
String? get flagAsset;
|
||||
@override
|
||||
String? get locationTitle;
|
||||
@override
|
||||
String? get locationDescription;
|
||||
@override
|
||||
String? get documentName;
|
||||
@override
|
||||
String? get documentUrl;
|
||||
@override
|
||||
double? get profileMatch;
|
||||
@override
|
||||
bool? get incompleteResume;
|
||||
@override
|
||||
String? get incompleteResumeReason;
|
||||
|
||||
/// Create a copy of TenderDetailResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$TenderDetailResponseModelImplCopyWith<_$TenderDetailResponseModelImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tender_detail_response_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$TenderDetailResponseModelImpl _$$TenderDetailResponseModelImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$TenderDetailResponseModelImpl(
|
||||
date: json['date'] as String?,
|
||||
status: json['status'] as String?,
|
||||
title: json['title'] as String?,
|
||||
id: json['id'] as String?,
|
||||
approvalDate: json['approvalDate'] as String?,
|
||||
submissionDate: json['submissionDate'] as String?,
|
||||
client: json['client'] as String?,
|
||||
deliveryLocation: json['deliveryLocation'] as String?,
|
||||
referenceNumber: json['referenceNumber'] as String?,
|
||||
country: json['country'] as String?,
|
||||
flagAsset: json['flagAsset'] as String?,
|
||||
locationTitle: json['locationTitle'] as String?,
|
||||
locationDescription: json['locationDescription'] as String?,
|
||||
documentName: json['documentName'] as String?,
|
||||
documentUrl: json['documentUrl'] as String?,
|
||||
profileMatch: (json['profileMatch'] as num?)?.toDouble(),
|
||||
incompleteResume: json['incompleteResume'] as bool?,
|
||||
incompleteResumeReason: json['incompleteResumeReason'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TenderDetailResponseModelImplToJson(
|
||||
_$TenderDetailResponseModelImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'date': instance.date,
|
||||
'status': instance.status,
|
||||
'title': instance.title,
|
||||
'id': instance.id,
|
||||
'approvalDate': instance.approvalDate,
|
||||
'submissionDate': instance.submissionDate,
|
||||
'client': instance.client,
|
||||
'deliveryLocation': instance.deliveryLocation,
|
||||
'referenceNumber': instance.referenceNumber,
|
||||
'country': instance.country,
|
||||
'flagAsset': instance.flagAsset,
|
||||
'locationTitle': instance.locationTitle,
|
||||
'locationDescription': instance.locationDescription,
|
||||
'documentName': instance.documentName,
|
||||
'documentUrl': instance.documentUrl,
|
||||
'profileMatch': instance.profileMatch,
|
||||
'incompleteResume': instance.incompleteResume,
|
||||
'incompleteResumeReason': instance.incompleteResumeReason,
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:tm_app/core/utils/logger.dart';
|
||||
import 'package:tm_app/core/utils/result.dart';
|
||||
import 'package:tm_app/data/services/mock_data/tender_detail_mock.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_request/tender_detail_request.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
import 'package:tm_app/data/services/network_manager.dart';
|
||||
|
||||
class TenderDetailService {
|
||||
TenderDetailService({required NetworkManager networkManager})
|
||||
: _networkManager = networkManager;
|
||||
|
||||
final NetworkManager _networkManager;
|
||||
|
||||
Future<Result<TenderDetailResponseModel>> getTenderDetail({
|
||||
required TenderDetailRequest request,
|
||||
}) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
appLogger.info('get tender detail success');
|
||||
|
||||
return Result.ok(
|
||||
TenderDetailResponseModel.fromJson(
|
||||
jsonDecode(tenderDetailMockData)['data'],
|
||||
),
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.error('get tender detail failed: $e', stackTrace: stackTrace);
|
||||
return Result.error(Exception(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../core/utils/result.dart';
|
||||
import '../data/models/user_model.dart';
|
||||
import '../data/repositories/auth_repository.dart';
|
||||
@@ -8,8 +7,25 @@ class AuthViewModel with ChangeNotifier {
|
||||
final AuthRepository _authRepository;
|
||||
|
||||
AuthViewModel({required AuthRepository authRepository})
|
||||
: _authRepository = authRepository;
|
||||
: _authRepository = authRepository {
|
||||
usernameController.addListener(_onTextChanged);
|
||||
passwordController.addListener(_onTextChanged);
|
||||
usernameFocus.addListener(_onTextChanged);
|
||||
passwordFocus.addListener(_onTextChanged);
|
||||
}
|
||||
|
||||
final TextEditingController usernameController = TextEditingController();
|
||||
final TextEditingController passwordController = TextEditingController();
|
||||
final FocusNode usernameFocus = FocusNode();
|
||||
final FocusNode passwordFocus = FocusNode();
|
||||
|
||||
bool _obscurePassword = true;
|
||||
bool get obscurePassword => _obscurePassword;
|
||||
bool get isLoginEnabled =>
|
||||
usernameController.text.isNotEmpty &&
|
||||
passwordController.text.isNotEmpty;
|
||||
|
||||
// Auth state
|
||||
bool _isLoading = false;
|
||||
String? _errorMessage;
|
||||
UserModel? _loggedInUser;
|
||||
@@ -18,25 +34,44 @@ class AuthViewModel with ChangeNotifier {
|
||||
String? get errorMessage => _errorMessage;
|
||||
UserModel? get loggedInUser => _loggedInUser;
|
||||
|
||||
Future<void> login(String email, String password) async {
|
||||
void togglePasswordVisibility() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _onTextChanged() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> login() async {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
|
||||
final result = await _authRepository.login(email, password);
|
||||
final result = await _authRepository.login(
|
||||
usernameController.text.trim(),
|
||||
passwordController.text.trim(),
|
||||
);
|
||||
|
||||
switch (result) {
|
||||
case Ok<UserModel>():
|
||||
_loggedInUser = result.value;
|
||||
// موفقیتآمیز
|
||||
break;
|
||||
case Error<UserModel>():
|
||||
_errorMessage = result.error.toString();
|
||||
// خطا
|
||||
break;
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
usernameController.dispose();
|
||||
passwordController.dispose();
|
||||
usernameFocus.dispose();
|
||||
passwordFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// lib/view_models/login_view_model.dart
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoginViewModel with ChangeNotifier {
|
||||
final TextEditingController usernameController = TextEditingController();
|
||||
final TextEditingController passwordController = TextEditingController();
|
||||
|
||||
final FocusNode usernameFocus = FocusNode();
|
||||
final FocusNode passwordFocus = FocusNode();
|
||||
|
||||
bool _obscurePassword = true;
|
||||
|
||||
LoginViewModel() {
|
||||
usernameController.addListener(_onTextChanged);
|
||||
passwordController.addListener(_onTextChanged);
|
||||
usernameFocus.addListener(_onTextChanged);
|
||||
passwordFocus.addListener(_onTextChanged);
|
||||
}
|
||||
|
||||
bool get obscurePassword => _obscurePassword;
|
||||
bool get isLoginEnabled =>
|
||||
usernameController.text.isNotEmpty &&
|
||||
passwordController.text.isNotEmpty;
|
||||
|
||||
void togglePasswordVisibility() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _onTextChanged() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
usernameController.dispose();
|
||||
passwordController.dispose();
|
||||
usernameFocus.dispose();
|
||||
passwordFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/utils/result.dart';
|
||||
import 'package:tm_app/data/repositories/tender_detail_repository.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_request/tender_detail_request.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
|
||||
class TenderDetailViewModel with ChangeNotifier {
|
||||
final TenderDetailRepository _tenderDetailRepository;
|
||||
|
||||
TenderDetailViewModel({required TenderDetailRepository tenderDetailRepository})
|
||||
: _tenderDetailRepository = tenderDetailRepository;
|
||||
|
||||
bool _isLoading = false;
|
||||
String? _errorMessage;
|
||||
TenderDetailResponseModel? _tenderDetail;
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
String? get errorMessage => _errorMessage;
|
||||
TenderDetailResponseModel? get tenderDetail => _tenderDetail;
|
||||
|
||||
Future<void> getTenderDetail({required String id}) async {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
|
||||
final request = TenderDetailRequest(id: id);
|
||||
final result = await _tenderDetailRepository.getTenderDetail(request: request);
|
||||
switch (result) {
|
||||
case Ok<TenderDetailResponseModel>():
|
||||
_tenderDetail = result.value;
|
||||
break;
|
||||
case Error<TenderDetailResponseModel>():
|
||||
_errorMessage = result.error.toString();
|
||||
break;
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_card.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
|
||||
|
||||
class TenderDetailDesktopScreen extends StatelessWidget {
|
||||
const TenderDetailDesktopScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: _buildAppBar(context),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 740,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TenderDetailHeader(isBig: true,),
|
||||
SizedBox(height: 24.0.h()),
|
||||
const TenderDetailCard(),
|
||||
SizedBox(height: 24.0.h()),
|
||||
TenderDetailActions(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
height: 24.0.w(),
|
||||
width: 24.0.w(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppStrings.tenderDetailTitle,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_card.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
|
||||
|
||||
class TenderDetailMobileScreen extends StatelessWidget {
|
||||
const TenderDetailMobileScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: _buildAppBar(context),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TenderDetailHeader(isBig: false),
|
||||
SizedBox(height: 24.0.h()),
|
||||
const TenderDetailCard(),
|
||||
SizedBox(height: 24.0.h()),
|
||||
TenderDetailActions(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
height: 24.0.w(),
|
||||
width: 24.0.w(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppStrings.tenderDetailTitle,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/utils/display_size_detector.dart';
|
||||
import 'package:tm_app/views/detail/detail_desktop_screen.dart';
|
||||
import 'package:tm_app/views/detail/detail_mobile_screen.dart';
|
||||
import 'package:tm_app/views/detail/detail_tablet_screen.dart';
|
||||
|
||||
class TenderDetailScreen extends StatelessWidget {
|
||||
const TenderDetailScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final displaySize = MediaQuery.of(context).size.displaySize;
|
||||
|
||||
switch (displaySize) {
|
||||
case DisplaySize.large:
|
||||
case DisplaySize.medium:
|
||||
return const TenderDetailDesktopScreen();
|
||||
case DisplaySize.small:
|
||||
return const TenderDetailTabletScreen();
|
||||
case DisplaySize.extraSmall:
|
||||
return const TenderDetailMobileScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_card.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
|
||||
|
||||
class TenderDetailTabletScreen extends StatelessWidget {
|
||||
const TenderDetailTabletScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: _buildAppBar(context),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 768,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TenderDetailHeader(isBig: true,),
|
||||
SizedBox(height: 24.0.h()),
|
||||
const TenderDetailCard(),
|
||||
SizedBox(height: 24.0.h()),
|
||||
TenderDetailActions(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
height: 24.0.w(),
|
||||
width: 24.0.w(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppStrings.tenderDetailTitle,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/view_models/tender_detail_view_model.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
|
||||
|
||||
class TenderDetailDesktopPage extends StatefulWidget {
|
||||
final String tenderId;
|
||||
const TenderDetailDesktopPage({required this.tenderId, super.key});
|
||||
|
||||
@override
|
||||
State<TenderDetailDesktopPage> createState() =>
|
||||
_TenderDetailDesktopPageState();
|
||||
}
|
||||
|
||||
class _TenderDetailDesktopPageState extends State<TenderDetailDesktopPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() {
|
||||
context.read<TenderDetailViewModel>().getTenderDetail(
|
||||
id: widget.tenderId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
appBar: _buildAppBar(context),
|
||||
body: Consumer<TenderDetailViewModel>(
|
||||
builder: (context, tenderViewModel, child) {
|
||||
if (tenderViewModel.isLoading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: AppColors.secondary50),
|
||||
);
|
||||
}
|
||||
|
||||
if (tenderViewModel.errorMessage != null) {
|
||||
return Center(child: Text(tenderViewModel.errorMessage!));
|
||||
}
|
||||
|
||||
final detail = tenderViewModel.tenderDetail;
|
||||
if (detail == null) {
|
||||
return const Center(child: Text('No tender details available'));
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 740,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TenderDetailHeader(isScreenBig: true, detail: detail),
|
||||
SizedBox(height: 32.0.h()),
|
||||
TenderDetailActions(isScreenBig: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
child: AppBar(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
height: 24.0.w(),
|
||||
width: 24.0.w(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppStrings.tenderDetailTitle,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey70,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/view_models/tender_detail_view_model.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
|
||||
|
||||
class TenderDetailMobilePage extends StatefulWidget {
|
||||
final String tenderId;
|
||||
const TenderDetailMobilePage({super.key, required this.tenderId});
|
||||
|
||||
@override
|
||||
State<TenderDetailMobilePage> createState() => _TenderDetailMobilePageState();
|
||||
|
||||
}
|
||||
|
||||
|
||||
class _TenderDetailMobilePageState extends State<TenderDetailMobilePage> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() {
|
||||
context.read<TenderDetailViewModel>().getTenderDetail(id: widget.tenderId);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
appBar: _buildAppBar(context),
|
||||
body: Consumer<TenderDetailViewModel>(
|
||||
builder: (context, tenderViewModel, child) {
|
||||
if (tenderViewModel.isLoading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: AppColors.secondary50),
|
||||
);
|
||||
}
|
||||
|
||||
if (tenderViewModel.errorMessage != null) {
|
||||
return Center(child: Text(tenderViewModel.errorMessage!));
|
||||
}
|
||||
|
||||
final detail = tenderViewModel.tenderDetail;
|
||||
if (detail == null) {
|
||||
return const Center(child: Text("No tender details available"));
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TenderDetailHeader(isScreenBig: false, detail: detail),
|
||||
SizedBox(height: 24.0.h()),
|
||||
TenderDetailActions(isScreenBig: false),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
child: AppBar(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
height: 24.0.w(),
|
||||
width: 24.0.w(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppStrings.tenderDetailTitle,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey70,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/detail/pages/detail_desktop_page.dart';
|
||||
import 'package:tm_app/views/detail/pages/detail_mobile_page.dart';
|
||||
import 'package:tm_app/views/detail/pages/detail_tablet_page.dart';
|
||||
import 'package:tm_app/views/shared/responsive_builder.dart';
|
||||
|
||||
class TenderDetailScreen extends StatelessWidget {
|
||||
const TenderDetailScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return ResponsiveBuilder(
|
||||
mobile: TenderDetailMobilePage(tenderId: 'JNDFKMDV-100JF',),
|
||||
tablet: TenderDetailTabletPage(tenderId: 'JNDFKMDV-100JF',),
|
||||
desktop: TenderDetailDesktopPage(tenderId: 'JNDFKMDV-100JF',),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/view_models/tender_detail_view_model.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
|
||||
|
||||
class TenderDetailTabletPage extends StatefulWidget {
|
||||
final String tenderId;
|
||||
const TenderDetailTabletPage({required this.tenderId, super.key});
|
||||
|
||||
@override
|
||||
State<TenderDetailTabletPage> createState() => _TenderDetailTabletPageState();
|
||||
}
|
||||
|
||||
class _TenderDetailTabletPageState extends State<TenderDetailTabletPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() {
|
||||
context.read<TenderDetailViewModel>().getTenderDetail(
|
||||
id: widget.tenderId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
appBar: _buildAppBar(context),
|
||||
body: Consumer<TenderDetailViewModel>(
|
||||
builder: (context, tenderViewModel, child) {
|
||||
if (tenderViewModel.isLoading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: AppColors.secondary50),
|
||||
);
|
||||
}
|
||||
|
||||
if (tenderViewModel.errorMessage != null) {
|
||||
return Center(child: Text(tenderViewModel.errorMessage!));
|
||||
}
|
||||
|
||||
final detail = tenderViewModel.tenderDetail;
|
||||
if (detail == null) {
|
||||
return const Center(child: Text('No tender details available'));
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 768,
|
||||
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TenderDetailHeader(isScreenBig: true, detail: detail),
|
||||
SizedBox(height: 28.0.h()),
|
||||
TenderDetailActions(isScreenBig: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
child: AppBar(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
elevation: 0,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
AssetsManager.arrowLeft,
|
||||
height: 24.0.w(),
|
||||
width: 24.0.w(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppStrings.tenderDetailTitle,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey70,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0.sp(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,22 @@ class DeadlineItem extends StatelessWidget {
|
||||
final String approvalDate;
|
||||
final String submissionText;
|
||||
final String submissionDate;
|
||||
final bool isBig;
|
||||
final bool isScreenBig;
|
||||
|
||||
const DeadlineItem({
|
||||
required this.title, required this.approvalText, required this.approvalDate, required this.submissionText, required this.submissionDate, required this.isBig, super.key,
|
||||
required this.title,
|
||||
required this.approvalText,
|
||||
required this.approvalDate,
|
||||
required this.submissionText,
|
||||
required this.submissionDate,
|
||||
required this.isScreenBig,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return isBig ? Padding(
|
||||
return isScreenBig
|
||||
? Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -95,7 +102,8 @@ class DeadlineItem extends StatelessWidget {
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
) : Padding(
|
||||
)
|
||||
: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -5,11 +5,36 @@ import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/login/widgets/login_button.dart';
|
||||
|
||||
class TenderDetailActions extends StatelessWidget {
|
||||
const TenderDetailActions({super.key});
|
||||
final bool isScreenBig;
|
||||
const TenderDetailActions({required this.isScreenBig, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
return isScreenBig
|
||||
? Row(
|
||||
children: [
|
||||
Spacer(),
|
||||
BaseButton(
|
||||
isEnabled: true,
|
||||
text: AppStrings.tenderRejectButton,
|
||||
textColor: AppColors.red10,
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
borderWidth: 1,
|
||||
onPressed: () {},
|
||||
width: 120.0.w(),
|
||||
),
|
||||
SizedBox(width: 5.0.w()),
|
||||
BaseButton(
|
||||
isEnabled: true,
|
||||
text: AppStrings.tenderSubmitButton,
|
||||
backgroundColor: AppColors.lightBlue,
|
||||
textColor: AppColors.mainBlue,
|
||||
onPressed: () {},
|
||||
width: 120.0.w(),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
BaseButton(
|
||||
isEnabled: true,
|
||||
@@ -24,7 +49,7 @@ class TenderDetailActions extends StatelessWidget {
|
||||
text: AppStrings.tenderRejectButton,
|
||||
borderColor: AppColors.red10,
|
||||
textColor: AppColors.red10,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
borderWidth: 1,
|
||||
onPressed: () {},
|
||||
),
|
||||
|
||||
@@ -20,11 +20,11 @@ class TenderDetailCard extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
AppStrings.tenderMatchProfile,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.black87,
|
||||
fontSize: 12.0.sp(),
|
||||
color: AppColors.grey80,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
@@ -46,7 +46,7 @@ class TenderDetailCard extends StatelessWidget {
|
||||
value: 0.75,
|
||||
backgroundColor: AppColors.grey20,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(AppColors.jellyBean),
|
||||
minHeight: 6,
|
||||
minHeight: 6.0.h(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
@@ -54,26 +54,26 @@ class TenderDetailCard extends StatelessWidget {
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(Icons.warning_rounded, color: Colors.red),
|
||||
const Icon(Icons.warning_rounded, color: AppColors.red),
|
||||
SizedBox(width: 8.0.w()),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
children: [
|
||||
Text(
|
||||
AppStrings.tenderIncompleteResume,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
color: Colors.black87,
|
||||
fontSize: 16.0.sp(),
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.0),
|
||||
SizedBox(height: 4.0.h()),
|
||||
Text(
|
||||
AppStrings.tenderNoExperience,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Colors.black54,
|
||||
fontSize: 14.0.sp(),
|
||||
color: AppColors.grey70,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
import 'package:tm_app/views/detail/widgets/status_tag.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_detail_info_section.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_document_section.dart';
|
||||
import 'package:tm_app/views/detail/widgets/tender_location_section.dart';
|
||||
|
||||
class TenderDetailHeader extends StatelessWidget {
|
||||
final bool isBig;
|
||||
const TenderDetailHeader({required this.isBig, super.key});
|
||||
final bool isScreenBig;
|
||||
final TenderDetailResponseModel detail;
|
||||
|
||||
const TenderDetailHeader({required this.isScreenBig, required this.detail, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: AppColors.backgroundColor,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(width: 0.5, color: AppColors.grey50),
|
||||
),
|
||||
@@ -29,10 +31,10 @@ class TenderDetailHeader extends StatelessWidget {
|
||||
SizedBox(height: 10.0.h()),
|
||||
const Divider(),
|
||||
SizedBox(height: 5.0.h()),
|
||||
TenderDetailInfoSection(isBig: isBig,),
|
||||
TenderLocationSection(),
|
||||
TenderDetailInfoSection(isScreenBig: isScreenBig, detail: detail),
|
||||
TenderLocationSection(detail: detail),
|
||||
SizedBox(height: 16.0.h()),
|
||||
TenderDocumentSection(),
|
||||
TenderDocumentSection(detail: detail),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -42,15 +44,15 @@ class TenderDetailHeader extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
AppStrings.tenderDateExample,
|
||||
detail.date ?? '',
|
||||
style: TextStyle(
|
||||
color: AppColors.grey60,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12.0.sp(),
|
||||
),
|
||||
),
|
||||
const StatusTag(
|
||||
text: AppStrings.tenderStatusOpen,
|
||||
StatusTag(
|
||||
text: detail.status ?? '',
|
||||
textColor: AppColors.cyanTeal,
|
||||
backgroundColor: AppColors.cyanAqua,
|
||||
),
|
||||
@@ -58,7 +60,7 @@ class TenderDetailHeader extends StatelessWidget {
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
AppStrings.tenderTitleExample,
|
||||
detail.title ?? '',
|
||||
style: TextStyle(
|
||||
color: AppColors.grey80,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
import 'package:tm_app/views/detail/widgets/deadline_item.dart';
|
||||
import 'package:tm_app/views/detail/widgets/info_item.dart';
|
||||
|
||||
class TenderDetailInfoSection extends StatelessWidget {
|
||||
final bool isBig;
|
||||
final bool isScreenBig;
|
||||
final TenderDetailResponseModel detail;
|
||||
|
||||
const TenderDetailInfoSection({required this.isBig ,super.key});
|
||||
const TenderDetailInfoSection({required this.isScreenBig, required this.detail, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -15,26 +17,27 @@ class TenderDetailInfoSection extends StatelessWidget {
|
||||
children: [
|
||||
InfoItem(
|
||||
title: AppStrings.tenderIdLabel,
|
||||
value: AppStrings.tenderIdExample,
|
||||
value: detail.id ?? '',
|
||||
),
|
||||
DeadlineItem(
|
||||
title: AppStrings.tenderDeadlineLabel,
|
||||
approvalText: AppStrings.tenderApprovalText,
|
||||
approvalDate: AppStrings.tenderApprovalDateExample,
|
||||
approvalDate: detail.approvalDate ?? '',
|
||||
submissionText: AppStrings.tenderSubmissionText,
|
||||
submissionDate: AppStrings.tenderSubmissionDateExample, isBig: isBig,
|
||||
submissionDate: detail.submissionDate ?? '',
|
||||
isScreenBig: isScreenBig,
|
||||
),
|
||||
InfoItem(
|
||||
title: AppStrings.tenderClientLabel,
|
||||
value: AppStrings.tenderClientExample,
|
||||
value: detail.client ?? '',
|
||||
),
|
||||
InfoItem(
|
||||
title: AppStrings.tenderDeliveryLocationsLabel,
|
||||
value: AppStrings.tenderDeliveryLocationExample,
|
||||
value: detail.deliveryLocation ?? '',
|
||||
),
|
||||
InfoItem(
|
||||
title: AppStrings.tenderReferenceNumberLabel,
|
||||
value: AppStrings.tenderReferenceNumberExample,
|
||||
value: detail.referenceNumber ?? '',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
class TenderDocumentSection extends StatelessWidget {
|
||||
const TenderDocumentSection({super.key});
|
||||
final TenderDetailResponseModel detail;
|
||||
|
||||
const TenderDocumentSection({super.key, required this.detail});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (detail.documentUrl != null) {
|
||||
//launchUrl(Uri.parse(detail.documentUrl!));
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(AssetsManager.export),
|
||||
SizedBox(height: 6.0.h()),
|
||||
Text(
|
||||
AppStrings.tenderPdfDocument,
|
||||
detail.documentName ?? '',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16.0.sp(),
|
||||
color: Colors.blue,
|
||||
color: AppColors.textBlue,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/constants/strings.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart';
|
||||
|
||||
class TenderLocationSection extends StatelessWidget {
|
||||
const TenderLocationSection({super.key});
|
||||
final TenderDetailResponseModel detail;
|
||||
|
||||
const TenderLocationSection({super.key, required this.detail});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -18,7 +21,7 @@ class TenderLocationSection extends StatelessWidget {
|
||||
SizedBox(
|
||||
height: 45.0.h(),
|
||||
child: Text(
|
||||
AppStrings.tenderReferenceNumberLabel,
|
||||
detail.locationTitle ?? '',
|
||||
style: TextStyle(
|
||||
color: AppColors.grey80,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -35,7 +38,7 @@ class TenderLocationSection extends StatelessWidget {
|
||||
),
|
||||
SizedBox(width: 4.0.w()),
|
||||
Text(
|
||||
AppStrings.tenderLocationCountry,
|
||||
detail.country ?? '',
|
||||
style: TextStyle(
|
||||
color: AppColors.grey70,
|
||||
fontSize: 14.0.sp(),
|
||||
@@ -44,7 +47,7 @@ class TenderLocationSection extends StatelessWidget {
|
||||
),
|
||||
SizedBox(width: 4.0.w()),
|
||||
Image.asset(
|
||||
AssetsManager.seFlag,
|
||||
detail.flagAsset ?? AssetsManager.seFlag,
|
||||
height: 20.0.h(),
|
||||
width: 20.0.w(),
|
||||
),
|
||||
@@ -57,7 +60,7 @@ class TenderLocationSection extends StatelessWidget {
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
AppStrings.locationDescription,
|
||||
detail.locationDescription ?? '',
|
||||
style: TextStyle(fontWeight: FontWeight.w400, fontSize: 14.0.sp()),
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
// lib/views/login_desktop_screen.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
|
||||
import '../../core/constants/assets.dart';
|
||||
import '../../core/constants/strings.dart';
|
||||
import '../../core/utils/size_config.dart';
|
||||
import 'widgets/widgets.dart';
|
||||
import '../../view_models/login_view_model.dart';
|
||||
|
||||
class LoginDesktopScreen extends StatefulWidget {
|
||||
const LoginDesktopScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginDesktopScreen> createState() => _LoginDesktopScreenState();
|
||||
}
|
||||
|
||||
class _LoginDesktopScreenState extends State<LoginDesktopScreen> {
|
||||
late final LoginViewModel _viewModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_viewModel = LoginViewModel();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_viewModel.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: SizedBox(
|
||||
width: 120.0.w(),
|
||||
child: ListenableBuilder(
|
||||
listenable: _viewModel,
|
||||
builder: (context, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LoginLogo(width: 190.0.w(), height: 88.0.h()),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _viewModel.usernameController,
|
||||
focusNode: _viewModel.usernameFocus,
|
||||
label: AppStrings.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
fieldHeight: 60.0.h(),
|
||||
borderRedus: 4.0.w(),
|
||||
prefixIconPadding: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _viewModel.passwordController,
|
||||
focusNode: _viewModel.passwordFocus,
|
||||
label: AppStrings.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
prefixIconPadding: 4.0.w(),
|
||||
isPassword: true,
|
||||
obscureText: _viewModel.obscurePassword,
|
||||
fieldHeight: 60.0.h(),
|
||||
onToggleVisibility: _viewModel.togglePasswordVisibility,
|
||||
borderRedus: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
BaseButton(
|
||||
isEnabled: _viewModel.isLoginEnabled,
|
||||
onPressed: _viewModel.isLoginEnabled
|
||||
? () => HomeRouteData().go(context)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// lib/views/login_screen.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
|
||||
import '../../core/constants/assets.dart';
|
||||
import '../../core/constants/strings.dart';
|
||||
import '../../core/utils/size_config.dart';
|
||||
import 'widgets/widgets.dart';
|
||||
import '../../view_models/login_view_model.dart';
|
||||
|
||||
class LoginMobileScreen extends StatefulWidget {
|
||||
const LoginMobileScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginMobileScreen> createState() => _LoginMobileScreenState();
|
||||
}
|
||||
|
||||
class _LoginMobileScreenState extends State<LoginMobileScreen> {
|
||||
late final LoginViewModel _viewModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_viewModel = LoginViewModel();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_viewModel.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: ListenableBuilder(
|
||||
listenable: _viewModel,
|
||||
builder: (context, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LoginLogo(width: 190.0.w(), height: 88.0.h()),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _viewModel.usernameController,
|
||||
focusNode: _viewModel.usernameFocus,
|
||||
label: AppStrings.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _viewModel.passwordController,
|
||||
focusNode: _viewModel.passwordFocus,
|
||||
label: AppStrings.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
isPassword: true,
|
||||
obscureText: _viewModel.obscurePassword,
|
||||
onToggleVisibility: _viewModel.togglePasswordVisibility,
|
||||
),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
BaseButton(
|
||||
isEnabled: _viewModel.isLoginEnabled,
|
||||
onPressed: _viewModel.isLoginEnabled
|
||||
? () {
|
||||
HomeRouteData().go(context);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/utils/display_size_detector.dart';
|
||||
import 'package:tm_app/views/login/login_desktop_screen.dart';
|
||||
import 'package:tm_app/views/login/login_mobile_screen.dart';
|
||||
import 'package:tm_app/views/login/login_tablet_screen.dart';
|
||||
|
||||
class LoginScreen extends StatelessWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final displaySize = MediaQuery.of(context).size.displaySize;
|
||||
|
||||
switch (displaySize) {
|
||||
case DisplaySize.large:
|
||||
case DisplaySize.medium:
|
||||
return const LoginDesktopScreen();
|
||||
case DisplaySize.small:
|
||||
return const LoginTabletScreen();
|
||||
case DisplaySize.extraSmall:
|
||||
return const LoginMobileScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// lib/views/login_tablet_screen.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
|
||||
import '../../core/constants/assets.dart';
|
||||
import '../../core/constants/strings.dart';
|
||||
import '../../core/utils/size_config.dart';
|
||||
import 'widgets/widgets.dart';
|
||||
import '../../view_models/login_view_model.dart';
|
||||
|
||||
class LoginTabletScreen extends StatefulWidget {
|
||||
const LoginTabletScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginTabletScreen> createState() => _LoginTabletScreenState();
|
||||
}
|
||||
|
||||
class _LoginTabletScreenState extends State<LoginTabletScreen> {
|
||||
late final LoginViewModel _viewModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_viewModel = LoginViewModel();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_viewModel.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: SizedBox(
|
||||
width: 120.0.w(),
|
||||
child: ListenableBuilder(
|
||||
listenable: _viewModel,
|
||||
builder: (context, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LoginLogo(width: 190.0.w(), height: 88.0.h()),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _viewModel.usernameController,
|
||||
focusNode: _viewModel.usernameFocus,
|
||||
label: AppStrings.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
fieldHeight: 60.0.h(),
|
||||
borderRedus: 4.0.w(),
|
||||
prefixIconPadding: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: _viewModel.passwordController,
|
||||
focusNode: _viewModel.passwordFocus,
|
||||
label: AppStrings.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
prefixIconPadding: 4.0.w(),
|
||||
isPassword: true,
|
||||
obscureText: _viewModel.obscurePassword,
|
||||
fieldHeight: 60.0.h(),
|
||||
onToggleVisibility: _viewModel.togglePasswordVisibility,
|
||||
borderRedus: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
BaseButton(
|
||||
isEnabled: _viewModel.isLoginEnabled,
|
||||
onPressed: _viewModel.isLoginEnabled
|
||||
? () => HomeRouteData().go(context)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
|
||||
import '../../../core/constants/assets.dart';
|
||||
import '../../../core/constants/strings.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../../../view_models/auth_view_model.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
|
||||
class LoginDesktopPage extends StatelessWidget {
|
||||
const LoginDesktopPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: SizedBox(
|
||||
width: 526,
|
||||
child: Consumer<AuthViewModel>(
|
||||
builder: (context, viewModel, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LoginLogo(width: 190.0.w(), height: 88.0.h()),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: viewModel.usernameController,
|
||||
focusNode: viewModel.usernameFocus,
|
||||
label: AppStrings.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
fieldHeight: 60.0.h(),
|
||||
borderRedus: 4.0.w(),
|
||||
prefixIconPadding: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: viewModel.passwordController,
|
||||
focusNode: viewModel.passwordFocus,
|
||||
label: AppStrings.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
prefixIconPadding: 4.0.w(),
|
||||
isPassword: true,
|
||||
obscureText: viewModel.obscurePassword,
|
||||
fieldHeight: 60.0.h(),
|
||||
onToggleVisibility: viewModel.togglePasswordVisibility,
|
||||
borderRedus: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
if (viewModel.isLoading)
|
||||
const CircularProgressIndicator()
|
||||
else
|
||||
BaseButton(
|
||||
isEnabled: viewModel.isLoginEnabled,
|
||||
onPressed:
|
||||
viewModel.isLoginEnabled
|
||||
? () async {
|
||||
await viewModel.login();
|
||||
if (viewModel.loggedInUser != null) {
|
||||
HomeRouteData().go(context);
|
||||
} else if (viewModel.errorMessage != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(viewModel.errorMessage!),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
|
||||
import '../../../core/constants/assets.dart';
|
||||
import '../../../core/constants/strings.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../../../view_models/auth_view_model.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
|
||||
class LoginMobilePage extends StatelessWidget {
|
||||
const LoginMobilePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: Consumer<AuthViewModel>(
|
||||
builder: (context, viewModel, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LoginLogo(width: 190.0.w(), height: 88.0.h()),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: viewModel.usernameController,
|
||||
focusNode: viewModel.usernameFocus,
|
||||
label: AppStrings.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: viewModel.passwordController,
|
||||
focusNode: viewModel.passwordFocus,
|
||||
label: AppStrings.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
isPassword: true,
|
||||
obscureText: viewModel.obscurePassword,
|
||||
onToggleVisibility: viewModel.togglePasswordVisibility,
|
||||
),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
if (viewModel.isLoading)
|
||||
const CircularProgressIndicator()
|
||||
else
|
||||
BaseButton(
|
||||
isEnabled: viewModel.isLoginEnabled,
|
||||
onPressed:
|
||||
viewModel.isLoginEnabled
|
||||
? () async {
|
||||
await viewModel.login();
|
||||
if (viewModel.loggedInUser != null) {
|
||||
HomeRouteData().go(context);
|
||||
} else if (viewModel.errorMessage != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(viewModel.errorMessage!),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/login/pages/login_desktop_page.dart';
|
||||
import 'package:tm_app/views/login/pages/login_mobile_page.dart';
|
||||
import 'package:tm_app/views/login/pages/login_tablet_page.dart';
|
||||
import 'package:tm_app/views/shared/responsive_builder.dart';
|
||||
|
||||
class LoginScreen extends StatelessWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return ResponsiveBuilder(
|
||||
mobile: LoginMobilePage(),
|
||||
tablet: LoginTabletPage(),
|
||||
desktop: LoginDesktopPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
|
||||
import '../../../core/constants/assets.dart';
|
||||
import '../../../core/constants/strings.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
import '../../../view_models/auth_view_model.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
|
||||
class LoginTabletPage extends StatelessWidget {
|
||||
const LoginTabletPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
|
||||
child: SizedBox(
|
||||
width: 526,
|
||||
child: Consumer<AuthViewModel>(
|
||||
builder: (context, viewModel, _) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LoginLogo(width: 190.0.w(), height: 88.0.h()),
|
||||
SizedBox(height: 32.0.h()),
|
||||
const LoginTitle(),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: viewModel.usernameController,
|
||||
focusNode: viewModel.usernameFocus,
|
||||
label: AppStrings.usernameLabel,
|
||||
iconPath: AssetsManager.userIcon,
|
||||
fieldHeight: 60.0.h(),
|
||||
borderRedus: 4.0.w(),
|
||||
prefixIconPadding: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 16.0.h()),
|
||||
|
||||
LoginTextField(
|
||||
controller: viewModel.passwordController,
|
||||
focusNode: viewModel.passwordFocus,
|
||||
label: AppStrings.passwordLabel,
|
||||
iconPath: AssetsManager.passwordIcon,
|
||||
prefixIconPadding: 4.0.w(),
|
||||
isPassword: true,
|
||||
obscureText: viewModel.obscurePassword,
|
||||
fieldHeight: 60.0.h(),
|
||||
onToggleVisibility: viewModel.togglePasswordVisibility,
|
||||
borderRedus: 4.0.w(),
|
||||
),
|
||||
SizedBox(height: 32.0.h()),
|
||||
|
||||
if (viewModel.isLoading)
|
||||
const CircularProgressIndicator()
|
||||
else
|
||||
BaseButton(
|
||||
isEnabled: viewModel.isLoginEnabled,
|
||||
onPressed:
|
||||
viewModel.isLoginEnabled
|
||||
? () async {
|
||||
await viewModel.login();
|
||||
if (viewModel.loggedInUser != null) {
|
||||
HomeRouteData().go(context);
|
||||
} else if (viewModel.errorMessage != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(viewModel.errorMessage!),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ class BaseButton extends StatelessWidget {
|
||||
final double? borderWidth;
|
||||
final double? borderRadius;
|
||||
final double? elevation;
|
||||
final double? width;
|
||||
|
||||
const BaseButton({
|
||||
required this.isEnabled,
|
||||
@@ -25,21 +26,22 @@ class BaseButton extends StatelessWidget {
|
||||
this.borderWidth,
|
||||
this.borderRadius,
|
||||
this.elevation,
|
||||
this.width,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
width: width ?? double.infinity,
|
||||
height: 55.0.h(),
|
||||
child: ElevatedButton(
|
||||
onPressed: isEnabled ? onPressed : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
backgroundColor ??
|
||||
(isEnabled ? AppColors.mainBlue : Colors.grey[300]),
|
||||
foregroundColor: textColor ?? Colors.white,
|
||||
(isEnabled ? AppColors.mainBlue : AppColors.backgroundColor),
|
||||
foregroundColor: textColor ?? AppColors.backgroundColor,
|
||||
elevation: elevation ?? 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 28.0.w()),
|
||||
@@ -54,7 +56,7 @@ class BaseButton extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: textColor ?? Colors.white,
|
||||
color: textColor ?? AppColors.backgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -52,7 +52,7 @@ class LoginTextField extends StatelessWidget {
|
||||
width: 24.0.w(),
|
||||
height: 24.0.h(),
|
||||
colorFilter: ColorFilter.mode(
|
||||
focusNode.hasFocus ? Colors.black87 : Colors.grey,
|
||||
focusNode.hasFocus ? AppColors.grey80 : AppColors.grey60,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
@@ -68,7 +68,7 @@ class LoginTextField extends StatelessWidget {
|
||||
width: 24.0.w(),
|
||||
height: 24.0.h(),
|
||||
colorFilter: ColorFilter.mode(
|
||||
focusNode.hasFocus ? Colors.black87 : Colors.grey,
|
||||
focusNode.hasFocus ? AppColors.grey80 : AppColors.grey60,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
|
||||
import '../../../core/constants/strings.dart';
|
||||
import '../../../core/utils/size_config.dart';
|
||||
@@ -20,7 +21,7 @@ class LoginTitle extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
color: AppColors.grey60,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
+27
-48
@@ -29,10 +29,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
version: "2.12.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -101,10 +101,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: "0b1b12a0a549605e5f04476031cd0bc91ead1d7c8e830773a18ee54179b3cb62"
|
||||
sha256: ba95c961bafcd8686d1cf63be864eb59447e795e124d98d6a27d91fcd13602fb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.11.0"
|
||||
version: "8.11.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -177,30 +177,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
device_frame:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_frame
|
||||
sha256: "7b2ebb2a09d6cc0f086b51bd1412d7be83e0170056a7290349169be41164c86a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
device_preview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_preview
|
||||
sha256: "88aa1cc73ee9a8ec771b309dcbc4000cc66b5d8456b825980997640ab1195bf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
|
||||
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.8.0+1"
|
||||
version: "5.9.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -213,10 +197,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
version: "1.3.2"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -254,11 +238,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -337,10 +316,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.5.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -357,14 +336,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -382,21 +353,29 @@ packages:
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.9.5"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "10.0.8"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -593,10 +572,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
||||
sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.10"
|
||||
version: "2.4.11"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -782,10 +761,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
version: "14.3.1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -843,5 +822,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.8.0 <4.0.0"
|
||||
flutter: ">=3.32.0"
|
||||
dart: ">=3.7.2 <4.0.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
||||
+5
-1
@@ -20,7 +20,7 @@ dependencies:
|
||||
logger: ^2.6.0
|
||||
dio: ^5.8.0+1
|
||||
shared_preferences: ^2.5.3
|
||||
|
||||
json_annotation: ^4.8.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -29,7 +29,11 @@ dev_dependencies:
|
||||
flutter_gen_runner:
|
||||
build_runner: ^2.5.4
|
||||
go_router_builder: ^3.0.0
|
||||
<<<<<<< HEAD
|
||||
json_serializable: ^6.9.5
|
||||
=======
|
||||
json_serializable: ^6.7.1
|
||||
>>>>>>> origin/refactor_dark_mood_repository
|
||||
|
||||
|
||||
flutter:
|
||||
|
||||
Reference in New Issue
Block a user