Align navigation with Material 3 responsive recommendations

- Replace single NavigationRail with adaptive navigation: NavigationBar
  (bottom) for compact <600dp, icon-only NavigationRail for medium
  600–840dp, and extended NavigationRail for expanded ≥840dp
- Remove nested Scaffold/AppBar from About, Settings, DeviceList, and
  DeviceDetail — all screens now render as plain widgets inside the
  single shell Scaffold; page titles surface as inline headings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-05-28 19:51:31 -04:00
co-authored by Claude Sonnet 4.6
parent ebcde0ff4a
commit e5c27f067c
5 changed files with 260 additions and 209 deletions
+51 -51
View File
@@ -119,64 +119,64 @@ class _DeviceListState extends State<DeviceList> {
@override
Widget build(BuildContext context) {
final formatter = FriendlyDateFormatter();
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: AppBar(
title: const Text('Devices'),
actions: [
Badge(
isLabelVisible: _hasActiveDetailFilters,
child: IconButton(
icon: const Icon(Icons.filter_list),
tooltip: 'Filter',
onPressed: () => _showFilterSheet(context),
),
),
],
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48),
child: Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Wrap(
spacing: 8.0,
children: _DeviceFilter.values
.map(
(f) => ChoiceChip(
label: Text(f.label),
selected: _filter == f,
onSelected: (_) {
setState(() => _filter = f);
_loadDevices();
},
),
)
.toList(),
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(child: Text('Devices', style: textTheme.headlineSmall)),
Badge(
isLabelVisible: _hasActiveDetailFilters,
child: IconButton(
icon: const Icon(Icons.filter_list),
tooltip: 'Filter',
onPressed: () => _showFilterSheet(context),
),
),
],
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Wrap(
spacing: 8.0,
children: _DeviceFilter.values
.map(
(f) => ChoiceChip(
label: Text(f.label),
selected: _filter == f,
onSelected: (_) {
setState(() => _filter = f);
_loadDevices();
},
),
)
.toList(),
),
),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _error != null
? Center(child: Text('Error: $_error'))
: _devices.isEmpty
? Center(child: Text(_emptyMessage()))
: RefreshIndicator(
onRefresh: _loadDevices,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: _devices.length,
itemBuilder: (context, index) => _DeviceCard(
device: _devices[index],
formatter: formatter,
Expanded(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _error != null
? Center(child: Text('Error: $_error'))
: _devices.isEmpty
? Center(child: Text(_emptyMessage()))
: RefreshIndicator(
onRefresh: _loadDevices,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: _devices.length,
itemBuilder: (context, index) => _DeviceCard(
device: _devices[index],
formatter: formatter,
onRefresh: _loadDevices,
),
),
),
),
),
),
],
);
}
}