badge percentage support

This commit is contained in:
2025-12-26 22:49:43 +00:00
parent 4bd6f0bbed
commit 0971124fd4
10 changed files with 866 additions and 113 deletions

View File

@@ -22,6 +22,14 @@ import 'package:provider/provider.dart';
final GlobalKey<NavigatorState> _shellNavigatorKey = GlobalKey<NavigatorState>();
const List<String> _contentPages = [
"/dashboard",
"/logbook",
"/traction",
"/add",
"/more",
];
const List<String> _defaultTabDestinations = [
"/dashboard",
"/logbook/entries",
"/traction",
@@ -29,7 +37,7 @@ const List<String> _contentPages = [
"/more",
];
const int _addTabIndex = 5;
const int _addTabIndex = 3;
class _NavItem {
final String label;
@@ -53,10 +61,9 @@ int tabIndexForPath(String path) {
matchPath = '/logbook/entries';
} else if (matchPath.startsWith('/trips')) {
matchPath = '/logbook/trips';
} else if (matchPath == '/logbook') {
matchPath = '/logbook/entries';
} else if (matchPath.startsWith('/logbook/trips')) {
matchPath = '/logbook/entries';
}
if (matchPath.startsWith('/logbook')) {
matchPath = '/logbook';
} else if (matchPath.startsWith('/profile') ||
matchPath.startsWith('/settings') ||
matchPath.startsWith('/more')) {
@@ -107,7 +114,7 @@ class _MyAppState extends State<MyApp> {
routes: [
GoRoute(
path: '/',
redirect: (_, __) => '/dashboard',
redirect: (context, state) => '/dashboard',
),
ShellRoute(
navigatorKey: _shellNavigatorKey,
@@ -119,7 +126,7 @@ class _MyAppState extends State<MyApp> {
),
GoRoute(
path: '/logbook',
builder: (context, state) => const LogbookPage(),
redirect: (context, state) => '/logbook/entries',
),
GoRoute(
path: '/logbook/entries',
@@ -132,12 +139,11 @@ class _MyAppState extends State<MyApp> {
),
GoRoute(
path: '/trips',
builder: (context, state) =>
const LogbookPage(initialTab: LogbookTab.trips),
redirect: (context, state) => '/logbook/trips',
),
GoRoute(
path: '/legs',
builder: (context, state) => const LogbookPage(),
redirect: (context, state) => '/logbook/entries',
),
GoRoute(
path: '/traction',
@@ -254,14 +260,14 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
List<String> get contentPages => _contentPages;
List<String> get tabDestinations => _defaultTabDestinations;
Future<void> _onItemTapped(int index, int currentIndex) async {
if (index < 0 || index >= contentPages.length) {
if (index < 0 || index >= tabDestinations.length) {
return;
}
final currentPath = GoRouterState.of(context).uri.path;
final targetPath = contentPages[index];
final targetPath = tabDestinations[index];
final alreadyAtTarget =
currentPath == targetPath || currentPath.startsWith('$targetPath/');
if (index == currentIndex && alreadyAtTarget) return;
@@ -272,9 +278,9 @@ class _MyHomePageState extends State<MyHomePage> {
});
}
final List<int> _history = [];
final List<String> _history = [];
int _historyPosition = -1;
final List<int> _forwardHistory = [];
final List<String> _forwardHistory = [];
bool _suppressRecord = false;
bool _fetched = false;
@@ -323,7 +329,7 @@ class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
final uri = GoRouterState.of(context).uri;
final pageIndex = tabIndexForPath(uri.path);
_syncHistory(pageIndex);
_syncHistory(uri.path);
if (pageIndex != _addTabIndex) {
NavigationGuard.unregister();
}
@@ -544,24 +550,21 @@ class _MyHomePageState extends State<MyHomePage> {
Future<void> _openNotificationsPanel(BuildContext context) async {
final data = context.read<DataService>();
final isWide = MediaQuery.sizeOf(context).width >= 900;
final sheetHeight = MediaQuery.sizeOf(context).height * 0.9;
try {
await data.fetchNotifications();
} catch (_) {
// Already logged inside data service.
}
if (!mounted) return;
final isWide = MediaQuery.of(context).size.width >= 900;
final panelBuilder = (BuildContext ctx) {
return _buildNotificationsContent(ctx, isWide);
};
if (!context.mounted) return;
if (isWide) {
await showDialog(
context: context,
builder: (dialogCtx) => Dialog(
insetPadding: const EdgeInsets.all(16),
child: panelBuilder(dialogCtx),
child: _buildNotificationsContent(dialogCtx, isWide),
),
);
} else {
@@ -569,11 +572,10 @@ class _MyHomePageState extends State<MyHomePage> {
context: context,
isScrollControlled: true,
builder: (sheetCtx) {
final height = MediaQuery.of(context).size.height * 0.9;
return SizedBox(
height: height,
height: sheetHeight,
child: SafeArea(
child: panelBuilder(sheetCtx),
child: _buildNotificationsContent(sheetCtx, isWide),
),
);
},
@@ -606,7 +608,7 @@ class _MyHomePageState extends State<MyHomePage> {
height: listHeight,
child: ListView.separated(
itemCount: notifications.length,
separatorBuilder: (_, __) => const SizedBox(height: 8),
separatorBuilder: (_, index) => const SizedBox(height: 8),
itemBuilder: (ctx, index) {
final item = notifications[index];
return Card(
@@ -644,11 +646,18 @@ class _MyHomePageState extends State<MyHomePage> {
.textTheme
.bodySmall
?.copyWith(
color: Theme.of(context)
.textTheme
.bodySmall
?.color
?.withOpacity(0.7),
color: () {
final baseColor = Theme.of(context)
.textTheme
.bodySmall
?.color;
if (baseColor == null) return null;
final newAlpha =
(baseColor.a * 0.7).clamp(0.0, 1.0);
return baseColor.withValues(
alpha: newAlpha,
);
}(),
),
),
],
@@ -757,31 +766,32 @@ class _MyHomePageState extends State<MyHomePage> {
);
}
int get _currentPageIndex => tabIndexForPath(GoRouterState.of(context).uri.path);
Future<bool> _handleBackNavigation({
bool allowExit = false,
bool recordForward = false,
}) async {
final pageIndex = _currentPageIndex;
final currentPath = GoRouterState.of(context).uri.path;
final shellNav = _shellNavigatorKey.currentState;
if (shellNav != null && shellNav.canPop()) {
if (recordForward) _pushForward(currentPath);
_alignHistoryAfterPop(currentPath);
shellNav.pop();
return true;
}
if (_historyPosition > 0) {
if (recordForward) _pushForward(pageIndex);
if (recordForward) _pushForward(currentPath);
_historyPosition -= 1;
_suppressRecord = true;
context.go(contentPages[_history[_historyPosition]]);
context.go(_history[_historyPosition]);
return true;
}
if (pageIndex != 0) {
if (recordForward) _pushForward(pageIndex);
final homePath = tabDestinations.first;
if (currentPath != homePath) {
if (recordForward) _pushForward(currentPath);
_suppressRecord = true;
context.go(contentPages[0]);
context.go(homePath);
return true;
}
@@ -795,35 +805,48 @@ class _MyHomePageState extends State<MyHomePage> {
Future<bool> _handleForwardNavigation() async {
if (_forwardHistory.isEmpty) return false;
final nextTab = _forwardHistory.removeLast();
final nextPath = _forwardHistory.removeLast();
// Move cursor forward, keeping history in sync.
if (_historyPosition < _history.length - 1) {
_historyPosition += 1;
_history[_historyPosition] = nextTab;
_history[_historyPosition] = nextPath;
if (_historyPosition < _history.length - 1) {
_history.removeRange(_historyPosition + 1, _history.length);
}
} else {
_history.add(nextTab);
_history.add(nextPath);
_historyPosition = _history.length - 1;
}
_suppressRecord = true;
if (!mounted) return false;
context.go(contentPages[nextTab]);
context.go(nextPath);
return true;
}
void _pushForward(int pageIndex) {
if (_forwardHistory.isEmpty || _forwardHistory.last != pageIndex) {
_forwardHistory.add(pageIndex);
void _pushForward(String path) {
if (_forwardHistory.isEmpty || _forwardHistory.last != path) {
_forwardHistory.add(path);
}
}
void _syncHistory(int pageIndex) {
void _alignHistoryAfterPop(String currentPath) {
if (_history.isEmpty) return;
if (_historyPosition >= 0 &&
_historyPosition < _history.length &&
_history[_historyPosition] == currentPath) {
if (_historyPosition > 0) {
_historyPosition -= 1;
}
_history.removeRange(_historyPosition + 1, _history.length);
_suppressRecord = true;
}
}
void _syncHistory(String path) {
if (_history.isEmpty) {
_history.add(pageIndex);
_history.add(path);
_historyPosition = 0;
return;
}
@@ -833,13 +856,13 @@ class _MyHomePageState extends State<MyHomePage> {
}
if (_historyPosition >= 0 &&
_historyPosition < _history.length &&
_history[_historyPosition] == pageIndex) {
_history[_historyPosition] == path) {
return;
}
if (_historyPosition < _history.length - 1) {
_history.removeRange(_historyPosition + 1, _history.length);
}
_history.add(pageIndex);
_history.add(path);
_historyPosition = _history.length - 1;
_forwardHistory.clear();
}
@@ -847,6 +870,6 @@ class _MyHomePageState extends State<MyHomePage> {
void _navigateToIndex(int index) {
_suppressRecord = false;
_forwardHistory.clear();
context.go(contentPages[index]);
context.go(tabDestinations[index]);
}
}