enhacement of UI for mobile screen responsiveness
This commit is contained in:
parent
41112a3eea
commit
5bed5bd2f4
@ -95,7 +95,7 @@ class _SearchAndFilterState extends State<SearchAndFilter> with UIMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: MySpacing.fromLTRB(12, 10, 12, 0),
|
||||
padding: MySpacing.fromLTRB(12, 10, 12, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -179,13 +179,6 @@ class ToggleButtonsRow extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F0F0),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@ -286,8 +279,10 @@ class ExpenseList extends StatelessWidget {
|
||||
return Center(child: MyText.bodyMedium('No expenses found.'));
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 80),
|
||||
return SafeArea(
|
||||
bottom: true,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 100),
|
||||
itemCount: expenseList.length,
|
||||
separatorBuilder: (_, __) =>
|
||||
Divider(color: Colors.grey.shade300, height: 20),
|
||||
@ -303,9 +298,7 @@ class ExpenseList extends StatelessWidget {
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
onTap: () async {
|
||||
await Get.to(
|
||||
() => ExpenseDetailScreen(expenseId: expense.id),
|
||||
);
|
||||
await Get.to(() => ExpenseDetailScreen(expenseId: expense.id));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
@ -321,7 +314,8 @@ class ExpenseList extends StatelessWidget {
|
||||
children: [
|
||||
MyText.bodyMedium('${expense.formattedAmount}',
|
||||
fontWeight: 600),
|
||||
if (expense.status.name.toLowerCase() == 'draft') ...[
|
||||
if (expense.status.name.toLowerCase() ==
|
||||
'draft') ...[
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () =>
|
||||
@ -362,6 +356,7 @@ class ExpenseList extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,38 +354,27 @@ class _AssignTaskBottomSheetState extends State<AssignTaskBottomSheet> {
|
||||
final result = await showModalBottomSheet<List<EmployeeModel>>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.white,
|
||||
barrierColor: Colors.white,
|
||||
useSafeArea: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (context) {
|
||||
return DraggableScrollableSheet(
|
||||
expand: false,
|
||||
initialChildSize: 0.85,
|
||||
minChildSize: 0.6,
|
||||
maxChildSize: 1.0,
|
||||
builder: (_, scrollController) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (_) => SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.90,
|
||||
child: MultipleSelectRoleBottomSheet(
|
||||
projectId: selectedProjectId!,
|
||||
organizationId: selectedOrganization?.id,
|
||||
serviceId: selectedService?.id,
|
||||
roleId: selectedRoleId,
|
||||
initiallySelected: controller.selectedEmployees.toList(),
|
||||
scrollController: scrollController,
|
||||
scrollController: ScrollController(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
controller.selectedEmployees
|
||||
.assignAll(result); // RxList updates UI automatically
|
||||
controller.selectedEmployees.assignAll(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,10 +38,18 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OrientationBuilder(
|
||||
builder: (context, orientation) {
|
||||
final bool isLandscape = orientation == Orientation.landscape;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(72),
|
||||
preferredSize: Size.fromHeight(
|
||||
isLandscape ? 55 : 72, // Responsive height
|
||||
),
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: AppBar(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
elevation: 0.5,
|
||||
@ -50,7 +58,6 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
title: Padding(
|
||||
padding: MySpacing.xy(16, 0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new,
|
||||
@ -58,7 +65,9 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
onPressed: () => Get.offNamed('/dashboard'),
|
||||
),
|
||||
MySpacing.width(8),
|
||||
Expanded(
|
||||
|
||||
/// FIX: Flexible to prevent overflow in landscape
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -74,6 +83,7 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
final projectName =
|
||||
projectController.selectedProject?.name ??
|
||||
'Select Project';
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
const Icon(Icons.work_outline,
|
||||
@ -99,9 +109,13 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
),
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
),
|
||||
|
||||
/// MAIN CONTENT
|
||||
body: SafeArea(
|
||||
bottom: true,
|
||||
child: Column(
|
||||
children: [
|
||||
// ---------------- TabBar ----------------
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: TabBar(
|
||||
@ -115,8 +129,6 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// ---------------- TabBarView ----------------
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
@ -128,6 +140,9 @@ class _DirectoryMainScreenState extends State<DirectoryMainScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,12 +49,14 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(
|
||||
0xFFF5F5F5),
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
appBar: _buildAppBar(),
|
||||
body: GestureDetector(
|
||||
|
||||
// ✅ SafeArea added so nothing hides under system navigation buttons
|
||||
body: SafeArea(
|
||||
bottom: true,
|
||||
child: GestureDetector(
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
@ -69,9 +71,12 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
displacement: 60,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Container(
|
||||
color:
|
||||
const Color(0xFFF5F5F5),
|
||||
child: Padding(
|
||||
// ✅ Extra bottom padding so content does NOT go under 3-button navbar
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 20,
|
||||
),
|
||||
|
||||
child: Column(
|
||||
children: [
|
||||
_buildSearchBar(),
|
||||
@ -84,6 +89,7 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -322,7 +328,6 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
);
|
||||
}
|
||||
|
||||
// ✅ No employee selected yet
|
||||
if (controller.selectedEmployee.value == null) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(top: 100),
|
||||
@ -330,7 +335,6 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
);
|
||||
}
|
||||
|
||||
// ✅ Employee selected but no payments found
|
||||
if (controller.payments.isEmpty) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(top: 100),
|
||||
@ -340,7 +344,6 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
);
|
||||
}
|
||||
|
||||
// ✅ Payments available
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@ -378,7 +381,7 @@ class _AdvancePaymentScreenState extends State<AdvancePaymentScreen>
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xFFE0E0E0), width: 0.9),
|
||||
bottom: BorderSide(color: const Color(0xFFE0E0E0), width: 0.9),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
|
||||
@ -113,14 +113,18 @@ class _FinanceScreenState extends State<FinanceScreen>
|
||||
),
|
||||
),
|
||||
),
|
||||
body: FadeTransition(
|
||||
body: SafeArea(
|
||||
top: false, // keep appbar area same
|
||||
bottom: true, // avoid system bottom buttons
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: Obx(() {
|
||||
if (menuController.isLoading.value) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (menuController.hasError.value || menuController.menuItems.isEmpty) {
|
||||
if (menuController.hasError.value ||
|
||||
menuController.menuItems.isEmpty) {
|
||||
return const Center(
|
||||
child: Text(
|
||||
"Failed to load menus. Please try again later.",
|
||||
@ -129,7 +133,6 @@ class _FinanceScreenState extends State<FinanceScreen>
|
||||
);
|
||||
}
|
||||
|
||||
// Filter allowed Finance menus dynamically
|
||||
final financeMenuIds = [
|
||||
MenuItems.expenseReimbursement,
|
||||
MenuItems.paymentRequests,
|
||||
@ -149,8 +152,18 @@ class _FinanceScreenState extends State<FinanceScreen>
|
||||
);
|
||||
}
|
||||
|
||||
// ---- IMPORTANT FIX: Add bottom safe padding ----
|
||||
final double bottomInset =
|
||||
MediaQuery.of(context).viewPadding.bottom;
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
bottomInset +
|
||||
24, // ensures charts never go under system buttons
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildFinanceModulesCompact(financeMenus),
|
||||
@ -165,6 +178,7 @@ class _FinanceScreenState extends State<FinanceScreen>
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -172,9 +186,12 @@ class _FinanceScreenState extends State<FinanceScreen>
|
||||
Widget _buildFinanceModulesCompact(List<MenuItem> financeMenus) {
|
||||
// Map menu IDs to icon + color
|
||||
final Map<String, _FinanceCardMeta> financeCardMeta = {
|
||||
MenuItems.expenseReimbursement: _FinanceCardMeta(LucideIcons.badge_dollar_sign, contentTheme.info),
|
||||
MenuItems.paymentRequests: _FinanceCardMeta(LucideIcons.receipt_text, contentTheme.primary),
|
||||
MenuItems.advancePaymentStatements: _FinanceCardMeta(LucideIcons.wallet, contentTheme.warning),
|
||||
MenuItems.expenseReimbursement:
|
||||
_FinanceCardMeta(LucideIcons.badge_dollar_sign, contentTheme.info),
|
||||
MenuItems.paymentRequests:
|
||||
_FinanceCardMeta(LucideIcons.receipt_text, contentTheme.primary),
|
||||
MenuItems.advancePaymentStatements:
|
||||
_FinanceCardMeta(LucideIcons.wallet, contentTheme.warning),
|
||||
};
|
||||
|
||||
// Build the stat items using API-provided mobileLink
|
||||
@ -198,14 +215,16 @@ Widget _buildFinanceModulesCompact(List<MenuItem> financeMenus) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
// Determine number of columns dynamically
|
||||
int crossAxisCount = (constraints.maxWidth ~/ 80).clamp(2, 4);
|
||||
double cardWidth = (constraints.maxWidth - (crossAxisCount - 1) * 6) / crossAxisCount;
|
||||
double cardWidth =
|
||||
(constraints.maxWidth - (crossAxisCount - 1) * 6) / crossAxisCount;
|
||||
|
||||
return Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
alignment: WrapAlignment.end,
|
||||
children: stats
|
||||
.map((stat) => _buildFinanceModuleCard(stat, projectSelected, cardWidth))
|
||||
.map((stat) =>
|
||||
_buildFinanceModuleCard(stat, projectSelected, cardWidth))
|
||||
.toList(),
|
||||
);
|
||||
});
|
||||
|
||||
@ -99,7 +99,13 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: _buildAppBar(),
|
||||
body: Column(
|
||||
|
||||
// ------------------------
|
||||
// FIX: SafeArea prevents content from going under 3-button navbar
|
||||
// ------------------------
|
||||
body: SafeArea(
|
||||
bottom: true,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.white,
|
||||
@ -135,6 +141,8 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
floatingActionButton: Obx(() {
|
||||
if (permissionController.permissions.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
@ -294,7 +302,6 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
|
||||
final list = filteredList(isHistory: isHistory);
|
||||
|
||||
// ScrollController for infinite scroll
|
||||
final scrollController = ScrollController();
|
||||
scrollController.addListener(() {
|
||||
if (scrollController.position.pixels >=
|
||||
@ -309,6 +316,7 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
child: list.isEmpty
|
||||
? ListView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.only(bottom: 100),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.5,
|
||||
@ -325,7 +333,12 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
)
|
||||
: ListView.separated(
|
||||
controller: scrollController,
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 80),
|
||||
|
||||
// ------------------------
|
||||
// FIX: ensure bottom list items stay visible above nav bar
|
||||
// ------------------------
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 120),
|
||||
|
||||
itemCount: list.length + 1,
|
||||
separatorBuilder: (_, __) =>
|
||||
Divider(color: Colors.grey.shade300, height: 20),
|
||||
@ -365,10 +378,6 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
Row(
|
||||
children: [
|
||||
MyText.bodyMedium(item.expenseCategory.name, fontWeight: 600),
|
||||
|
||||
// -------------------------------
|
||||
// ADV CHIP (only if advance)
|
||||
// -------------------------------
|
||||
if (item.isAdvancePayment == true) ...[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
|
||||
@ -51,6 +51,7 @@ class _JobDetailsScreenState extends State<JobDetailsScreen> with UIMixin {
|
||||
controller.fetchJobDetail(widget.jobId).then((_) {
|
||||
final job = controller.jobDetail.value?.data;
|
||||
if (job != null) {
|
||||
_selectedTags.value = job.tags ?? [];
|
||||
_titleController.text = job.title ?? '';
|
||||
_descriptionController.text = job.description ?? '';
|
||||
_startDateController.text = DateTimeUtils.convertUtcToLocal(
|
||||
@ -169,6 +170,11 @@ class _JobDetailsScreenState extends State<JobDetailsScreen> with UIMixin {
|
||||
message: "Job updated successfully",
|
||||
type: SnackbarType.success);
|
||||
await controller.fetchJobDetail(widget.jobId);
|
||||
final updatedJob = controller.jobDetail.value?.data;
|
||||
if (updatedJob != null) {
|
||||
_selectedTags.value = updatedJob.tags ?? [];
|
||||
}
|
||||
|
||||
isEditing.value = false;
|
||||
} else {
|
||||
showAppSnackbar(
|
||||
|
||||
@ -22,11 +22,11 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
final ServiceProjectController controller =
|
||||
Get.put(ServiceProjectController());
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Fetch projects safely after first frame
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
controller.fetchProjects();
|
||||
});
|
||||
@ -49,7 +49,6 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
onTap: () {
|
||||
// Navigate to ServiceProjectDetailsScreen
|
||||
Get.to(() => ServiceProjectDetailsScreen(
|
||||
projectId: project.id,
|
||||
projectName: project.name,
|
||||
@ -60,7 +59,6 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
/// Project Header
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -92,20 +90,14 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
MySpacing.height(10),
|
||||
|
||||
/// Assigned Date
|
||||
_buildDetailRow(
|
||||
Icons.date_range_outlined,
|
||||
Colors.teal,
|
||||
"Assigned: ${DateTimeUtils.convertUtcToLocal(project.assignedDate.toIso8601String(), format: DateTimeUtils.defaultFormat)}",
|
||||
fontSize: 13,
|
||||
),
|
||||
|
||||
MySpacing.height(8),
|
||||
|
||||
/// Client Info
|
||||
if (project.client != null)
|
||||
_buildDetailRow(
|
||||
Icons.account_circle_outlined,
|
||||
@ -113,20 +105,14 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
"Client: ${project.client!.name} (${project.client!.contactPerson})",
|
||||
fontSize: 13,
|
||||
),
|
||||
|
||||
MySpacing.height(8),
|
||||
|
||||
/// Contact Info
|
||||
_buildDetailRow(
|
||||
Icons.phone,
|
||||
Colors.green,
|
||||
"Contact: ${project.contactName} (${project.contactPhone})",
|
||||
fontSize: 13,
|
||||
),
|
||||
|
||||
MySpacing.height(12),
|
||||
|
||||
/// Services List
|
||||
if (project.services.isNotEmpty)
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
@ -197,14 +183,18 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
|
||||
appBar: CustomAppBar(
|
||||
title: "Service Projects",
|
||||
projectName: 'All Service Projects',
|
||||
onBackPressed: () => Get.toNamed('/dashboard'),
|
||||
),
|
||||
body: Column(
|
||||
|
||||
// FIX 1: Entire body wrapped in SafeArea
|
||||
body: SafeArea(
|
||||
bottom: true,
|
||||
child: Column(
|
||||
children: [
|
||||
/// Search bar and actions
|
||||
Padding(
|
||||
padding: MySpacing.xy(8, 8),
|
||||
child: Row(
|
||||
@ -253,8 +243,6 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
/// Project List
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
@ -262,6 +250,7 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
}
|
||||
|
||||
final projects = controller.filteredProjects;
|
||||
|
||||
return MyRefreshIndicator(
|
||||
onRefresh: _refreshProjects,
|
||||
backgroundColor: Colors.indigo,
|
||||
@ -270,8 +259,11 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
? _buildEmptyState()
|
||||
: ListView.separated(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
|
||||
// FIX 2: Increased bottom padding for landscape
|
||||
padding: MySpacing.only(
|
||||
left: 8, right: 8, top: 4, bottom: 80),
|
||||
left: 8, right: 8, top: 4, bottom: 120),
|
||||
|
||||
itemCount: projects.length,
|
||||
separatorBuilder: (_, __) => MySpacing.height(12),
|
||||
itemBuilder: (_, index) =>
|
||||
@ -282,6 +274,7 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user