feat: Update app layout with MainAppHeader and enhance user settings interface
Build Android APK / build-apk (push) Successful in 19m27s
Build Android AAB / build-aab (push) Successful in 12m5s

This commit is contained in:
2026-04-23 03:09:24 +07:00
parent 297fc45707
commit 2b8fa4ee57
13 changed files with 1627 additions and 625 deletions
+108 -30
View File
@@ -8,44 +8,122 @@ class AppShell extends StatelessWidget {
final Widget child;
int _indexForLocation(String location) {
if (location.startsWith(RouteNames.search)) return 1;
if (location.startsWith(RouteNames.bookshelf)) return 2;
if (location.startsWith(RouteNames.genres)) return 3;
if (location.startsWith(RouteNames.profile)) return 4;
return 0;
String _tabForLocation(String location) {
if (location.startsWith(RouteNames.bookshelf)) return RouteNames.bookshelf;
if (location.startsWith(RouteNames.genres)) return RouteNames.genres;
if (location.startsWith(RouteNames.profile)) return RouteNames.profile;
return RouteNames.home;
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final location = GoRouterState.of(context).uri.path;
final selectedIndex = _indexForLocation(location);
final selectedTab = _tabForLocation(location);
return Scaffold(
body: child,
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
switch (index) {
case 0:
context.go(RouteNames.home);
case 1:
context.go(RouteNames.search);
case 2:
context.go(RouteNames.bookshelf);
case 3:
context.go(RouteNames.genres);
case 4:
context.go(RouteNames.profile);
}
},
destinations: const [
NavigationDestination(icon: Icon(Icons.home_outlined), label: 'Home'),
NavigationDestination(icon: Icon(Icons.search), label: 'Tim kiem'),
NavigationDestination(icon: Icon(Icons.bookmark_border), label: 'Tu sach'),
NavigationDestination(icon: Icon(Icons.category_outlined), label: 'The loai'),
NavigationDestination(icon: Icon(Icons.person_outline), label: 'Tai khoan'),
],
bottomNavigationBar: Container(
decoration: BoxDecoration(
color: colorScheme.surface,
border: Border(
top: BorderSide(color: colorScheme.outlineVariant.withAlpha(80)),
),
),
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 8, 10, 6),
child: Row(
children: [
_ShellNavItem(
icon: Icons.home_rounded,
label: 'Trang chủ',
selected: selectedTab == RouteNames.home,
onTap: () => context.go(RouteNames.home),
),
_ShellNavItem(
icon: Icons.layers_rounded,
label: 'Tủ sách',
selected: selectedTab == RouteNames.bookshelf,
onTap: () => context.go(RouteNames.bookshelf),
),
_ShellNavItem(
icon: Icons.category_rounded,
label: 'Thể loại',
selected: selectedTab == RouteNames.genres,
onTap: () => context.go(RouteNames.genres),
),
_ShellNavItem(
icon: Icons.person_rounded,
label: 'Tài khoản',
selected: selectedTab == RouteNames.profile,
onTap: () => context.go(RouteNames.profile),
),
],
),
),
),
),
);
}
}
class _ShellNavItem extends StatelessWidget {
const _ShellNavItem({
required this.icon,
required this.label,
required this.selected,
required this.onTap,
});
final IconData icon;
final String label;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final activeColor = const Color(0xFF14B8A6);
final inactiveColor = colorScheme.onSurfaceVariant;
return Expanded(
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(18),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 180),
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: selected ? activeColor.withAlpha(28) : Colors.transparent,
shape: BoxShape.circle,
),
child: Icon(
icon,
size: 22,
color: selected ? activeColor : inactiveColor,
),
),
const SizedBox(height: 4),
Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: selected ? const Color(0xFFF7B500) : inactiveColor,
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
),
),
],
),
),
),
);
}