Adds a dedicated BrandWordmark widget that splits 'Recl∆Im'
into three TextSpans and tints the middle ∆I in the petrol
signal colour. The fai_web universe uses this same accent rule
for the F∆I and Ch∆In glyphs — Recl∆Im now follows it, so
the brand stays visually coherent across every F∆I surface.
Where it lands:
- Landing page: large DeltaMark (96 px) and BrandWordmark
(56 px) sit on a single row, the wordmark to the right of
the mark. The two together carry the brand identity that
a viewer sees first when opening the app.
- NavigationRail leading: the muted 'Recl∆Im' Text line is
replaced with the petrol-accented BrandWordmark (18 px),
keeping the vendor tag 'F∆I' as a small muted line under it.
widget_test.dart adapts: the literal 'Recl∆Im'-find no longer
matches because the wordmark is now three text spans, so the
test checks for BrandWordmark by type — equivalent assertion,
robust to glyph-level splits.
Signed-off-by: flemming-it <sf@flemming.it>
100 lines
3.2 KiB
Dart
100 lines
3.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../data/repository.dart';
|
|
import '../theme/reclaim_tokens.dart';
|
|
import '../widgets/brand_wordmark.dart';
|
|
import '../widgets/delta_mark.dart';
|
|
import 'about_page.dart';
|
|
import 'heatmap_page.dart';
|
|
import 'hub_status_page.dart';
|
|
import 'legal_page.dart';
|
|
import 'norms_list_page.dart';
|
|
|
|
/// App shell with a NavigationRail. Each rail destination is one
|
|
/// page; pages do their own scrolling.
|
|
class ShellPage extends StatefulWidget {
|
|
const ShellPage({super.key, required this.repository});
|
|
|
|
final EvaluationRepository repository;
|
|
|
|
@override
|
|
State<ShellPage> createState() => _ShellPageState();
|
|
}
|
|
|
|
class _ShellPageState extends State<ShellPage> {
|
|
int _index = 0;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = Theme.of(context).textTheme;
|
|
final pages = <Widget>[
|
|
HeatmapPage(repository: widget.repository),
|
|
NormsListPage(repository: widget.repository),
|
|
HubStatusPage(repository: widget.repository),
|
|
const AboutPage(),
|
|
const LegalPage(),
|
|
];
|
|
|
|
return Scaffold(
|
|
body: Row(
|
|
children: [
|
|
NavigationRail(
|
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
extended: MediaQuery.of(context).size.width > 1100,
|
|
selectedIndex: _index,
|
|
onDestinationSelected: (i) => setState(() => _index = i),
|
|
leading: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: ReclaimSpace.lg,
|
|
horizontal: ReclaimSpace.md,
|
|
),
|
|
child: Column(
|
|
children: [
|
|
const DeltaMark(size: 36),
|
|
const SizedBox(height: ReclaimSpace.sm),
|
|
const BrandWordmark(size: 18),
|
|
Text(
|
|
'F∆I',
|
|
style: t.labelSmall?.copyWith(
|
|
letterSpacing: 2,
|
|
color: ReclaimColors.mute,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
destinations: const [
|
|
NavigationRailDestination(
|
|
icon: Icon(Icons.grid_view_outlined),
|
|
selectedIcon: Icon(Icons.grid_view),
|
|
label: Text('Heatmap'),
|
|
),
|
|
NavigationRailDestination(
|
|
icon: Icon(Icons.list_alt_outlined),
|
|
selectedIcon: Icon(Icons.list_alt),
|
|
label: Text('Normen'),
|
|
),
|
|
NavigationRailDestination(
|
|
icon: Icon(Icons.hub_outlined),
|
|
selectedIcon: Icon(Icons.hub),
|
|
label: Text('Hub'),
|
|
),
|
|
NavigationRailDestination(
|
|
icon: Icon(Icons.info_outline),
|
|
selectedIcon: Icon(Icons.info),
|
|
label: Text('Methodik'),
|
|
),
|
|
NavigationRailDestination(
|
|
icon: Icon(Icons.gavel_outlined),
|
|
selectedIcon: Icon(Icons.gavel),
|
|
label: Text('Recht'),
|
|
),
|
|
],
|
|
),
|
|
const VerticalDivider(width: 1),
|
|
Expanded(child: pages[_index]),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|