Change AppPage view.

This commit is contained in:
Elias Bonnici 2024-01-15 15:58:40 +01:00 committed by Dain Nilsson
parent d759b57b4d
commit ffdac2b443
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
11 changed files with 131 additions and 117 deletions

View File

@ -27,9 +27,8 @@ import '../state.dart';
import 'message_page.dart';
class AppFailurePage extends ConsumerWidget {
final Widget? title;
final Object cause;
const AppFailurePage({this.title, required this.cause, super.key}) : super();
const AppFailurePage({required this.cause, super.key}) : super();
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -99,7 +98,6 @@ class AppFailurePage extends ConsumerWidget {
}
return MessagePage(
title: title,
graphic: graphic,
header: header,
message: message,

View File

@ -32,7 +32,7 @@ final _navKey = GlobalKey();
final _navExpandedKey = GlobalKey();
class AppPage extends StatelessWidget {
final Widget? title;
final String? title;
final Widget Function(BuildContext context, bool expanded) builder;
final Widget Function(BuildContext context)? detailViewBuilder;
final List<Widget> actions;
@ -150,9 +150,29 @@ class AppPage extends StatelessWidget {
));
}
List<Widget> _buildTitle(BuildContext context) {
return title != null
? [
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(title!,
style: Theme.of(context).textTheme.displaySmall!.copyWith(
color: Theme.of(context).colorScheme.primary)),
),
),
const SizedBox(
height: 16,
)
]
: [];
}
Widget _buildMainContent(BuildContext context, bool expanded) {
final content = Column(
children: [
..._buildTitle(context),
builder(context, expanded),
if (actions.isNotEmpty)
Align(
@ -248,10 +268,7 @@ class AppPage extends StatelessWidget {
return Scaffold(
key: scaffoldGlobalKey,
appBar: AppBar(
title: title,
titleSpacing: hasDrawer ? 2 : 8,
centerTitle: true,
titleTextStyle: Theme.of(context).textTheme.titleLarge,
scrolledUnderElevation: 0.0,
leadingWidth: hasRail ? 84 : null,
leading: hasRail
? const Row(

View File

@ -21,7 +21,6 @@ import 'package:flutter/material.dart';
import 'app_page.dart';
class MessagePage extends StatelessWidget {
final Widget? title;
final Widget? graphic;
final String? header;
final String? message;
@ -35,7 +34,6 @@ class MessagePage extends StatelessWidget {
const MessagePage({
super.key,
this.title,
this.graphic,
this.header,
this.message,
@ -50,7 +48,6 @@ class MessagePage extends StatelessWidget {
@override
Widget build(BuildContext context) => AppPage(
title: title,
centered: true,
actions: actions,
keyActionsBuilder: keyActionsBuilder,

View File

@ -36,7 +36,7 @@ class FidoScreen extends ConsumerWidget {
final l10n = AppLocalizations.of(context)!;
return ref.watch(fidoStateProvider(deviceData.node.path)).when(
loading: () => AppPage(
title: Text(l10n.s_webauthn),
title: l10n.s_webauthn,
centered: true,
delayedContent: true,
builder: (context, _) => const CircularProgressIndicator(),
@ -47,7 +47,6 @@ class FidoScreen extends ConsumerWidget {
0;
if (Capability.fido2.value & supported == 0) {
return MessagePage(
title: Text(l10n.s_webauthn),
graphic: Icon(Icons.security,
size: 96, color: Theme.of(context).colorScheme.primary),
header: l10n.l_ready_to_use,
@ -59,14 +58,12 @@ class FidoScreen extends ConsumerWidget {
0;
if (Capability.fido2.value & enabled == 0) {
return MessagePage(
title: Text(l10n.s_webauthn),
header: l10n.s_fido_disabled,
message: l10n.l_webauthn_req_fido2,
);
}
return AppFailurePage(
title: Text(l10n.s_webauthn),
cause: error,
);
},

View File

@ -44,7 +44,6 @@ class FidoLockedPage extends ConsumerWidget {
if (!state.hasPin) {
if (state.bioEnroll != null) {
return MessagePage(
title: Text(l10n.s_webauthn),
graphic: Icon(Icons.fingerprint,
size: 96, color: Theme.of(context).colorScheme.primary),
header: l10n.s_no_fingerprints,
@ -54,7 +53,6 @@ class FidoLockedPage extends ConsumerWidget {
);
} else {
return MessagePage(
title: Text(l10n.s_webauthn),
graphic: Icon(Icons.security,
size: 96, color: Theme.of(context).colorScheme.primary),
header: state.credMgmt
@ -69,7 +67,6 @@ class FidoLockedPage extends ConsumerWidget {
if (!state.credMgmt && state.bioEnroll == null) {
return MessagePage(
title: Text(l10n.s_webauthn),
graphic: Icon(Icons.security,
size: 96, color: Theme.of(context).colorScheme.primary),
header: l10n.l_ready_to_use,
@ -81,7 +78,6 @@ class FidoLockedPage extends ConsumerWidget {
if (state.forcePinChange) {
return MessagePage(
title: Text(l10n.s_webauthn),
header: l10n.s_pin_change_required,
message: l10n.l_pin_change_required_desc,
keyActionsBuilder: hasActions ? _buildActions : null,
@ -90,7 +86,7 @@ class FidoLockedPage extends ConsumerWidget {
}
return AppPage(
title: Text(l10n.s_webauthn),
title: l10n.s_webauthn,
keyActionsBuilder: hasActions ? _buildActions : null,
builder: (context, _) => Column(
children: [
@ -157,7 +153,7 @@ class _PinEntryFormState extends ConsumerState<_PinEntryForm> {
final l10n = AppLocalizations.of(context)!;
final noFingerprints = widget._state.bioEnroll == false;
return Padding(
padding: const EdgeInsets.only(left: 18.0, right: 18, top: 32),
padding: const EdgeInsets.only(left: 18.0, right: 18, top: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

View File

@ -167,7 +167,7 @@ class _FidoUnlockedPageState extends ConsumerState<FidoUnlockedPage> {
}),
},
builder: (context) => AppPage(
title: Text(l10n.s_webauthn),
title: l10n.s_webauthn,
detailViewBuilder: switch (_selected) {
FidoCredential credential => (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
@ -290,7 +290,6 @@ class _FidoUnlockedPageState extends ConsumerState<FidoUnlockedPage> {
if (widget.state.bioEnroll != null) {
return MessagePage(
title: Text(l10n.s_webauthn),
graphic: Icon(Icons.fingerprint,
size: 96, color: Theme.of(context).colorScheme.primary),
header: l10n.s_no_fingerprints,
@ -304,7 +303,6 @@ class _FidoUnlockedPageState extends ConsumerState<FidoUnlockedPage> {
}
return MessagePage(
title: Text(l10n.s_webauthn),
graphic: Icon(Icons.security,
size: 96, color: Theme.of(context).colorScheme.primary),
header: l10n.l_no_discoverable_accounts,
@ -317,7 +315,7 @@ class _FidoUnlockedPageState extends ConsumerState<FidoUnlockedPage> {
}
Widget _buildLoadingPage(BuildContext context) => AppPage(
title: Text(AppLocalizations.of(context)!.s_webauthn),
title: AppLocalizations.of(context)!.s_webauthn,
centered: true,
delayedContent: true,
builder: (context, _) => const CircularProgressIndicator(),

View File

@ -18,7 +18,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../widgets/list_title.dart';
import '../models.dart';
import '../state.dart';
import 'account_view.dart';
@ -50,7 +49,6 @@ class AccountList extends ConsumerWidget {
policy: WidgetOrderTraversalPolicy(),
child: Column(
children: [
if (pinnedCreds.isNotEmpty) ListTitle(l10n.s_pinned),
...pinnedCreds.map(
(entry) => AccountView(
entry.credential,
@ -58,7 +56,11 @@ class AccountList extends ConsumerWidget {
selected: entry.credential == selected,
),
),
if (creds.isNotEmpty) ListTitle(l10n.s_accounts),
if (pinnedCreds.isNotEmpty)
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Divider(),
),
...creds.map(
(entry) => AccountView(
entry.credential,

View File

@ -54,15 +54,12 @@ class OathScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
return ref.watch(oathStateProvider(devicePath)).when(
loading: () => MessagePage(
title: Text(l10n.s_authenticator),
graphic: const CircularProgressIndicator(),
loading: () => const MessagePage(
graphic: CircularProgressIndicator(),
delayedContent: true,
),
error: (error, _) => AppFailurePage(
title: Text(l10n.s_authenticator),
cause: error,
),
data: (oathState) => oathState.locked
@ -82,12 +79,12 @@ class _LockedView extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final hasActions = ref.watch(featureProvider)(features.actions);
return AppPage(
title: Text(AppLocalizations.of(context)!.s_authenticator),
title: AppLocalizations.of(context)!.s_accounts,
keyActionsBuilder: hasActions
? (context) => oathBuildActions(context, devicePath, oathState, ref)
: null,
builder: (context, _) => Padding(
padding: const EdgeInsets.symmetric(vertical: 18),
padding: const EdgeInsets.symmetric(vertical: 8),
child: UnlockForm(
devicePath,
keystore: oathState.keystore,
@ -158,7 +155,6 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
if (numCreds == 0) {
return MessagePage(
title: Text(l10n.s_authenticator),
key: keys.noAccountsView,
graphic: Icon(Icons.people,
size: 96, color: Theme.of(context).colorScheme.primary),
@ -232,66 +228,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
}),
},
builder: (context) => AppPage(
title: Focus(
canRequestFocus: false,
onKeyEvent: (node, event) {
if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
node.focusInDirection(TraversalDirection.down);
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: Builder(builder: (context) {
final textTheme = Theme.of(context).textTheme;
return AppTextFormField(
key: keys.searchAccountsField,
controller: searchController,
focusNode: searchFocus,
// Use the default style, but with a smaller font size:
style: textTheme.titleMedium
?.copyWith(fontSize: textTheme.titleSmall?.fontSize),
decoration: AppInputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32),
borderSide: BorderSide(
width: 0,
style: searchFocus.hasFocus
? BorderStyle.solid
: BorderStyle.none,
),
),
contentPadding: const EdgeInsets.all(16),
fillColor: Theme.of(context).hoverColor,
filled: true,
hintText: l10n.s_search_accounts,
isDense: true,
prefixIcon: const Padding(
padding: EdgeInsetsDirectional.only(start: 8.0),
child: Icon(Icons.search_outlined),
),
suffixIcon: searchController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
iconSize: 16,
onPressed: () {
searchController.clear();
ref.read(searchProvider.notifier).setFilter('');
setState(() {});
},
)
: null,
),
onChanged: (value) {
ref.read(searchProvider.notifier).setFilter(value);
setState(() {});
},
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {
Focus.of(context).focusInDirection(TraversalDirection.down);
},
);
}),
),
title: l10n.s_accounts,
keyActionsBuilder: hasActions
? (context) => oathBuildActions(
context,
@ -397,15 +334,91 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
return null;
}),
},
child: Consumer(
builder: (context, ref, _) {
return AccountList(
ref.watch(credentialListProvider(widget.devicePath)) ??
[],
expanded: expanded,
selected: _selected,
);
},
child: Column(
children: [
Focus(
canRequestFocus: false,
onKeyEvent: (node, event) {
if (event.logicalKey ==
LogicalKeyboardKey.arrowDown) {
node.focusInDirection(TraversalDirection.down);
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: Builder(builder: (context) {
final textTheme = Theme.of(context).textTheme;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
child: AppTextFormField(
key: keys.searchAccountsField,
controller: searchController,
focusNode: searchFocus,
// Use the default style, but with a smaller font size:
style: textTheme.titleMedium?.copyWith(
fontSize: textTheme.titleSmall?.fontSize),
decoration: AppInputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32),
borderSide: BorderSide(
width: 0,
style: searchFocus.hasFocus
? BorderStyle.solid
: BorderStyle.none,
),
),
contentPadding: const EdgeInsets.all(16),
fillColor: Theme.of(context).hoverColor,
filled: true,
hintText: l10n.s_search_accounts,
isDense: true,
prefixIcon: const Padding(
padding:
EdgeInsetsDirectional.only(start: 8.0),
child: Icon(Icons.search_outlined),
),
suffixIcon: searchController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
iconSize: 16,
onPressed: () {
searchController.clear();
ref
.read(searchProvider.notifier)
.setFilter('');
setState(() {});
},
)
: null,
),
onChanged: (value) {
ref
.read(searchProvider.notifier)
.setFilter(value);
setState(() {});
},
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {
Focus.of(context)
.focusInDirection(TraversalDirection.down);
},
),
);
}),
),
Consumer(
builder: (context, ref, _) {
return AccountList(
ref.watch(credentialListProvider(
widget.devicePath)) ??
[],
expanded: expanded,
selected: _selected,
);
},
)
],
),
)
: const CircularProgressIndicator();

View File

@ -66,7 +66,7 @@ class _UnlockFormState extends ConsumerState<UnlockForm> {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 18.0, right: 18, top: 14),
padding: const EdgeInsets.only(left: 18.0, right: 18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

View File

@ -54,13 +54,11 @@ class _OtpScreenState extends ConsumerState<OtpScreen> {
final l10n = AppLocalizations.of(context)!;
final hasFeature = ref.watch(featureProvider);
return ref.watch(otpStateProvider(widget.devicePath)).when(
loading: () => MessagePage(
title: Text(l10n.s_slots),
graphic: const CircularProgressIndicator(),
loading: () => const MessagePage(
graphic: CircularProgressIndicator(),
delayedContent: true,
),
error: (error, _) =>
AppFailurePage(title: Text(l10n.s_slots), cause: error),
error: (error, _) => AppFailurePage(cause: error),
data: (otpState) {
final selected = _selected != null
? otpState.slots.firstWhere((e) => e.slot == _selected)
@ -92,7 +90,7 @@ class _OtpScreenState extends ConsumerState<OtpScreen> {
}),
},
child: AppPage(
title: Text(l10n.s_slots),
title: l10n.s_slots,
detailViewBuilder: selected != null
? (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,

View File

@ -56,13 +56,11 @@ class _PivScreenState extends ConsumerState<PivScreen> {
final l10n = AppLocalizations.of(context)!;
final hasFeature = ref.watch(featureProvider);
return ref.watch(pivStateProvider(widget.devicePath)).when(
loading: () => MessagePage(
title: Text(l10n.s_certificates),
graphic: const CircularProgressIndicator(),
loading: () => const MessagePage(
graphic: CircularProgressIndicator(),
delayedContent: true,
),
error: (error, _) => AppFailurePage(
title: Text(l10n.s_certificates),
cause: error,
),
data: (pivState) {
@ -105,7 +103,7 @@ class _PivScreenState extends ConsumerState<PivScreen> {
),
},
child: AppPage(
title: Text(l10n.s_certificates),
title: l10n.s_certificates,
detailViewBuilder: selected != null
? (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,