diff --git a/lib/screens/login/login_screen.dart b/lib/screens/login/login_screen.dart index 8ad974e..99260b7 100644 --- a/lib/screens/login/login_screen.dart +++ b/lib/screens/login/login_screen.dart @@ -14,6 +14,7 @@ import 'package:fladder/screens/shared/animated_fade_size.dart'; import 'package:fladder/screens/shared/fladder_logo.dart'; import 'package:fladder/util/adaptive_layout/adaptive_layout.dart'; import 'package:fladder/widgets/keyboard/slide_in_keyboard.dart'; +import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart'; import 'package:fladder/widgets/navigation_scaffold/components/fladder_app_bar.dart'; @RoutePage() @@ -53,20 +54,22 @@ class _LoginPageState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.end, spacing: 16, children: [ - if (!AdaptiveLayout.of(context).isDesktop) - FloatingActionButton( + if (AdaptiveLayout.of(context).isDesktop) + AdaptiveFab( + context: context, key: const Key("edit_user_button"), heroTag: "edit_user_button", backgroundColor: editUsersMode ? Theme.of(context).colorScheme.errorContainer : null, child: const Icon(IconsaxPlusLinear.edit_2), onPressed: () => setState(() => editUsersMode = !editUsersMode), - ), - FloatingActionButton( + ).normal, + AdaptiveFab( + context: context, key: const Key("new_user_button"), heroTag: "new_user_button", child: const Icon(IconsaxPlusLinear.add_square), onPressed: () => ref.read(authProvider.notifier).addNewUser(), - ), + ).normal, ], ), _ => null, diff --git a/lib/screens/shared/media/expanding_overview.dart b/lib/screens/shared/media/expanding_overview.dart index 4c2137c..80c396d 100644 --- a/lib/screens/shared/media/expanding_overview.dart +++ b/lib/screens/shared/media/expanding_overview.dart @@ -4,8 +4,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:iconsax_plus/iconsax_plus.dart'; +import 'package:fladder/screens/shared/flat_button.dart'; +import 'package:fladder/util/adaptive_layout/adaptive_layout.dart'; import 'package:fladder/util/localization_helper.dart'; -import 'package:fladder/util/sticky_header_text.dart'; +import 'package:fladder/widgets/shared/ensure_visible.dart'; class ExpandingOverview extends ConsumerStatefulWidget { final String text; @@ -29,58 +31,68 @@ class _ExpandingOverviewState extends ConsumerState { final color = Theme.of(context).colorScheme.onSurface; const int maxLength = 200; final bool canExpand = widget.text.length > maxLength; - return AnimatedSize( - duration: const Duration(milliseconds: 250), - alignment: Alignment.topCenter, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - StickyHeaderText( - label: context.localized.overview, - ), - ShaderMask( - shaderCallback: (bounds) => LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - stops: const [0, 1], - colors: [ - color, - color.withValues( - alpha: !canExpand - ? 1 - : expanded - ? 1 - : 0), - ], - ).createShader(bounds), - child: HtmlWidget( - widget.text.substring(0, !expanded ? maxLength.clamp(0, widget.text.length) : widget.text.length - 1), - textStyle: Theme.of(context).textTheme.bodyLarge, + final isDpad = AdaptiveLayout.inputDeviceOf(context) == InputDevice.dPad; + return FlatButton( + onTap: canExpand && isDpad ? toggleState : null, + onFocusChange: (value) { + if (value) { + context.ensureVisible(); + } + }, + child: AnimatedSize( + duration: const Duration(milliseconds: 250), + alignment: Alignment.topCenter, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.localized.overview, + style: Theme.of(context).textTheme.titleLarge, ), - ), - if (canExpand) ...{ - const SizedBox(height: 16), - Align( - alignment: Alignment.center, - child: Transform.translate( - offset: Offset(0, expanded ? 0 : -15), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 250), - child: expanded - ? IconButton.filledTonal( - onPressed: toggleState, - icon: const Icon(IconsaxPlusLinear.arrow_up_1), - ) - : IconButton.filledTonal( - onPressed: toggleState, - icon: const Icon(IconsaxPlusLinear.arrow_down), - ), - ), + ShaderMask( + shaderCallback: (bounds) => LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0, 1], + colors: [ + color, + color.withValues( + alpha: !canExpand + ? 1 + : expanded + ? 1 + : 0), + ], + ).createShader(bounds), + child: HtmlWidget( + widget.text.substring(0, !expanded ? maxLength.clamp(0, widget.text.length) : widget.text.length - 1), + textStyle: Theme.of(context).textTheme.bodyLarge, ), ), - }, - ], + if (canExpand && !isDpad) ...{ + const SizedBox(height: 16), + Align( + alignment: Alignment.center, + child: Transform.translate( + offset: Offset(0, expanded ? 0 : -15), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + child: expanded + ? IconButton.filledTonal( + onPressed: toggleState, + icon: const Icon(IconsaxPlusLinear.arrow_up_1), + ) + : IconButton.filledTonal( + onPressed: toggleState, + icon: const Icon(IconsaxPlusLinear.arrow_down), + ), + ), + ), + ), + }, + ], + ), ), ); } diff --git a/lib/widgets/navigation_scaffold/components/adaptive_fab.dart b/lib/widgets/navigation_scaffold/components/adaptive_fab.dart index 8af9ba9..7df9bf3 100644 --- a/lib/widgets/navigation_scaffold/components/adaptive_fab.dart +++ b/lib/widgets/navigation_scaffold/components/adaptive_fab.dart @@ -3,23 +3,37 @@ import 'package:flutter/material.dart'; class AdaptiveFab { final BuildContext context; final String title; + final String? heroTag; final Widget child; final Function() onPressed; + final Color? backgroundColor; final Key? key; AdaptiveFab({ required this.context, this.title = '', + this.heroTag, required this.child, required this.onPressed, this.key, + this.backgroundColor, }); - FloatingActionButton get normal { - return FloatingActionButton( - key: key, - onPressed: onPressed, - tooltip: title, - child: child, + Widget get normal { + return Hero( + tag: heroTag ?? UniqueKey(), + child: IconButton.filledTonal( + iconSize: 26, + key: key, + tooltip: title, + onPressed: onPressed, + style: IconButton.styleFrom( + backgroundColor: backgroundColor, + ), + icon: Padding( + padding: const EdgeInsets.all(8.0), + child: child, + ), + ), ); } @@ -34,6 +48,7 @@ class AdaptiveFab { onPressed: onPressed, style: FilledButton.styleFrom( padding: const EdgeInsets.all(16), + backgroundColor: backgroundColor, ), child: Row( spacing: 16,