diff --git a/analysis_options.yaml b/analysis_options.yaml
index a4ccf49..650dc2d 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -62,6 +62,7 @@ linter:
prefer_iterable_whereType: true
prefer_null_aware_operators: true
prefer_spread_collections: true
+ prefer_const_constructors: true
recursive_getters: true
type_init_formals: true
unnecessary_constructor_name: true
diff --git a/assets/icons/arrow-down.svg b/assets/icons/arrow-down.svg
new file mode 100644
index 0000000..64a3e86
--- /dev/null
+++ b/assets/icons/arrow-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/arrow-up.svg b/assets/icons/arrow-up.svg
new file mode 100644
index 0000000..f080b70
--- /dev/null
+++ b/assets/icons/arrow-up.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/clipboard-text.svg b/assets/icons/clipboard-text.svg
new file mode 100644
index 0000000..7ee1569
--- /dev/null
+++ b/assets/icons/clipboard-text.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/danger-fill.svg b/assets/icons/danger-fill.svg
new file mode 100644
index 0000000..15c578f
--- /dev/null
+++ b/assets/icons/danger-fill.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/upload_file.svg b/assets/icons/upload_file.svg
new file mode 100644
index 0000000..a414ea0
--- /dev/null
+++ b/assets/icons/upload_file.svg
@@ -0,0 +1,6 @@
+
diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart
index 5c3bdfb..ec4f903 100644
--- a/lib/core/config/dependencies.dart
+++ b/lib/core/config/dependencies.dart
@@ -11,6 +11,7 @@ import 'package:tm_app/data/services/home_service.dart';
import 'package:tm_app/data/services/liked_tenders_service.dart';
import 'package:tm_app/data/services/tender_detail_service.dart';
import 'package:tm_app/data/services/your_tenders_service.dart';
+import 'package:tm_app/view_models/final_completion_of_documents_view_model.dart';
import 'package:tm_app/view_models/liked_tenders_view_model.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/view_models/tenders_view_model.dart';
@@ -142,6 +143,9 @@ List get viewModels {
authViewModel: context.read(),
),
),
+ ChangeNotifierProvider(
+ create: (context) => FinalCompletionOfDocumentsViewModel(),
+ ),
];
}
diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart
index 723fbe1..e0088c3 100644
--- a/lib/core/constants/assets.dart
+++ b/lib/core/constants/assets.dart
@@ -49,6 +49,7 @@ class AssetsManager {
static const arrowRight = 'assets/icons/arrow-right.svg';
static const notification = 'assets/icons/notification.svg';
static const danger = 'assets/icons/danger.svg';
+
static const calendar = 'assets/icons/calendar.svg';
static const menu = 'assets/icons/menu.svg';
static const thumbLike = 'assets/icons/thumb_like.svg';
@@ -67,5 +68,13 @@ class AssetsManager {
//Liked tenders
static const trash = 'assets/icons/trash.svg';
static const arrowDownSmall = 'assets/icons/arrow_down_small.svg';
+
static const filter = 'assets/icons/filter.svg';
+
+ //final completion of documents
+ static const clipboardText = 'assets/icons/clipboard-text.svg';
+ static const dangerFill = 'assets/icons/danger-fill.svg';
+ static const arrowDown = 'assets/icons/arrow-down.svg';
+ static const arrowUp = 'assets/icons/arrow-up.svg';
+ static const uploadFile = 'assets/icons/upload_file.svg';
}
diff --git a/lib/core/routes/app_routes.dart b/lib/core/routes/app_routes.dart
index fd5edb0..9089cfa 100644
--- a/lib/core/routes/app_routes.dart
+++ b/lib/core/routes/app_routes.dart
@@ -6,6 +6,7 @@ import 'package:tm_app/views/home/pages/home_screen.dart';
import 'package:tm_app/views/liked_tenders/pages/liked_tenders_screen.dart';
import 'package:tm_app/views/login/pages/login_screen.dart';
+import '../../views/final_completion_of_documents/pages/final_completion_of_documents_screen.dart';
import '../../views/forgot_password/pages/forgot_password_screen.dart';
import '../../views/notification/pages/notification_screen.dart';
import '../../views/profile/pages/profile_screen.dart';
@@ -169,7 +170,20 @@ class YourTendersRouteData extends GoRouteData with _$YourTendersRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
- return YourTendersScreen();
+ return const YourTendersScreen();
+ }
+}
+
+@TypedGoRoute(
+ path: '/final_completion_of_documents',
+)
+class FinalCompletionOfDocumentsRouteData extends GoRouteData
+ with _$FinalCompletionOfDocumentsRouteData {
+ const FinalCompletionOfDocumentsRouteData();
+
+ @override
+ Widget build(BuildContext context, GoRouterState state) {
+ return const FinalCompletionOfDocumentsScreen();
}
}
@@ -179,7 +193,7 @@ class LikedTendersRouteData extends GoRouteData with _$LikedTendersRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
- return LikedTendersScreen();
+ return const LikedTendersScreen();
}
}
@@ -211,6 +225,6 @@ class ForgotPasswordRouteData extends GoRouteData
@override
Widget build(BuildContext context, GoRouterState state) {
- return ForgotPasswordScreen();
+ return const ForgotPasswordScreen();
}
}
diff --git a/lib/core/routes/app_routes.g.dart b/lib/core/routes/app_routes.g.dart
index 778d11f..8511a54 100644
--- a/lib/core/routes/app_routes.g.dart
+++ b/lib/core/routes/app_routes.g.dart
@@ -10,6 +10,7 @@ List get $appRoutes => [
$loginScreenRoute,
$appShellRouteData,
$yourTendersRouteData,
+ $finalCompletionOfDocumentsRouteData,
$likedTendersRouteData,
$completionOfDocumentsMobileRouteData,
$tenderDetailRouteData,
@@ -202,6 +203,34 @@ mixin _$YourTendersRouteData on GoRouteData {
void replace(BuildContext context) => context.replace(location);
}
+RouteBase get $finalCompletionOfDocumentsRouteData => GoRouteData.$route(
+ path: '/final_completion_of_documents',
+
+ factory: _$FinalCompletionOfDocumentsRouteData._fromState,
+);
+
+mixin _$FinalCompletionOfDocumentsRouteData on GoRouteData {
+ static FinalCompletionOfDocumentsRouteData _fromState(GoRouterState state) =>
+ const FinalCompletionOfDocumentsRouteData();
+
+ @override
+ String get location =>
+ GoRouteData.$location('/final_completion_of_documents');
+
+ @override
+ void go(BuildContext context) => context.go(location);
+
+ @override
+ Future push(BuildContext context) => context.push(location);
+
+ @override
+ void pushReplacement(BuildContext context) =>
+ context.pushReplacement(location);
+
+ @override
+ void replace(BuildContext context) => context.replace(location);
+}
+
RouteBase get $likedTendersRouteData => GoRouteData.$route(
path: '/like-tenders',
diff --git a/lib/core/theme/colors.dart b/lib/core/theme/colors.dart
index 615afb1..77265b4 100644
--- a/lib/core/theme/colors.dart
+++ b/lib/core/theme/colors.dart
@@ -168,4 +168,10 @@ class AppColors {
static const Color lightBlue = Color(0xFFe8ecfc);
static const Color cyanAqua = Color(0xFFd6f4f8);
static const Color cyanTeal = Color(0xFF24A6B4);
+ static const Color yellow1 = Color(0xFFFFF7EE);
+ static const Color yellow2 = Color(0xFFFFE4C9);
+ static const Color yellow3 = Color(0xFFF8EAD6);
+ static const Color yellow4 = Color(0xFFC07A25);
+ static const Color secondaryborder = Color(0xFFCDCDCD);
+ static const Color secondaryborder2 = Color(0xFFDADADA);
}
diff --git a/lib/view_models/final_completion_of_documents_view_model.dart b/lib/view_models/final_completion_of_documents_view_model.dart
new file mode 100644
index 0000000..1b7608f
--- /dev/null
+++ b/lib/view_models/final_completion_of_documents_view_model.dart
@@ -0,0 +1,16 @@
+import 'dart:io';
+
+import 'package:file_picker/file_picker.dart';
+import 'package:flutter/material.dart';
+
+class FinalCompletionOfDocumentsViewModel extends ChangeNotifier {
+ final FilePicker _filePicker = FilePicker.platform;
+ File? _selectedFile;
+ File? get selectedFile => _selectedFile;
+
+ Future pickFile() async {
+ final result = await _filePicker.pickFiles();
+ _selectedFile = File(result?.files.single.path ?? '');
+ notifyListeners();
+ }
+}
diff --git a/lib/views/final_completion_of_documents/pages/final_completion_of_documents_screen.dart b/lib/views/final_completion_of_documents/pages/final_completion_of_documents_screen.dart
new file mode 100644
index 0000000..8256cd2
--- /dev/null
+++ b/lib/views/final_completion_of_documents/pages/final_completion_of_documents_screen.dart
@@ -0,0 +1,330 @@
+import 'package:dotted_border/dotted_border.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:go_router/go_router.dart';
+import 'package:provider/provider.dart';
+import 'package:tm_app/core/constants/assets.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+import 'package:tm_app/view_models/final_completion_of_documents_view_model.dart';
+import 'package:tm_app/views/final_completion_of_documents/strings/final_completion_of_documents_strings.dart';
+import 'package:tm_app/views/final_completion_of_documents/widgets/comment_box.dart';
+import 'package:tm_app/views/login/widgets/widgets.dart';
+import 'package:tm_app/views/shared/desktop_navigation_widget.dart';
+
+import '../../shared/main_drop_down.dart';
+
+class FinalCompletionOfDocumentsScreen extends StatelessWidget {
+ const FinalCompletionOfDocumentsScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ SizeConfig.init(context);
+ return Scaffold(
+ body: Consumer(
+ builder:
+ (context, viewModel, child) => Column(
+ children: [
+ const DesktopNavigationWidget(currentIndex: 1),
+ const SizedBox(height: 64.0),
+ Expanded(
+ child: SizedBox(
+ width: 740,
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ Row(
+ children: [
+ InkWell(
+ borderRadius: BorderRadius.circular(12),
+ onTap: () => context.pop(),
+ child: SvgPicture.asset(
+ AssetsManager.arrowLeft,
+ ),
+ ),
+ const SizedBox(width: 8.0),
+ Text(
+ FinalCompletionOfDocumentsStrings
+ .completionOfDocuments,
+ style: TextStyle(
+ fontSize: 20.0,
+ fontWeight: FontWeight.bold,
+ color: AppColors.grey70,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 32.0),
+ const CommentBox(),
+ const SizedBox(height: 32.0),
+ FormCard(viewModel: viewModel),
+ const SizedBox(height: 40.0),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ BaseButton(
+ isEnabled: true,
+ onPressed: () {},
+ width: 176,
+ backgroundColor: AppColors.primary10,
+ textColor: AppColors.mainBlue,
+ text: FinalCompletionOfDocumentsStrings.back,
+ ),
+ const SizedBox(width: 16.0),
+ BaseButton(
+ isEnabled: true,
+ onPressed: () {},
+ width: 176,
+ text: FinalCompletionOfDocumentsStrings.next,
+ ),
+ ],
+ ),
+ const SizedBox(height: 35.0),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class FormCard extends StatelessWidget {
+ const FormCard({required this.viewModel, super.key});
+ final FinalCompletionOfDocumentsViewModel viewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: AppColors.borderColor),
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ width: double.infinity,
+ height: 64.0,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: AppColors.borderColor),
+ ),
+ alignment: AlignmentDirectional.centerStart,
+ child: Container(
+ width: 118,
+ height: 32,
+ decoration: BoxDecoration(
+ color: AppColors.yellow3,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ alignment: Alignment.center,
+ child: const Text(
+ '50% Completed',
+ style: TextStyle(color: AppColors.yellow4),
+ ),
+ ),
+ ),
+ const SizedBox(height: 48.0),
+ Text(
+ FinalCompletionOfDocumentsStrings.numberOfServersListedTitle,
+ style: TextStyle(
+ fontSize: 16.0,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ const SizedBox(height: 12.0),
+ Text(
+ '12 servers across 2 data centers',
+ style: TextStyle(
+ fontSize: 16.0,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey80,
+ ),
+ ),
+ const SizedBox(height: 24),
+ Divider(color: AppColors.borderColor),
+ const SizedBox(height: 24),
+ Text(
+ FinalCompletionOfDocumentsStrings
+ .technicalTeamOrganizationChartUploadedTitle,
+ style: TextStyle(
+ fontSize: 16.0,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ const SizedBox(height: 12.0),
+ Text(
+ 'PDF submitted with full team roles and structure',
+ style: TextStyle(
+ fontSize: 16.0,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey80,
+ ),
+ ),
+ const SizedBox(height: 24),
+ Divider(color: AppColors.borderColor),
+ const SizedBox(height: 48),
+ _titleDropDownRow(
+ text:
+ FinalCompletionOfDocumentsStrings
+ .describeInfrastructureCapabilitiestitle,
+ ),
+ const SizedBox(height: 16),
+ _uploadFileCard(viewModel: viewModel),
+ const SizedBox(height: 40),
+ _titleDropDownRow(
+ text:
+ FinalCompletionOfDocumentsStrings
+ .uploadTechnicalTeamOrgChartTitle,
+ ),
+ const SizedBox(height: 16),
+ _describeYourTeamTextField(),
+ const SizedBox(height: 48),
+ _titleDropDownRow(
+ text:
+ FinalCompletionOfDocumentsStrings
+ .noCertificationInISO27001Title,
+ ),
+ const SizedBox(height: 16),
+ _uploadFileCard(viewModel: viewModel),
+ const SizedBox(height: 48),
+ _titleDropDownRow(
+ text:
+ FinalCompletionOfDocumentsStrings
+ .uploadCompletionCertificateTitle,
+ ),
+ const SizedBox(height: 16),
+ _uploadFileCard(viewModel: viewModel),
+ ],
+ ),
+ );
+ }
+
+ Widget _describeYourTeamTextField() {
+ return TextFormField(
+ maxLines: 5,
+ decoration: InputDecoration(
+ filled: true,
+ fillColor: AppColors.grey0,
+ hint: Text(
+ FinalCompletionOfDocumentsStrings.describeYourTeamHint,
+ style: TextStyle(
+ fontSize: 12.0,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ border: OutlineInputBorder(
+ borderSide: const BorderSide(color: AppColors.secondaryborder2),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ enabledBorder: OutlineInputBorder(
+ borderSide: const BorderSide(color: AppColors.secondaryborder2),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderSide: const BorderSide(color: AppColors.secondaryborder2),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ );
+ }
+
+ Widget _uploadFileCard({
+ required FinalCompletionOfDocumentsViewModel viewModel,
+ }) {
+ return InkWell(
+ onTap: () => viewModel.pickFile(),
+ child: Container(
+ width: double.infinity,
+ height: 140,
+ decoration: BoxDecoration(color: AppColors.grey0),
+ child: DottedBorder(
+ options: const RoundedRectDottedBorderOptions(
+ radius: Radius.circular(12),
+ dashPattern: [4, 4],
+ strokeWidth: 1,
+ color: AppColors.secondaryborder,
+ padding: EdgeInsets.all(16),
+ ),
+
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SvgPicture.asset(AssetsManager.uploadFile),
+ const SizedBox(height: 12),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Text(
+ FinalCompletionOfDocumentsStrings.chooseFile,
+ style: TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ color: AppColors.mainBlue,
+ ),
+ ),
+ const SizedBox(width: 4),
+ Text(
+ FinalCompletionOfDocumentsStrings.toUpload,
+ style: TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ color: AppColors.grey60,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+ Text(
+ FinalCompletionOfDocumentsStrings.selectZipOrEtcTitle,
+ style: TextStyle(
+ fontSize: 12,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _titleDropDownRow({required String text}) {
+ return Row(
+ children: [
+ SvgPicture.asset(AssetsManager.dangerFill),
+ const SizedBox(width: 8),
+ Text(
+ text,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ const Spacer(),
+ MainDropDown(
+ text: FinalCompletionOfDocumentsStrings.statusTitle,
+ items: const [
+ FinalCompletionOfDocumentsStrings.statusWaitingForOthers,
+ FinalCompletionOfDocumentsStrings.statusToDo,
+ FinalCompletionOfDocumentsStrings.statusDone,
+ ],
+ onSelect: (String item) {},
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/views/final_completion_of_documents/strings/final_completion_of_documents_strings.dart b/lib/views/final_completion_of_documents/strings/final_completion_of_documents_strings.dart
new file mode 100644
index 0000000..8e9d150
--- /dev/null
+++ b/lib/views/final_completion_of_documents/strings/final_completion_of_documents_strings.dart
@@ -0,0 +1,29 @@
+class FinalCompletionOfDocumentsStrings {
+ FinalCompletionOfDocumentsStrings._();
+ static const String completionOfDocuments = 'Completion of documents';
+ static const String commentNoteTitle = 'Comment / note';
+ static const String replyToComment = 'reply to comment';
+ static const String deadlineForCompletionOfDocuments =
+ 'Deadline for Completion of documents:';
+ static const String numberOfServersListedTitle = 'Number of servers listed';
+ static const String technicalTeamOrganizationChartUploadedTitle =
+ 'Technical team organization chart uploaded';
+ static const String describeInfrastructureCapabilitiestitle =
+ 'Describe infrastructure capabilities';
+ static const String statusTitle = 'Status';
+ static const String statusWaitingForOthers = 'Waiting for others';
+ static const String statusToDo = 'To do';
+ static const String statusDone = 'Done';
+ static const String chooseFile = 'Choose file';
+ static const String toUpload = 'To upload';
+ static const String selectZipOrEtcTitle = 'Select zip,image,pdf or ms.word';
+ static const String uploadTechnicalTeamOrgChartTitle =
+ 'Upload technical team org chart';
+ static const String describeYourTeamHint = 'Describe your team';
+ static const String noCertificationInISO27001Title =
+ 'No certification in ISO 27001';
+ static const String uploadCompletionCertificateTitle =
+ 'Upload completion certificate';
+ static const String back = 'Back';
+ static const String next = 'Next';
+}
diff --git a/lib/views/final_completion_of_documents/widgets/comment_box.dart b/lib/views/final_completion_of_documents/widgets/comment_box.dart
new file mode 100644
index 0000000..8c4ef54
--- /dev/null
+++ b/lib/views/final_completion_of_documents/widgets/comment_box.dart
@@ -0,0 +1,117 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:tm_app/core/constants/assets.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/views/final_completion_of_documents/strings/final_completion_of_documents_strings.dart';
+
+class CommentBox extends StatelessWidget {
+ const CommentBox({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 148,
+ decoration: BoxDecoration(
+ color: AppColors.yellow1,
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: AppColors.yellow2),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.fromSTEB(40, 24.0, 0.0, 0),
+
+ child: Row(
+ children: [
+ SvgPicture.asset(AssetsManager.clipboardText),
+ const SizedBox(width: 16.0),
+ Text(
+ FinalCompletionOfDocumentsStrings.commentNoteTitle,
+ style: TextStyle(
+ fontSize: 16.0,
+ fontWeight: FontWeight.w500,
+ color: AppColors.grey80,
+ ),
+ ),
+ ],
+ ),
+ ),
+ const Spacer(),
+ const Padding(
+ padding: EdgeInsetsDirectional.only(start: 65, bottom: 10),
+ child: SizedBox(
+ width: 256,
+ height: 78,
+ child: Text(
+ 'Lorem ipsum is a dummy or placeholder text commonly used in graphic design, publishing, and web development.',
+ maxLines: 3,
+ style: TextStyle(
+ fontSize: 14.0,
+ overflow: TextOverflow.ellipsis,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.only(end: 14, top: 15),
+ child: Row(
+ children: [
+ Text(
+ FinalCompletionOfDocumentsStrings
+ .deadlineForCompletionOfDocuments,
+ style: TextStyle(
+ fontSize: 12.0,
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey80,
+ ),
+ ),
+ const SizedBox(width: 4),
+ Container(
+ width: 87,
+ height: 20,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(80),
+ color: AppColors.mainBlue,
+ ),
+ alignment: Alignment.center,
+ child: Text(
+ '2025-06-15',
+ style: TextStyle(
+ color: AppColors.white,
+ fontSize: 14.0,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 51),
+ TextButton(
+ onPressed: () {},
+ child: const Text(
+ FinalCompletionOfDocumentsStrings.replyToComment,
+ style: TextStyle(
+ fontSize: 14.0,
+ fontWeight: FontWeight.w500,
+ color: AppColors.mainBlue,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/views/shared/main_drop_down.dart b/lib/views/shared/main_drop_down.dart
new file mode 100644
index 0000000..cee7447
--- /dev/null
+++ b/lib/views/shared/main_drop_down.dart
@@ -0,0 +1,180 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+
+import '../../core/constants/assets.dart';
+
+class MainDropDown extends StatefulWidget {
+ const MainDropDown({
+ required this.onSelect,
+ required this.items,
+ required this.text,
+ super.key,
+ this.backgroundColor,
+ this.active = true,
+ });
+ final ValueChanged onSelect;
+ final List items;
+ final Color? backgroundColor;
+ final String text;
+ final bool active;
+
+ @override
+ State createState() => _MainDropDownState();
+}
+
+class _MainDropDownState extends State {
+ final LayerLink _layerLink = LayerLink();
+ OverlayEntry? _overlayEntry;
+ String? selectedItemText;
+
+ void _showPopupMenu() {
+ if (_overlayEntry == null) {
+ OverlayState? overlayState = Overlay.of(context);
+ _overlayEntry = _createOverlayEntry();
+ overlayState.insert(_overlayEntry!);
+ setState(() {});
+ } else {
+ _hidePopupMenu();
+ }
+ }
+
+ void _hidePopupMenu() {
+ setState(() {
+ _overlayEntry?.remove();
+ _overlayEntry = null;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return _body();
+ }
+
+ Widget _body() {
+ return CompositedTransformTarget(
+ link: _layerLink,
+ child: GestureDetector(
+ onTap: () {
+ if (widget.active) {
+ _showPopupMenu();
+ } else {
+ return;
+ }
+ },
+ child: Container(
+ width: 168.0.w(),
+ height: 40.0.h(),
+ decoration: BoxDecoration(
+ color: widget.backgroundColor ?? Colors.transparent,
+ borderRadius: BorderRadius.circular(12.0),
+ border: Border.all(color: AppColors.secondaryborder),
+ ),
+ child: Row(
+ children: [
+ SizedBox(width: 10.0.w()),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SizedBox(
+ width: 120.0.w(),
+ child: Text(
+ selectedItemText ?? widget.text,
+ style: TextStyle(
+ color: AppColors.grey80,
+ fontSize: 16.0.sp(),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ ),
+ ],
+ ),
+ const Spacer(),
+
+ SvgPicture.asset(
+ _overlayEntry == null
+ ? AssetsManager.arrowDown
+ : AssetsManager.arrowUp,
+ ),
+ SizedBox(width: 12.0.w()),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ OverlayEntry _createOverlayEntry() {
+ return OverlayEntry(
+ builder:
+ (context) => Positioned(
+ width: 168.0.w(),
+ child: CompositedTransformFollower(
+ link: _layerLink,
+ showWhenUnlinked: false,
+ offset: Offset(0, 48.0.h()), // Offset below the TextField
+ child: _notificationsMessages(),
+ ),
+ ),
+ );
+ }
+
+ Widget _notificationsMessages() {
+ return Material(
+ color: Colors.transparent,
+ child: Container(
+ padding: EdgeInsets.symmetric(vertical: 4.0.h()),
+ decoration: BoxDecoration(
+ color: AppColors.white,
+ borderRadius: BorderRadius.circular(12.0),
+ border: Border.all(color: AppColors.secondaryborder),
+ ),
+ child: Column(
+ children: [
+ ...widget.items.map((String choice) {
+ return GestureDetector(
+ onTap: () {
+ setState(() {
+ selectedItemText = choice;
+ widget.onSelect(choice);
+ });
+ _hidePopupMenu();
+ },
+ child: Column(
+ children: [
+ SizedBox(
+ width: double.maxFinite,
+ child: Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: 16.0.w(),
+ vertical: 8.0.h(),
+ ),
+ child: Text(
+ choice,
+ style: TextStyle(
+ fontSize: 16.0.sp(),
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey70,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ void dispose() {
+ _overlayEntry?.remove();
+ _overlayEntry = null;
+ super.dispose();
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index ef4ff3d..23e4a3c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -29,6 +29,8 @@ dependencies:
toastification: ^3.0.3
pinput: ^5.0.2
path_provider: ^2.1.5
+ dotted_border: ^3.1.0
+ file_picker: ^10.3.3
dev_dependencies: