AppPage improvements.

- Make AppPage content scrollable.
- Add optional FAB (with extra padding for FAB).
- Add bottomMenu.
This commit is contained in:
Dain Nilsson 2022-04-03 11:03:03 +02:00
parent 2f13399b09
commit 40d616c8bd
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
6 changed files with 78 additions and 34 deletions

View File

@ -1,4 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'models.dart';
import 'state.dart';
ScaffoldFeatureController showMessage( ScaffoldFeatureController showMessage(
BuildContext context, BuildContext context,
@ -14,3 +18,43 @@ ScaffoldFeatureController showMessage(
width: narrow ? null : 400, width: narrow ? null : 400,
)); ));
} }
Future<void> showBottomMenu(
BuildContext context, List<MenuAction> actions) async {
await showModalBottomSheet(
context: context,
constraints: MediaQuery.of(context).size.width > 540
? const BoxConstraints(maxWidth: 380)
: null,
builder: (context) => _BottomMenu(actions),
);
}
class _BottomMenu extends ConsumerWidget {
final List<MenuAction> actions;
const _BottomMenu(this.actions);
@override
Widget build(BuildContext context, WidgetRef ref) {
// If current device changes, we need to pop back to the main Page.
ref.listen<DeviceNode?>(currentDeviceProvider, (previous, next) {
Navigator.of(context).pop();
});
return Column(
mainAxisSize: MainAxisSize.min,
children: actions
.map((a) => ListTile(
leading: a.icon,
title: Text(a.text),
onTap: a.action == null
? null
: () {
Navigator.pop(context);
a.action?.call(context);
},
))
.toList(),
);
}
}

View File

@ -93,6 +93,9 @@ class DevicePath {
int get hashCode => Object.hashAll(segments); int get hashCode => Object.hashAll(segments);
String get key => segments.join('/'); String get key => segments.join('/');
@override
String toString() => key;
} }
@freezed @freezed

View File

@ -9,13 +9,13 @@ class AppPage extends ConsumerWidget {
final Widget? title; final Widget? title;
final Widget child; final Widget child;
final Widget? floatingActionButton; final Widget? floatingActionButton;
final void Function()? onBack; final bool centered;
AppPage( AppPage(
{Key? key, {Key? key,
this.title, this.title,
required this.child, required this.child,
this.onBack, this.floatingActionButton,
this.floatingActionButton}) this.centered = false})
: super(key: key); : super(key: key);
@override @override
@ -45,21 +45,24 @@ class AppPage extends ConsumerWidget {
}, },
); );
Widget _buildScrollView() => SingleChildScrollView(
// Make sure FAB doesn't block content
padding: floatingActionButton != null
? const EdgeInsets.only(bottom: 72)
: null,
child: child,
);
Scaffold _buildScaffold(BuildContext context, WidgetRef ref, bool hasDrawer) { Scaffold _buildScaffold(BuildContext context, WidgetRef ref, bool hasDrawer) {
return Scaffold( return Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
appBar: AppBar( appBar: AppBar(
leading: onBack != null
? BackButton(
onPressed: onBack,
)
: null,
title: title, title: title,
centerTitle: true, centerTitle: true,
actions: const [DeviceButton()], actions: const [DeviceButton()],
), ),
drawer: hasDrawer ? const MainPageDrawer() : null, drawer: hasDrawer ? const MainPageDrawer() : null,
body: child, body: centered ? Center(child: _buildScrollView()) : _buildScrollView(),
floatingActionButton: floatingActionButton, floatingActionButton: floatingActionButton,
); );
} }

View File

@ -10,14 +10,9 @@ class DeviceInfoScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppPage( return AppPage(
child: Center( title: const Text('Coming soon!'),
child: Column( centered: true,
mainAxisAlignment: MainAxisAlignment.center, child: const Text('This page intentionally left blank (for now)'),
children: const [
Text('This page intentionally left blank (for now)'),
],
),
),
); );
} }
} }

View File

@ -28,7 +28,7 @@ class MainPage extends ConsumerWidget {
switch (app) { switch (app) {
case Application.oath: case Application.oath:
return OathScreen(deviceData); return OathScreen(deviceData.node.path);
case Application.management: case Application.management:
return ManagementScreen(deviceData); return ManagementScreen(deviceData);
case Application.fido: case Application.fido:

View File

@ -58,22 +58,21 @@ class NoDeviceScreen extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return AppPage( return AppPage(
child: Center( centered: true,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: node?.map(usbYubiKey: (node) { children: node?.map(usbYubiKey: (node) {
return _buildUsbPid(context, ref, node.pid); return _buildUsbPid(context, ref, node.pid);
}, nfcReader: (node) { }, nfcReader: (node) {
return const [ return const [
DeviceAvatar(child: Icon(Icons.wifi)), DeviceAvatar(child: Icon(Icons.wifi)),
Text('Place your YubiKey on the NFC reader'), Text('Place your YubiKey on the NFC reader'),
]; ];
}) ?? }) ??
const [ const [
DeviceAvatar(child: Icon(Icons.usb)), DeviceAvatar(child: Icon(Icons.usb)),
Text('Insert your YubiKey'), Text('Insert your YubiKey'),
], ],
),
), ),
); );
} }