Improved attendance screen navigations on mobile

This commit is contained in:
2026-05-18 23:53:08 +08:00
parent 425fe8e683
commit 82141dd61c
+146 -13
View File
@@ -105,19 +105,7 @@ class _AttendanceScreenState extends ConsumerState<AttendanceScreen>
title: 'Attendance',
subtitle: 'Check in, logbook, pass slip and leave',
),
TabBar(
controller: _tabController,
isScrollable: true,
tabAlignment: TabAlignment.start,
tabs: const [
Tab(text: 'Check In'),
Tab(text: 'Logbook'),
Tab(text: 'Work Log'),
Tab(text: 'My Schedule'),
Tab(text: 'Pass Slip'),
Tab(text: 'Leave'),
],
),
_buildAttendanceNav(context),
Expanded(
child: TabBarView(
controller: _tabController,
@@ -137,6 +125,35 @@ class _AttendanceScreenState extends ConsumerState<AttendanceScreen>
);
}
Widget _buildAttendanceNav(BuildContext context) {
final isMobile = AppBreakpoints.isMobile(context);
final isDesktop = AppBreakpoints.isDesktop(context);
if (isMobile) {
return _AttendanceSectionPicker(
controller: _tabController,
currentIndex: _tabController.index,
);
}
return TabBar(
controller: _tabController,
isScrollable: isDesktop,
tabAlignment: isDesktop ? TabAlignment.start : TabAlignment.fill,
tabs: _attendanceTabs(shortLabels: !isDesktop),
);
}
List<Widget> _attendanceTabs({required bool shortLabels}) => [
Tab(icon: const Icon(Icons.login_rounded, size: 18), text: 'Check In'),
Tab(icon: const Icon(Icons.menu_book_outlined, size: 18), text: 'Logbook'),
Tab(icon: const Icon(Icons.timer_outlined, size: 18), text: 'Work Log'),
Tab(
icon: const Icon(Icons.calendar_month_outlined, size: 18),
text: shortLabels ? 'Schedule' : 'My Schedule',
),
Tab(icon: const Icon(Icons.directions_walk_outlined, size: 18), text: 'Pass Slip'),
Tab(icon: const Icon(Icons.event_busy_outlined, size: 18), text: 'Leave'),
];
Widget _buildFabMenu(
BuildContext context,
ThemeData theme,
@@ -282,6 +299,122 @@ class _AttendanceScreenState extends ConsumerState<AttendanceScreen>
}
}
// ────────────────────────────────────────────────
// Adaptive section picker — mobile only (<600dp)
// ────────────────────────────────────────────────
class _AttendanceSectionPicker extends StatelessWidget {
const _AttendanceSectionPicker({
required this.controller,
required this.currentIndex,
});
final TabController controller;
final int currentIndex;
static final _sections = <({IconData icon, String label})>[
(icon: Icons.login_rounded, label: 'Check In'),
(icon: Icons.menu_book_outlined, label: 'Logbook'),
(icon: Icons.timer_outlined, label: 'Work Log'),
(icon: Icons.calendar_month_outlined, label: 'My Schedule'),
(icon: Icons.directions_walk_outlined, label: 'Pass Slip'),
(icon: Icons.event_busy_outlined, label: 'Leave'),
];
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final tt = Theme.of(context).textTheme;
final current = _sections[currentIndex];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: currentIndex > 0
? () => controller.animateTo(currentIndex - 1)
: null,
),
Expanded(
child: ActionChip(
avatar: Icon(current.icon, size: 16, color: cs.primary),
label: Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Text(
current.label,
style: tt.labelLarge?.copyWith(color: cs.onSurface),
overflow: TextOverflow.ellipsis,
),
),
Icon(Icons.expand_more, size: 16, color: cs.onSurfaceVariant),
],
),
backgroundColor: cs.surfaceContainerHighest,
side: BorderSide.none,
onPressed: () => _showSheet(context),
),
),
IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: currentIndex < _sections.length - 1
? () => controller.animateTo(currentIndex + 1)
: null,
),
],
),
);
}
void _showSheet(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final tt = Theme.of(context).textTheme;
showModalBottomSheet(
context: context,
useRootNavigator: true,
showDragHandle: true,
builder: (sheetContext) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Align(
alignment: Alignment.centerLeft,
child: Text('Attendance', style: tt.titleMedium),
),
),
for (int i = 0; i < _sections.length; i++)
ListTile(
leading: Icon(
_sections[i].icon,
color: i == currentIndex ? cs.primary : cs.onSurfaceVariant,
),
title: Text(
_sections[i].label,
style: i == currentIndex
? tt.bodyMedium?.copyWith(
color: cs.primary,
fontWeight: FontWeight.w600,
)
: tt.bodyMedium,
),
selected: i == currentIndex,
selectedTileColor: cs.primaryContainer.withValues(alpha: 0.3),
onTap: () {
controller.animateTo(i);
Navigator.pop(sheetContext);
},
),
const SizedBox(height: 16),
],
),
);
}
}
// ────────────────────────────────────────────────
// Tab 1 Check In / Check Out
// ────────────────────────────────────────────────