Files
DriverVault/Phone App/lib/screens/root_shell.dart
T
tajniak81andClaude Opus 4.8 13f50aa9a4 Phone App: add Charging screen + Settings integration tabs
Brings the Flutter app to parity with the recent Web App changes, which
touched features the Phone App did not yet have — so this builds the
Charging and Integrations subsystems, then applies the tab/fold structure.

Charging (new nav destination): split into "Public chargers" (stylized
discovery map + demo session + nearby public stations) and "Home chargers"
(the real Anker Solix OCPP control card — serial refresh, connector/energy
tiles, start/stop, current limit, password step-up on reset — plus the
user's home charger list). Gated by the per-user control mode, degrading to
a Settings hint when off.

Settings: split into "Personal settings" (the existing account/appearance/
profile/security/privacy/danger sections) and "Integrations". The latter
holds foldable Toyota and Anker Solix cards over the superadmin -> org ->
user cascade: locked fields with "inherited from" notes, org-admin scope
switch, enable toggle, save/test with health result, and Anker OCPP token
provisioning. Both tabs stay mounted (IndexedStack) so in-flight edits
survive a switch.

Adds the integration + OCPP control endpoints to api.dart, the resolved
IntegrationView/Scope/Field, IntegrationHealth and AnkerControl models, and
charging.*/settings.tabs.*/nav.charging strings (en/pl/da) plus
settings.integrations.* (en) — mirroring the Web App's own pl/da coverage,
which leaves integrations and charger control untranslated as an English
fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:03:52 +02:00

70 lines
2.1 KiB
Dart

import "package:flutter/material.dart";
import "../i18n.dart";
import "../main.dart";
import "dashboard_screen.dart";
import "charging_screen.dart";
import "settings_screen.dart";
import "admin_users_screen.dart";
/// The app's root shell — a persistent bottom navigation bar (DriverVault mockup
/// layout) hosting the Garage, Charging, Settings and (for admins) Users sections
/// in an IndexedStack so each keeps its state as you switch tabs.
class RootShell extends StatefulWidget {
const RootShell({super.key});
@override
State<RootShell> createState() => _RootShellState();
}
class _RootShellState extends State<RootShell> {
int _index = 0;
@override
Widget build(BuildContext context) {
final isAdmin = authService.user?.isAdmin == true;
final pages = <Widget>[
const DashboardScreen(),
const ChargingScreen(),
const SettingsScreen(),
if (isAdmin) const AdminUsersScreen(),
];
final destinations = <NavigationDestination>[
NavigationDestination(
icon: const Icon(Icons.grid_view_outlined),
selectedIcon: const Icon(Icons.grid_view_rounded),
label: t("nav.garage"),
),
NavigationDestination(
icon: const Icon(Icons.ev_station_outlined),
selectedIcon: const Icon(Icons.ev_station),
label: t("nav.charging"),
),
NavigationDestination(
icon: const Icon(Icons.settings_outlined),
selectedIcon: const Icon(Icons.settings),
label: t("nav.settings"),
),
if (isAdmin)
NavigationDestination(
icon: const Icon(Icons.group_outlined),
selectedIcon: const Icon(Icons.group),
label: t("nav.users"),
),
];
// Guard against the index falling out of range if admin status changes.
final index = _index.clamp(0, pages.length - 1);
return Scaffold(
body: IndexedStack(index: index, children: pages),
bottomNavigationBar: NavigationBar(
selectedIndex: index,
onDestinationSelected: (i) => setState(() => _index = i),
destinations: destinations,
),
);
}
}