Simplify mobile navigation and update progress plan

This commit is contained in:
Rijad Zuzo
2026-02-15 14:34:26 +01:00
parent ac9577c759
commit d708aa58d9
6 changed files with 359 additions and 47 deletions
+117 -15
View File
@@ -19,18 +19,24 @@ class _AppShellState extends ConsumerState<AppShell> {
int _index = 0;
static const List<_ShellDestination> _destinations = <_ShellDestination>[
_ShellDestination('Dashboard', Icons.dashboard_rounded),
_ShellDestination('People', Icons.people_alt_rounded),
_ShellDestination('Moments', Icons.auto_awesome_rounded),
_ShellDestination('Signals', Icons.tips_and_updates_rounded),
_ShellDestination('Sync', Icons.sync_rounded),
_ShellDestination('Settings', Icons.settings_rounded),
_ShellDestination('Dashboard', Icons.dashboard_rounded, 0),
_ShellDestination('People', Icons.people_alt_rounded, 1),
_ShellDestination('Moments', Icons.auto_awesome_rounded, 2),
_ShellDestination('Signals', Icons.tips_and_updates_rounded, 3),
_ShellDestination('Sync', Icons.sync_rounded, 4),
_ShellDestination('Settings', Icons.settings_rounded, 5),
];
static const List<_ShellDestination> _compactPrimaryDestinations =
<_ShellDestination>[
_ShellDestination('Dashboard', Icons.dashboard_rounded, 0),
_ShellDestination('People', Icons.people_alt_rounded, 1),
_ShellDestination('Moments', Icons.auto_awesome_rounded, 2),
_ShellDestination('Signals', Icons.tips_and_updates_rounded, 3),
];
@override
Widget build(BuildContext context) {
final Widget body = _screenFor(_index);
return DecoratedBox(
decoration: const BoxDecoration(
gradient: LinearGradient(
@@ -49,15 +55,36 @@ class _AppShellState extends ConsumerState<AppShell> {
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (constraints.maxWidth < 1000) {
final int compactIndex = _compactSelectedIndex(_index);
final _ShellDestination active =
_compactPrimaryDestinations[compactIndex];
return _CompactShell(
index: _index,
body: body,
destinations: _destinations,
title: active.label,
index: compactIndex,
body: _screenFor(active.screenIndex),
destinations: _compactPrimaryDestinations,
onChange: (int value) {
setState(() {
_index = value;
_index = _compactPrimaryDestinations[value].screenIndex;
});
},
onOpenSecondary: (_SecondaryDestination destination) {
final int screenIndex = switch (destination) {
_SecondaryDestination.sync => 4,
_SecondaryDestination.settings => 5,
};
Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return _CompactSecondaryScreen(
title: _destinations[screenIndex].label,
child: _screenFor(screenIndex),
);
},
),
);
},
);
}
@@ -77,7 +104,7 @@ class _AppShellState extends ConsumerState<AppShell> {
duration: const Duration(milliseconds: 250),
child: KeyedSubtree(
key: ValueKey<int>(_index),
child: body,
child: _screenFor(_index),
),
),
),
@@ -90,6 +117,15 @@ class _AppShellState extends ConsumerState<AppShell> {
);
}
int _compactSelectedIndex(int fullIndex) {
for (int i = 0; i < _compactPrimaryDestinations.length; i += 1) {
if (_compactPrimaryDestinations[i].screenIndex == fullIndex) {
return i;
}
}
return 0;
}
Widget _screenFor(int index) {
switch (index) {
case 0:
@@ -227,22 +263,60 @@ class _Sidebar extends StatelessWidget {
class _CompactShell extends StatelessWidget {
const _CompactShell({
required this.title,
required this.index,
required this.body,
required this.destinations,
required this.onChange,
required this.onOpenSecondary,
});
final String title;
final int index;
final Widget body;
final List<_ShellDestination> destinations;
final ValueChanged<int> onChange;
final ValueChanged<_SecondaryDestination> onOpenSecondary;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(child: body),
Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
child: Row(
children: <Widget>[
Expanded(
child: Text(
title,
style: Theme.of(context).textTheme.titleLarge,
),
),
PopupMenuButton<_SecondaryDestination>(
tooltip: 'More',
icon: const Icon(Icons.more_horiz_rounded),
onSelected: onOpenSecondary,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<_SecondaryDestination>>[
const PopupMenuItem<_SecondaryDestination>(
value: _SecondaryDestination.sync,
child: Text('Sync'),
),
const PopupMenuItem<_SecondaryDestination>(
value: _SecondaryDestination.settings,
child: Text('Settings'),
),
],
),
],
),
),
Expanded(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 220),
child: KeyedSubtree(key: ValueKey<int>(index), child: body),
),
),
NavigationBar(
selectedIndex: index,
onDestinationSelected: onChange,
@@ -260,9 +334,37 @@ class _CompactShell extends StatelessWidget {
}
}
class _CompactSecondaryScreen extends StatelessWidget {
const _CompactSecondaryScreen({required this.title, required this.child});
final String title;
final Widget child;
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[Color(0xFFF3F8FB), Color(0xFFEAF1F8)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(title: Text(title)),
body: child,
),
);
}
}
enum _SecondaryDestination { sync, settings }
class _ShellDestination {
const _ShellDestination(this.label, this.icon);
const _ShellDestination(this.label, this.icon, this.screenIndex);
final String label;
final IconData icon;
final int screenIndex;
}