Update iOS deployment target to 13.0, enhance Flutter integration, and improve tender submission flow

- Changed the iOS deployment target from 12.0 to 13.0 in Podfile and project settings.
- Updated AppDelegate to support implicit Flutter engine initialization.
- Modified Info.plist to include scene configuration for multiple scenes.
- Enhanced CompletionOfDocumentsRouteData to accept a tenderId parameter.
- Implemented tender submission functionality in TenderDetailViewModel and updated UI components to reflect loading states and success/error messages.
- Added new strings for submission success and error notifications.
This commit is contained in:
AmirReza Jamali
2026-05-30 16:48:05 +03:30
parent ae08b946f6
commit e6b4720dcd
19 changed files with 445 additions and 55 deletions
@@ -7,7 +7,8 @@ import '../../../core/utils/size_config.dart';
import '../../shared/responsive_builder.dart';
class CompletionOfDocumentsScreen extends StatefulWidget {
const CompletionOfDocumentsScreen({super.key});
final String? tenderId;
const CompletionOfDocumentsScreen({this.tenderId, super.key});
@override
State<CompletionOfDocumentsScreen> createState() => _CompletionOfDocumentsScreenState();
@@ -41,10 +42,10 @@ class _CompletionOfDocumentsScreenState extends State<CompletionOfDocumentsScree
@override
Widget build(BuildContext context) {
SizeConfig.init(context);
return const ResponsiveBuilder(
mobile: CompletionOfDocumentsMobilePage(),
tablet: CompletionOfDocumentsTabletPage(),
desktop: CompletionOfDocumentsDesktopPage(),
return ResponsiveBuilder(
mobile: CompletionOfDocumentsMobilePage(tenderId: widget.tenderId),
tablet: CompletionOfDocumentsTabletPage(tenderId: widget.tenderId),
desktop: CompletionOfDocumentsDesktopPage(tenderId: widget.tenderId),
);
}
}
@@ -3,6 +3,10 @@ import 'package:flutter_svg/flutter_svg.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:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/utils/app_toast.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/completion_of_documents/strings/completion_of_documents_string.dart';
import 'package:tm_app/views/shared/base_button.dart';
import 'package:tm_app/views/shared/desktop_navigation_widget.dart';
@@ -10,7 +14,8 @@ import 'package:tm_app/views/shared/tablet_desktop_appbar.dart'
show TabletDesktopAppbar;
class CompletionOfDocumentsDesktopPage extends StatelessWidget {
const CompletionOfDocumentsDesktopPage({super.key});
final String? tenderId;
const CompletionOfDocumentsDesktopPage({this.tenderId, super.key});
@override
Widget build(BuildContext context) {
@@ -77,14 +82,17 @@ class CompletionOfDocumentsDesktopPage extends StatelessWidget {
SizedBox(height: 24.0.h()),
BaseButton(
isEnabled: true,
onPressed: () {},
text:
CompletionOfDocumentsStrings
.submitForCompletion,
textColor: AppColors.textBlue,
backgroundColor: AppColors.primary30,
Consumer<TenderDetailViewModel>(
builder: (context, viewModel, _) => BaseButton(
isEnabled: !viewModel.isSubmitApprovalLoading,
isLoading: viewModel.isSubmitApprovalLoading,
onPressed: () => _submitForCompletion(context),
text:
CompletionOfDocumentsStrings
.submitForCompletion,
textColor: AppColors.textBlue,
backgroundColor: AppColors.primary30,
),
),
SizedBox(height: 24.0.h()),
],
@@ -304,6 +312,26 @@ class CompletionOfDocumentsDesktopPage extends StatelessWidget {
);
}
Future<void> _submitForCompletion(BuildContext context) async {
final id = tenderId;
if (id == null || id.isEmpty) {
AppToast.error(context, CompletionOfDocumentsStrings.submitError);
return;
}
final viewModel = context.read<TenderDetailViewModel>();
final success = await viewModel.submitForCompletion(tenderId: id);
if (!context.mounted) return;
if (success) {
AppToast.success(context, CompletionOfDocumentsStrings.submitSuccess);
context.pop();
} else {
AppToast.error(
context,
viewModel.errorMessage ?? CompletionOfDocumentsStrings.submitError,
);
}
}
Widget _docCard({
required String title,
required String subtitle,
@@ -3,13 +3,18 @@ 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/core/utils/size_config.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/utils/app_toast.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/completion_of_documents/strings/completion_of_documents_string.dart';
import 'package:tm_app/views/shared/base_button.dart';
import '../../shared/tender_app_bar.dart';
class CompletionOfDocumentsMobilePage extends StatelessWidget {
const CompletionOfDocumentsMobilePage({super.key});
final String? tenderId;
const CompletionOfDocumentsMobilePage({this.tenderId, super.key});
@override
Widget build(BuildContext context) {
@@ -242,12 +247,15 @@ class CompletionOfDocumentsMobilePage extends StatelessWidget {
),
),
SizedBox(height: 24.0.h()),
BaseButton(
isEnabled: true,
onPressed: () {},
text: CompletionOfDocumentsStrings.submitForCompletion,
textColor: AppColors.textBlue,
backgroundColor: AppColors.primary30,
Consumer<TenderDetailViewModel>(
builder: (context, viewModel, _) => BaseButton(
isEnabled: !viewModel.isSubmitApprovalLoading,
isLoading: viewModel.isSubmitApprovalLoading,
onPressed: () => _submitForCompletion(context),
text: CompletionOfDocumentsStrings.submitForCompletion,
textColor: AppColors.textBlue,
backgroundColor: AppColors.primary30,
),
),
],
),
@@ -256,6 +264,26 @@ class CompletionOfDocumentsMobilePage extends StatelessWidget {
);
}
Future<void> _submitForCompletion(BuildContext context) async {
final id = tenderId;
if (id == null || id.isEmpty) {
AppToast.error(context, CompletionOfDocumentsStrings.submitError);
return;
}
final viewModel = context.read<TenderDetailViewModel>();
final success = await viewModel.submitForCompletion(tenderId: id);
if (!context.mounted) return;
if (success) {
AppToast.success(context, CompletionOfDocumentsStrings.submitSuccess);
context.pop();
} else {
AppToast.error(
context,
viewModel.errorMessage ?? CompletionOfDocumentsStrings.submitError,
);
}
}
Widget _docCard({
required String title,
required String subtitle,
@@ -3,6 +3,10 @@ import 'package:flutter_svg/flutter_svg.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:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/utils/app_toast.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/completion_of_documents/strings/completion_of_documents_string.dart';
import 'package:tm_app/views/shared/base_button.dart';
import 'package:tm_app/views/shared/tablet_desktop_appbar.dart';
@@ -10,7 +14,8 @@ import 'package:tm_app/views/shared/tablet_navigation_widget.dart';
import 'package:tm_app/views/shared/tender_app_bar.dart';
class CompletionOfDocumentsTabletPage extends StatelessWidget {
const CompletionOfDocumentsTabletPage({super.key});
final String? tenderId;
const CompletionOfDocumentsTabletPage({this.tenderId, super.key});
@override
Widget build(BuildContext context) {
@@ -83,14 +88,17 @@ class CompletionOfDocumentsTabletPage extends StatelessWidget {
SizedBox(height: 24.0.h()),
BaseButton(
isEnabled: true,
onPressed: () {},
text:
CompletionOfDocumentsStrings
.submitForCompletion,
textColor: AppColors.textBlue,
backgroundColor: AppColors.primary30,
Consumer<TenderDetailViewModel>(
builder: (context, viewModel, _) => BaseButton(
isEnabled: !viewModel.isSubmitApprovalLoading,
isLoading: viewModel.isSubmitApprovalLoading,
onPressed: () => _submitForCompletion(context),
text:
CompletionOfDocumentsStrings
.submitForCompletion,
textColor: AppColors.textBlue,
backgroundColor: AppColors.primary30,
),
),
],
),
@@ -309,6 +317,26 @@ class CompletionOfDocumentsTabletPage extends StatelessWidget {
);
}
Future<void> _submitForCompletion(BuildContext context) async {
final id = tenderId;
if (id == null || id.isEmpty) {
AppToast.error(context, CompletionOfDocumentsStrings.submitError);
return;
}
final viewModel = context.read<TenderDetailViewModel>();
final success = await viewModel.submitForCompletion(tenderId: id);
if (!context.mounted) return;
if (success) {
AppToast.success(context, CompletionOfDocumentsStrings.submitSuccess);
context.pop();
} else {
AppToast.error(
context,
viewModel.errorMessage ?? CompletionOfDocumentsStrings.submitError,
);
}
}
// -------------------- Document Card --------------------
Widget _docCard({
required String title,
@@ -7,4 +7,6 @@ class CompletionOfDocumentsStrings {
'Set Deadline for Completion of documents :';
static const String note = 'Note:';
static const String submitForCompletion = 'Submit for completion';
static const String submitSuccess = 'Tender submitted for completion';
static const String submitError = 'Failed to submit tender for completion';
}
@@ -75,9 +75,9 @@ class _TenderDetailActionsState extends State<TenderDetailActions> {
// submissionMode: value,
// );
const CompletionOfDocumentsRouteData().push(
context,
);
CompletionOfDocumentsRouteData(
tenderId: widget.detail.id!,
).push(context);
} else if (value ==
TenderSubmissionMode.partnership.value) {
Future.delayed(Duration.zero, () {
@@ -213,8 +213,9 @@ class _TenderDetailActionsState extends State<TenderDetailActions> {
const FinalCompletionOfDocumentsRouteData()
.push(context);
} else {
const CompletionOfDocumentsRouteData()
.push(context);
CompletionOfDocumentsRouteData(
tenderId: widget.detail.id!,
).push(context);
}
}
} else if (value ==
@@ -23,4 +23,5 @@ class ProfileStrings {
static const String mainDataTab = 'Main Data';
static const String membersTab = 'Members';
static const String subjectTab = 'Subject';
static const String appVersion = 'Version 1.0.7';
}
@@ -98,6 +98,15 @@ class MainDataTab extends StatelessWidget {
),
),
),
SizedBox(height: 24.0.h()),
Text(
ProfileStrings.appVersion,
style: TextStyle(
fontSize: 12.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey70,
),
),
],
),
);