#437 tv: toggle button state

This commit is contained in:
Thibault Deckers 2022-12-15 18:59:20 +01:00
parent 1cba919385
commit da751190c5
47 changed files with 570 additions and 289 deletions

View file

@ -114,6 +114,12 @@ class EntryActions {
EntryAction.videoSettings, EntryAction.videoSettings,
]; ];
static const videoPlayback = [
EntryAction.videoReplay10,
EntryAction.videoTogglePlay,
EntryAction.videoSkip10,
];
static const commonMetadataActions = [ static const commonMetadataActions = [
EntryAction.editDate, EntryAction.editDate,
EntryAction.editLocation, EntryAction.editLocation,

View file

@ -2,7 +2,10 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
enum ShareAction { imageOnly, videoOnly, } enum ShareAction {
imageOnly,
videoOnly,
}
extension ExtraShareAction on ShareAction { extension ExtraShareAction on ShareAction {
String getText(BuildContext context) { String getText(BuildContext context) {

View file

@ -15,7 +15,7 @@ import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';

View file

@ -5,7 +5,7 @@ import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/basic/link_chip.dart'; import 'package:aves/widgets/common/basic/link_chip.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -21,18 +21,18 @@ import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/collection/entry_set_action_delegate.dart'; import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
import 'package:aves/widgets/collection/filter_bar.dart'; import 'package:aves/widgets/collection/filter_bar.dart';
import 'package:aves/widgets/collection/query_bar.dart'; import 'package:aves/widgets/collection/query_bar.dart';
import 'package:aves/widgets/common/action_controls/togglers/favourite.dart';
import 'package:aves/widgets/common/action_controls/togglers/title_search.dart';
import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart'; import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar/app_bar_title.dart'; import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
import 'package:aves/widgets/common/app_bar/favourite_toggler.dart';
import 'package:aves/widgets/common/app_bar/title_search_toggler.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_app_bar.dart'; import 'package:aves/widgets/common/identity/aves_app_bar.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/common/search/route.dart'; import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/dialogs/tile_view_dialog.dart'; import 'package:aves/widgets/dialogs/tile_view_dialog.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart';
import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves/widgets/search/search_delegate.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -176,7 +176,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
children: [ children: [
if (isTelevision) if (isTelevision)
SizedBox( SizedBox(
height: ActionButton.getTelevisionButtonHeight(context), height: CaptionedButton.getTelevisionButtonHeight(context),
child: ListView( child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
@ -215,7 +215,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
double get appBarContentHeight { double get appBarContentHeight {
double height = kToolbarHeight; double height = kToolbarHeight;
if (device.isTelevision) { if (device.isTelevision) {
height += ActionButton.getTelevisionButtonHeight(context); height += CaptionedButton.getTelevisionButtonHeight(context);
} }
if (showFilterBar) { if (showFilterBar) {
height += FilterBar.preferredHeight; height += FilterBar.preferredHeight;
@ -339,12 +339,10 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
...EntrySetActions.general, ...EntrySetActions.general,
...isSelecting ? EntrySetActions.pageSelection : EntrySetActions.pageBrowsing, ...isSelecting ? EntrySetActions.pageSelection : EntrySetActions.pageBrowsing,
].where(isVisible).map((action) { ].where(isVisible).map((action) {
// TODO TLAD [tv] togglers cf `_toIconActionButton`
final enabled = canApply(action); final enabled = canApply(action);
return ActionButton( return CaptionedButton(
text: action.getText(context), iconButton: _buildButtonIcon(context, action, enabled: enabled, selection: selection),
icon: action.getIcon(), captionText: _buildButtonCaption(context, action, enabled: enabled),
enabled: enabled,
onPressed: enabled ? () => _onActionSelected(action) : null, onPressed: enabled ? () => _onActionSelected(action) : null,
); );
}).toList(); }).toList();
@ -364,7 +362,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
final browsingQuickActions = settings.collectionBrowsingQuickActions; final browsingQuickActions = settings.collectionBrowsingQuickActions;
final selectionQuickActions = isTrash ? [EntrySetAction.delete, EntrySetAction.restore] : settings.collectionSelectionQuickActions; final selectionQuickActions = isTrash ? [EntrySetAction.delete, EntrySetAction.restore] : settings.collectionSelectionQuickActions;
final quickActionButtons = (isSelecting ? selectionQuickActions : browsingQuickActions).where(isVisible).map( final quickActionButtons = (isSelecting ? selectionQuickActions : browsingQuickActions).where(isVisible).map(
(action) => _toIconActionButton(action, enabled: canApply(action), selection: selection), (action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
); );
return [ return [
@ -426,7 +424,12 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
// key is expected by test driver (e.g. 'menu-configureView', 'menu-map') // key is expected by test driver (e.g. 'menu-configureView', 'menu-map')
Key _getActionKey(EntrySetAction action) => Key('menu-${action.name}'); Key _getActionKey(EntrySetAction action) => Key('menu-${action.name}');
Widget _toIconActionButton(EntrySetAction action, {required bool enabled, required Selection<AvesEntry> selection}) { Widget _buildButtonIcon(
BuildContext context,
EntrySetAction action, {
required bool enabled,
required Selection<AvesEntry> selection,
}) {
final onPressed = enabled ? () => _onActionSelected(action) : null; final onPressed = enabled ? () => _onActionSelected(action) : null;
switch (action) { switch (action) {
case EntrySetAction.toggleTitleSearch: case EntrySetAction.toggleTitleSearch:
@ -455,6 +458,24 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
} }
} }
Widget _buildButtonCaption(
BuildContext context,
EntrySetAction action, {
required bool enabled,
}) {
switch (action) {
case EntrySetAction.toggleTitleSearch:
return TitleSearchTogglerCaption(
enabled: enabled,
);
default:
return CaptionedButtonText(
text: action.getText(context),
enabled: enabled,
);
}
}
PopupMenuItem<EntrySetAction> _toMenuItem(EntrySetAction action, {required bool enabled, required Selection<AvesEntry> selection}) { PopupMenuItem<EntrySetAction> _toMenuItem(EntrySetAction action, {required bool enabled, required Selection<AvesEntry> selection}) {
late Widget child; late Widget child;
switch (action) { switch (action) {

View file

@ -33,7 +33,7 @@ import 'package:aves/widgets/common/grid/sections/section_layout.dart';
import 'package:aves/widgets/common/grid/selector.dart'; import 'package:aves/widgets/common/grid/selector.dart';
import 'package:aves/widgets/common/grid/sliver.dart'; import 'package:aves/widgets/common/grid/sliver.dart';
import 'package:aves/widgets/common/grid/theme.dart'; import 'package:aves/widgets/common/grid/theme.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/common/identity/scroll_thumb.dart'; import 'package:aves/widgets/common/identity/scroll_thumb.dart';
import 'package:aves/widgets/common/providers/tile_extent_controller_provider.dart'; import 'package:aves/widgets/common/providers/tile_extent_controller_provider.dart';

View file

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/route_layout.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/route_layout.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -2,7 +2,7 @@ import 'dart:async';
import 'dart:ui'; import 'dart:ui';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/quick_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/quick_chooser.dart';
import 'package:aves_ui/aves_ui.dart'; import 'package:aves_ui/aves_ui.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -3,9 +3,9 @@ import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/album_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/album_chooser.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart'; import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';

View file

@ -1,6 +1,6 @@
import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/rate_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/rate_chooser.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RateButton extends ChooserQuickButton<int> { class RateButton extends ChooserQuickButton<int> {

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/quick_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/quick_chooser.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RateQuickChooser extends StatefulWidget { class RateQuickChooser extends StatefulWidget {

View file

@ -1,8 +1,8 @@
import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/actions/share_actions.dart'; import 'package:aves/model/actions/share_actions.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/share_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/share_chooser.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ShareButton extends ChooserQuickButton<ShareAction> { class ShareButton extends ChooserQuickButton<ShareAction> {

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/actions/share_actions.dart'; import 'package:aves/model/actions/share_actions.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -3,9 +3,9 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/tag_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/tag_chooser.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart'; import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -5,6 +5,7 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/sweeper.dart'; import 'package:aves/widgets/common/fx/sweeper.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -49,6 +50,7 @@ class _FavouriteTogglerState extends State<FavouriteToggler> {
@override @override
void dispose() { void dispose() {
favourites.removeListener(_onChanged); favourites.removeListener(_onChanged);
isFavouriteNotifier.dispose();
super.dispose(); super.dispose();
} }
@ -94,3 +96,60 @@ class _FavouriteTogglerState extends State<FavouriteToggler> {
isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite);
} }
} }
class FavouriteTogglerCaption extends StatefulWidget {
final Set<AvesEntry> entries;
final bool enabled;
const FavouriteTogglerCaption({
super.key,
required this.entries,
required this.enabled,
});
@override
State<FavouriteTogglerCaption> createState() => _FavouriteTogglerCaptionState();
}
class _FavouriteTogglerCaptionState extends State<FavouriteTogglerCaption> {
final ValueNotifier<bool> isFavouriteNotifier = ValueNotifier(false);
Set<AvesEntry> get entries => widget.entries;
@override
void initState() {
super.initState();
favourites.addListener(_onChanged);
_onChanged();
}
@override
void didUpdateWidget(covariant FavouriteTogglerCaption oldWidget) {
super.didUpdateWidget(oldWidget);
_onChanged();
}
@override
void dispose() {
favourites.removeListener(_onChanged);
isFavouriteNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: isFavouriteNotifier,
builder: (context, isFavourite, child) {
return CaptionedButtonText(
text: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite,
enabled: widget.enabled,
);
},
);
}
void _onChanged() {
isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite);
}
}

View file

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/viewer/video/controller.dart'; import 'package:aves/widgets/viewer/video/controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -47,3 +48,34 @@ class MuteToggler extends StatelessWidget {
); );
} }
} }
class MuteTogglerCaption extends StatelessWidget {
final AvesVideoController? controller;
final bool enabled;
const MuteTogglerCaption({
super.key,
required this.controller,
required this.enabled,
});
bool get isMuted => controller?.isMuted ?? false;
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false),
builder: (context, canDo, child) {
return StreamBuilder<double>(
stream: controller?.volumeStream ?? Stream.value(1.0),
builder: (context, snapshot) {
return CaptionedButtonText(
text: isMuted ? context.l10n.videoActionUnmute : context.l10n.videoActionMute,
enabled: canDo && enabled,
);
},
);
},
);
}
}

View file

@ -4,6 +4,7 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/viewer/video/controller.dart'; import 'package:aves/widgets/viewer/video/controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -98,3 +99,29 @@ class _PlayTogglerState extends State<PlayToggler> with SingleTickerProviderStat
} }
} }
} }
class PlayTogglerCaption extends StatelessWidget {
final AvesVideoController? controller;
final bool enabled;
const PlayTogglerCaption({
super.key,
required this.controller,
required this.enabled,
});
bool get isPlaying => controller?.isPlaying ?? false;
@override
Widget build(BuildContext context) {
return StreamBuilder<VideoStatus>(
stream: controller?.statusStream ?? Stream.value(VideoStatus.idle),
builder: (context, snapshot) {
return CaptionedButtonText(
text: isPlaying ? context.l10n.videoActionPause : context.l10n.videoActionPlay,
enabled: enabled,
);
},
);
}
}

View file

@ -1,7 +1,10 @@
import 'package:aves/model/query.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class TitleSearchToggler extends StatelessWidget { class TitleSearchToggler extends StatelessWidget {
final bool queryEnabled, isMenuItem; final bool queryEnabled, isMenuItem;
@ -30,3 +33,26 @@ class TitleSearchToggler extends StatelessWidget {
); );
} }
} }
class TitleSearchTogglerCaption extends StatelessWidget {
final bool enabled;
const TitleSearchTogglerCaption({
super.key,
required this.enabled,
});
@override
Widget build(BuildContext context) {
// `Query` may not be available during hero
return Selector<Query?, bool>(
selector: (context, query) => query?.enabled ?? false,
builder: (context, queryEnabled, child) {
return CaptionedButtonText(
text: queryEnabled ? context.l10n.collectionActionHideTitleSearch : context.l10n.collectionActionShowTitleSearch,
enabled: enabled,
);
},
);
}
}

View file

@ -0,0 +1,105 @@
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class CaptionedButton extends StatelessWidget {
final Animation<double> scale;
final Widget captionText;
final Widget iconButton;
final bool showCaption;
final VoidCallback? onPressed;
CaptionedButton({
super.key,
this.scale = kAlwaysCompleteAnimation,
Widget? icon,
Widget? iconButton,
String? caption,
Widget? captionText,
this.showCaption = true,
required this.onPressed,
}) : assert(icon != null || iconButton != null),
assert(caption != null || captionText != null),
iconButton = iconButton ?? IconButton(icon: icon!, onPressed: onPressed),
captionText = captionText ?? CaptionedButtonText(text: caption!, enabled: onPressed != null);
static const double padding = 8;
@override
Widget build(BuildContext context) {
return SizedBox(
width: _width(context),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: padding),
OverlayButton(
scale: scale,
child: iconButton,
),
if (showCaption) ...[
const SizedBox(height: padding),
ScaleTransition(
scale: scale,
child: captionText,
),
],
const SizedBox(height: padding),
],
),
);
}
static double _width(BuildContext context) => OverlayButton.getSize(context) + padding * 2;
static Size getSize(BuildContext context, String text, {required bool showCaption}) {
final width = _width(context);
var height = width;
if (showCaption) {
final para = RenderParagraph(
TextSpan(text: text, style: CaptionedButtonText.textStyle(context)),
textDirection: TextDirection.ltr,
textScaleFactor: MediaQuery.textScaleFactorOf(context),
maxLines: CaptionedButtonText.maxLines,
)..layout(const BoxConstraints(), parentUsesSize: true);
height += para.getMaxIntrinsicHeight(width) + padding;
}
return Size(width, height);
}
static double getTelevisionButtonHeight(BuildContext context) {
final text = 'whatever' * 42;
return CaptionedButton.getSize(context, text, showCaption: true).height;
}
}
class CaptionedButtonText extends StatelessWidget {
final String text;
final bool enabled;
static const int maxLines = 2;
const CaptionedButtonText({
super.key,
required this.text,
required this.enabled,
});
@override
Widget build(BuildContext context) {
var style = textStyle(context);
if (!enabled) {
style = style.copyWith(color: style.color!.withOpacity(.2));
}
return Text(
text,
style: style,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: maxLines,
);
}
static TextStyle textStyle(BuildContext context) => Theme.of(context).textTheme.bodySmall!;
}

View file

@ -13,13 +13,13 @@ import 'package:aves/utils/change_notifier.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/utils/math_utils.dart'; import 'package:aves/utils/math_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/common/map/attribution.dart'; import 'package:aves/widgets/common/map/attribution.dart';
import 'package:aves/widgets/common/map/buttons/panel.dart'; import 'package:aves/widgets/common/map/buttons/panel.dart';
import 'package:aves/widgets/common/map/decorator.dart'; import 'package:aves/widgets/common/map/decorator.dart';
import 'package:aves/widgets/common/map/leaflet/map.dart'; import 'package:aves/widgets/common/map/leaflet/map.dart';
import 'package:aves/widgets/common/thumbnail/image.dart'; import 'package:aves/widgets/common/thumbnail/image.dart';
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart'; import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:aves_map/aves_map.dart'; import 'package:aves_map/aves_map.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fluster/fluster.dart'; import 'package:fluster/fluster.dart';

View file

@ -10,7 +10,7 @@ import 'package:aves/widgets/collection/collection_grid.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/grid/theme.dart'; import 'package:aves/widgets/common/grid/theme.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/common/thumbnail/decorated.dart'; import 'package:aves/widgets/common/thumbnail/decorated.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';

View file

@ -11,7 +11,7 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/utils/debouncer.dart'; import 'package:aves/utils/debouncer.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/common/map/geo_map.dart'; import 'package:aves/widgets/common/map/geo_map.dart';
import 'package:aves/widgets/common/providers/map_theme_provider.dart'; import 'package:aves/widgets/common/providers/map_theme_provider.dart';
import 'package:aves_map/aves_map.dart'; import 'package:aves_map/aves_map.dart';

View file

@ -8,17 +8,17 @@ import 'package:aves/model/query.dart';
import 'package:aves/model/selection.dart'; import 'package:aves/model/selection.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/action_controls/togglers/title_search.dart';
import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart'; import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar/app_bar_title.dart'; import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
import 'package:aves/widgets/common/app_bar/title_search_toggler.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_app_bar.dart'; import 'package:aves/widgets/common/identity/aves_app_bar.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/common/search/route.dart'; import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
import 'package:aves/widgets/filter_grids/common/query_bar.dart'; import 'package:aves/widgets/filter_grids/common/query_bar.dart';
import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves/widgets/search/search_delegate.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -143,7 +143,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
children: [ children: [
if (isTelevision) if (isTelevision)
SizedBox( SizedBox(
height: ActionButton.getTelevisionButtonHeight(context), height: CaptionedButton.getTelevisionButtonHeight(context),
child: ListView( child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
@ -166,7 +166,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
double get appBarContentHeight { double get appBarContentHeight {
double height = kToolbarHeight; double height = kToolbarHeight;
if (device.isTelevision) { if (device.isTelevision) {
height += ActionButton.getTelevisionButtonHeight(context); height += CaptionedButton.getTelevisionButtonHeight(context);
} }
if (context.read<Query>().enabled) { if (context.read<Query>().enabled) {
height += FilterQueryBar.preferredHeight; height += FilterQueryBar.preferredHeight;
@ -281,12 +281,10 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
...ChipSetActions.general, ...ChipSetActions.general,
...isSelecting ? ChipSetActions.selection : ChipSetActions.browsing, ...isSelecting ? ChipSetActions.selection : ChipSetActions.browsing,
].where(isVisible).map((action) { ].where(isVisible).map((action) {
// TODO TLAD [tv] togglers cf `FilterGridAppBar.toMenuItem`
final enabled = canApply(action); final enabled = canApply(action);
return ActionButton( return CaptionedButton(
text: action.getText(context), iconButton: _buildButtonIcon(context, actionDelegate, action, enabled: enabled),
icon: action.getIcon(), captionText: _buildButtonCaption(context, action, enabled: enabled),
enabled: enabled,
onPressed: enabled ? () => _onActionSelected(context, action, actionDelegate) : null, onPressed: enabled ? () => _onActionSelected(context, action, actionDelegate) : null,
); );
}).toList(); }).toList();
@ -302,7 +300,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
final isSelecting = selection.isSelecting; final isSelecting = selection.isSelecting;
final quickActionButtons = (isSelecting ? selectionQuickActions : browsingQuickActions).where(isVisible).map( final quickActionButtons = (isSelecting ? selectionQuickActions : browsingQuickActions).where(isVisible).map(
(action) => _toActionButton(context, actionDelegate, action, enabled: canApply(action)), (action) => _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)),
); );
return [ return [
@ -342,7 +340,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
]; ];
} }
Widget _toActionButton( Widget _buildButtonIcon(
BuildContext context, BuildContext context,
CSAD actionDelegate, CSAD actionDelegate,
ChipSetAction action, { ChipSetAction action, {
@ -370,6 +368,24 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
} }
} }
Widget _buildButtonCaption(
BuildContext context,
ChipSetAction action, {
required bool enabled,
}) {
switch (action) {
case ChipSetAction.toggleTitleSearch:
return TitleSearchTogglerCaption(
enabled: enabled,
);
default:
return CaptionedButtonText(
text: action.getText(context),
enabled: enabled,
);
}
}
void _onActivityChanged() { void _onActivityChanged() {
if (context.read<Selection<FilterGridItem<T>>>().isSelecting) { if (context.read<Selection<FilterGridItem<T>>>().isSelecting) {
_browseToSelectAnimation.forward(); _browseToSelectAnimation.forward();

View file

@ -1,78 +0,0 @@
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ActionButton extends StatelessWidget {
final String text;
final Widget? icon;
final bool enabled, showCaption;
final VoidCallback? onPressed;
const ActionButton({
super.key,
required this.text,
required this.icon,
this.enabled = true,
this.showCaption = true,
this.onPressed,
}) : assert(onPressed == null || enabled);
static const int maxLines = 2;
static const double padding = 8;
@override
Widget build(BuildContext context) {
final textStyle = _textStyle(context);
final _enabled = onPressed != null || enabled;
return SizedBox(
width: _width(context),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: padding),
OverlayButton(
child: IconButton(
icon: icon ?? const SizedBox(),
onPressed: onPressed ?? (_enabled ? () {} : null),
),
),
if (showCaption) ...[
const SizedBox(height: padding),
Text(
text,
style: _enabled ? textStyle : textStyle.copyWith(color: textStyle.color!.withOpacity(.2)),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: maxLines,
),
],
const SizedBox(height: padding),
],
),
);
}
static TextStyle _textStyle(BuildContext context) => Theme.of(context).textTheme.bodySmall!;
static double _width(BuildContext context) => OverlayButton.getSize(context) + padding * 2;
static Size getSize(BuildContext context, String text, {required bool showCaption}) {
final width = _width(context);
var height = width;
if (showCaption) {
final para = RenderParagraph(
TextSpan(text: text, style: _textStyle(context)),
textDirection: TextDirection.ltr,
textScaleFactor: MediaQuery.textScaleFactorOf(context),
maxLines: maxLines,
)..layout(const BoxConstraints(), parentUsesSize: true);
height += para.getMaxIntrinsicHeight(width) + padding;
}
return Size(width, height);
}
static double getTelevisionButtonHeight(BuildContext context) {
final text = 'whatever' * 42;
return ActionButton.getSize(context, text, showCaption: true).height;
}
}

View file

@ -1,7 +1,7 @@
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_button.dart';
import 'package:aves/widgets/settings/common/quick_actions/placeholder.dart'; import 'package:aves/widgets/settings/common/quick_actions/placeholder.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -13,7 +13,7 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
final ValueNotifier<T?> draggedQuickAction; final ValueNotifier<T?> draggedQuickAction;
final ValueNotifier<T?> draggedAvailableAction; final ValueNotifier<T?> draggedAvailableAction;
final bool Function(T? action) removeQuickAction; final bool Function(T? action) removeQuickAction;
final Widget? Function(T action) actionIcon; final Widget Function(T action) actionIcon;
final String Function(BuildContext context, T action) actionText; final String Function(BuildContext context, T action) actionText;
static const double spacing = 8; static const double spacing = 8;
@ -107,11 +107,11 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
bool enabled = true, bool enabled = true,
bool showCaption = true, bool showCaption = true,
}) => }) =>
ActionButton( CaptionedButton(
text: actionText(context, action),
icon: actionIcon(action), icon: actionIcon(action),
enabled: enabled, caption: actionText(context, action),
showCaption: showCaption, showCaption: showCaption,
onPressed: enabled ? () {} : null,
); );
void _setDraggedQuickAction(T? action) => draggedQuickAction.value = action; void _setDraggedQuickAction(T? action) => draggedQuickAction.value = action;
@ -121,7 +121,7 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
void _setPanelHighlight(bool flag) => panelHighlight.value = flag; void _setPanelHighlight(bool flag) => panelHighlight.value = flag;
static double heightFor(BuildContext context, List<String> captions, double width) { static double heightFor(BuildContext context, List<String> captions, double width) {
final buttonSizes = captions.map((v) => ActionButton.getSize(context, v, showCaption: true)); final buttonSizes = captions.map((v) => CaptionedButton.getSize(context, v, showCaption: true));
final actionsPerRun = (width - padding.horizontal + spacing) ~/ (buttonSizes.first.width + spacing); final actionsPerRun = (width - padding.horizontal + spacing) ~/ (buttonSizes.first.width + spacing);
final runCount = (captions.length / actionsPerRun).ceil(); final runCount = (captions.length / actionsPerRun).ceil();
var height = .0; var height = .0;

View file

@ -5,12 +5,12 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/utils/change_notifier.dart'; import 'package:aves/utils/change_notifier.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_button.dart'; import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_panel.dart'; import 'package:aves/widgets/settings/common/quick_actions/action_panel.dart';
import 'package:aves/widgets/settings/common/quick_actions/available_actions.dart'; import 'package:aves/widgets/settings/common/quick_actions/available_actions.dart';
import 'package:aves/widgets/settings/common/quick_actions/placeholder.dart'; import 'package:aves/widgets/settings/common/quick_actions/placeholder.dart';
import 'package:aves/widgets/settings/common/quick_actions/quick_actions.dart'; import 'package:aves/widgets/settings/common/quick_actions/quick_actions.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -19,7 +19,7 @@ import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class QuickActionEditorPage<T extends Object> extends StatelessWidget { class QuickActionEditorPage<T extends Object> extends StatelessWidget {
final String title, bannerText; final String title, bannerText;
final List<List<T>> allAvailableActions; final List<List<T>> allAvailableActions;
final Widget? Function(T action) actionIcon; final Widget Function(T action) actionIcon;
final String Function(BuildContext context, T action) actionText; final String Function(BuildContext context, T action) actionText;
final List<T> Function() load; final List<T> Function() load;
final void Function(List<T> actions) save; final void Function(List<T> actions) save;
@ -58,7 +58,7 @@ class QuickActionEditorPage<T extends Object> extends StatelessWidget {
class QuickActionEditorBody<T extends Object> extends StatefulWidget { class QuickActionEditorBody<T extends Object> extends StatefulWidget {
final String bannerText; final String bannerText;
final List<List<T>> allAvailableActions; final List<List<T>> allAvailableActions;
final Widget? Function(T action) actionIcon; final Widget Function(T action) actionIcon;
final String Function(BuildContext context, T action) actionText; final String Function(BuildContext context, T action) actionText;
final List<T> Function() load; final List<T> Function() load;
final void Function(List<T> actions) save; final void Function(List<T> actions) save;
@ -208,10 +208,11 @@ class _QuickActionEditorBodyState<T extends Object> extends State<QuickActionEdi
insertAction: _insertQuickAction, insertAction: _insertQuickAction,
removeAction: _removeQuickAction, removeAction: _removeQuickAction,
onTargetLeave: _onQuickActionTargetLeave, onTargetLeave: _onQuickActionTargetLeave,
draggableFeedbackBuilder: (action) => ActionButton( draggableFeedbackBuilder: (action) => CaptionedButton(
text: widget.actionText(context, action),
icon: widget.actionIcon(action), icon: widget.actionIcon(action),
caption: widget.actionText(context, action),
showCaption: false, showCaption: false,
onPressed: () {},
), ),
child: _buildQuickActionButton(action, animation), child: _buildQuickActionButton(action, animation),
); );
@ -361,7 +362,7 @@ class _QuickActionEditorBodyState<T extends Object> extends State<QuickActionEdi
padding: const EdgeInsets.symmetric(vertical: _QuickActionEditorBodyState.quickActionVerticalPadding, horizontal: 4), padding: const EdgeInsets.symmetric(vertical: _QuickActionEditorBodyState.quickActionVerticalPadding, horizontal: 4),
child: OverlayButton( child: OverlayButton(
child: IconButton( child: IconButton(
icon: widget.actionIcon(action) ?? const SizedBox(), icon: widget.actionIcon(action),
onPressed: () {}, onPressed: () {},
), ),
), ),

View file

@ -1,5 +1,5 @@
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
enum QuickActionPlacement { header, action, footer } enum QuickActionPlacement { header, action, footer }

View file

@ -10,7 +10,7 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/borders.dart'; import 'package:aves/widgets/common/fx/borders.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/home_widget.dart'; import 'package:aves/widgets/home_widget.dart';
import 'package:aves/widgets/settings/common/collection_tile.dart'; import 'package:aves/widgets/settings/common/collection_tile.dart';
import 'package:aves/widgets/settings/common/tiles.dart'; import 'package:aves/widgets/settings/common/tiles.dart';

View file

@ -2,7 +2,7 @@ import 'package:aves/model/filters/album.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/filter_grids/album_pick.dart'; import 'package:aves/widgets/filter_grids/album_pick.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart'; import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:aves/widgets/settings/navigation/drawer_editor_banner.dart'; import 'package:aves/widgets/settings/navigation/drawer_editor_banner.dart';

View file

@ -8,7 +8,7 @@ import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/settings/privacy/file_picker/crumb_line.dart'; import 'package:aves/widgets/settings/privacy/file_picker/crumb_line.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';

View file

@ -5,7 +5,7 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/settings/privacy/file_picker/file_picker_page.dart'; import 'package:aves/widgets/settings/privacy/file_picker/file_picker_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -16,8 +16,8 @@ import 'package:aves/theme/colors.dart';
import 'package:aves/theme/format.dart'; import 'package:aves/theme/format.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_utils.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/rate_button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/rate_button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/tag_button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/tag_button.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/viewer/action/entry_info_action_delegate.dart'; import 'package:aves/widgets/viewer/action/entry_info_action_delegate.dart';

View file

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/viewer/overlay/common.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/viewer/panorama_page.dart'; import 'package:aves/widgets/viewer/panorama_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -1,5 +1,5 @@
import 'package:aves/model/actions/slideshow_actions.dart'; import 'package:aves/model/actions/slideshow_actions.dart';
import 'package:aves/widgets/viewer/overlay/common.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart'; import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart';
import 'package:aves/widgets/viewer/slideshow_page.dart'; import 'package:aves/widgets/viewer/slideshow_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -14,7 +14,7 @@ class SlideshowButtons extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// TODO TLAD [tv] action buttons // TODO TLAD [tv] captioned buttons
const padding = ViewerButtonRowContent.padding; const padding = ViewerButtonRowContent.padding;
return SafeArea( return SafeArea(
child: Padding( child: Padding(

View file

@ -1,8 +1,8 @@
import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/viewer/overlay/common.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/viewer/overlay/video/play_toggler.dart'; import 'package:aves/widgets/common/action_controls/togglers/play.dart';
import 'package:aves/widgets/viewer/video/controller.dart'; import 'package:aves/widgets/viewer/video/controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/widgets/viewer/overlay/common.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/viewer/overlay/video/controls.dart'; import 'package:aves/widgets/viewer/overlay/video/controls.dart';
import 'package:aves/widgets/viewer/overlay/video/progress_bar.dart'; import 'package:aves/widgets/viewer/overlay/video/progress_bar.dart';
import 'package:aves/widgets/viewer/video/controller.dart'; import 'package:aves/widgets/viewer/video/controller.dart';

View file

@ -5,20 +5,20 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/app_bar/favourite_toggler.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/move_button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/move_button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/rate_button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/rate_button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/share_button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/share_button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/tag_button.dart';
import 'package:aves/widgets/common/app_bar/quick_choosers/tag_button.dart'; import 'package:aves/widgets/common/action_controls/togglers/favourite.dart';
import 'package:aves/widgets/common/action_controls/togglers/mute.dart';
import 'package:aves/widgets/common/action_controls/togglers/play.dart';
import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/menu.dart';
import 'package:aves/widgets/common/basic/popup_menu_button.dart'; import 'package:aves/widgets/common/basic/popup_menu_button.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_button.dart'; import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/viewer/action/entry_action_delegate.dart'; import 'package:aves/widgets/viewer/action/entry_action_delegate.dart';
import 'package:aves/widgets/viewer/notifications.dart'; import 'package:aves/widgets/viewer/notifications.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:aves/widgets/viewer/overlay/video/mute_toggler.dart';
import 'package:aves/widgets/viewer/overlay/video/play_toggler.dart';
import 'package:aves/widgets/viewer/video/conductor.dart'; import 'package:aves/widgets/viewer/video/conductor.dart';
import 'package:aves/widgets/viewer/video/controller.dart'; import 'package:aves/widgets/viewer/video/controller.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@ -49,16 +49,17 @@ class ViewerButtons extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final actionDelegate = EntryActionDelegate(mainEntry, pageEntry, collection);
if (device.isTelevision) { if (device.isTelevision) {
return _TvButtonRowContent( return _TvButtonRowContent(
actionDelegate: actionDelegate,
scale: scale, scale: scale,
mainEntry: mainEntry, mainEntry: mainEntry,
pageEntry: pageEntry, pageEntry: pageEntry,
collection: collection,
); );
} }
final actionDelegate = EntryActionDelegate(mainEntry, pageEntry, collection);
final trashed = mainEntry.trashed; final trashed = mainEntry.trashed;
return SafeArea( return SafeArea(
top: false, top: false,
@ -75,6 +76,7 @@ class ViewerButtons extends StatelessWidget {
final exportActions = EntryActions.export.where((action) => !quickActions.contains(action)).where(actionDelegate.isVisible).toList(); final exportActions = EntryActions.export.where((action) => !quickActions.contains(action)).where(actionDelegate.isVisible).toList();
final videoActions = EntryActions.video.where((action) => !quickActions.contains(action)).where(actionDelegate.isVisible).toList(); final videoActions = EntryActions.video.where((action) => !quickActions.contains(action)).where(actionDelegate.isVisible).toList();
return ViewerButtonRowContent( return ViewerButtonRowContent(
actionDelegate: EntryActionDelegate(mainEntry, pageEntry, collection),
quickActions: quickActions, quickActions: quickActions,
topLevelActions: topLevelActions, topLevelActions: topLevelActions,
exportActions: exportActions, exportActions: exportActions,
@ -82,7 +84,6 @@ class ViewerButtons extends StatelessWidget {
scale: scale, scale: scale,
mainEntry: mainEntry, mainEntry: mainEntry,
pageEntry: pageEntry, pageEntry: pageEntry,
collection: collection,
); );
}, },
); );
@ -93,48 +94,64 @@ class ViewerButtons extends StatelessWidget {
} }
class _TvButtonRowContent extends StatelessWidget { class _TvButtonRowContent extends StatelessWidget {
final EntryActionDelegate actionDelegate;
final Animation<double> scale; final Animation<double> scale;
final AvesEntry mainEntry, pageEntry; final AvesEntry mainEntry, pageEntry;
final CollectionLens? collection;
const _TvButtonRowContent({ const _TvButtonRowContent({
required this.actionDelegate,
required this.scale, required this.scale,
required this.mainEntry, required this.mainEntry,
required this.pageEntry, required this.pageEntry,
required this.collection,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final actionDelegate = EntryActionDelegate(mainEntry, pageEntry, collection); return Selector<VideoConductor, AvesVideoController?>(
selector: (context, vc) => vc.getController(pageEntry),
builder: (context, videoController, child) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
...EntryActions.topLevel, ...EntryActions.topLevel,
...EntryActions.export, ...EntryActions.export,
...EntryActions.videoPlayback,
...EntryActions.video, ...EntryActions.video,
].where(actionDelegate.isVisible).map((action) { ].where(actionDelegate.isVisible).map((action) {
// TODO TLAD [tv] togglers cf `_buildOverlayButton`
// TODO TLAD [tv] use `scale`
final enabled = actionDelegate.canApply(action); final enabled = actionDelegate.canApply(action);
return ActionButton( return CaptionedButton(
text: action.getText(context), scale: scale,
icon: action.getIcon(), iconButton: _buildButtonIcon(
context: context,
action: action,
mainEntry: mainEntry,
pageEntry: pageEntry,
videoController: videoController,
actionDelegate: actionDelegate,
),
captionText: _buildButtonCaption(
context: context,
action: action,
mainEntry: mainEntry,
pageEntry: pageEntry,
videoController: videoController,
enabled: enabled, enabled: enabled,
),
onPressed: enabled ? () => actionDelegate.onActionSelected(context, action) : null, onPressed: enabled ? () => actionDelegate.onActionSelected(context, action) : null,
); );
}).toList(), }).toList(),
); );
},
);
} }
} }
class ViewerButtonRowContent extends StatelessWidget { class ViewerButtonRowContent extends StatelessWidget {
final EntryActionDelegate actionDelegate;
final List<EntryAction> quickActions, topLevelActions, exportActions, videoActions; final List<EntryAction> quickActions, topLevelActions, exportActions, videoActions;
final Animation<double> scale; final Animation<double> scale;
final AvesEntry mainEntry, pageEntry; final AvesEntry mainEntry, pageEntry;
final CollectionLens? collection;
final ValueNotifier<String?> _popupExpandedNotifier = ValueNotifier(null); final ValueNotifier<String?> _popupExpandedNotifier = ValueNotifier(null);
AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry; AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry;
@ -143,6 +160,7 @@ class ViewerButtonRowContent extends StatelessWidget {
ViewerButtonRowContent({ ViewerButtonRowContent({
super.key, super.key,
required this.actionDelegate,
required this.quickActions, required this.quickActions,
required this.topLevelActions, required this.topLevelActions,
required this.exportActions, required this.exportActions,
@ -150,7 +168,6 @@ class ViewerButtonRowContent extends StatelessWidget {
required this.scale, required this.scale,
required this.mainEntry, required this.mainEntry,
required this.pageEntry, required this.pageEntry,
required this.collection,
}); });
@override @override
@ -216,7 +233,7 @@ class ViewerButtonRowContent extends StatelessWidget {
onSelected: (action) { onSelected: (action) {
_popupExpandedNotifier.value = null; _popupExpandedNotifier.value = null;
// wait for the popup menu to hide before proceeding with the action // wait for the popup menu to hide before proceeding with the action
Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(context, action)); Future.delayed(Durations.popupMenuAnimation * timeDilation, () => actionDelegate.onActionSelected(context, action));
}, },
onCanceled: () { onCanceled: () {
_popupExpandedNotifier.value = null; _popupExpandedNotifier.value = null;
@ -239,101 +256,18 @@ class ViewerButtonRowContent extends StatelessWidget {
} }
Widget _buildOverlayButton(BuildContext context, EntryAction action, AvesVideoController? videoController) { Widget _buildOverlayButton(BuildContext context, EntryAction action, AvesVideoController? videoController) {
Widget? child;
void onPressed() => _onActionSelected(context, action);
ValueListenableBuilder<bool> _buildFromListenable(ValueListenable<bool>? enabledNotifier) {
return ValueListenableBuilder<bool>(
valueListenable: enabledNotifier ?? ValueNotifier(false),
builder: (context, canDo, child) => IconButton(
icon: child!,
onPressed: canDo ? onPressed : null,
tooltip: action.getText(context),
),
child: action.getIcon(),
);
}
final blurred = settings.enableBlurEffect;
switch (action) {
case EntryAction.copy:
child = MoveButton(
copy: true,
blurred: blurred,
onChooserValue: (album) => _entryActionDelegate.quickMove(context, album, copy: true),
onPressed: onPressed,
);
break;
case EntryAction.move:
child = MoveButton(
copy: false,
blurred: blurred,
onChooserValue: (album) => _entryActionDelegate.quickMove(context, album, copy: false),
onPressed: onPressed,
);
break;
case EntryAction.share:
child = ShareButton(
blurred: blurred,
entries: {mainEntry},
onChooserValue: (action) => _entryActionDelegate.quickShare(context, action),
onPressed: onPressed,
);
break;
case EntryAction.toggleFavourite:
child = FavouriteToggler(
entries: {favouriteTargetEntry},
onPressed: onPressed,
);
break;
case EntryAction.videoToggleMute:
child = MuteToggler(
controller: videoController,
onPressed: onPressed,
);
break;
case EntryAction.videoTogglePlay:
child = PlayToggler(
controller: videoController,
onPressed: onPressed,
);
break;
case EntryAction.videoCaptureFrame:
child = _buildFromListenable(videoController?.canCaptureFrameNotifier);
break;
case EntryAction.videoSelectStreams:
child = _buildFromListenable(videoController?.canSelectStreamNotifier);
break;
case EntryAction.videoSetSpeed:
child = _buildFromListenable(videoController?.canSetSpeedNotifier);
break;
case EntryAction.editRating:
child = RateButton(
blurred: blurred,
onChooserValue: (rating) => _entryActionDelegate.quickRate(context, rating),
onPressed: onPressed,
);
break;
case EntryAction.editTags:
child = TagButton(
blurred: blurred,
onChooserValue: (filter) => _entryActionDelegate.quickTag(context, filter),
onPressed: onPressed,
);
break;
default:
child = IconButton(
icon: action.getIcon(),
onPressed: onPressed,
tooltip: action.getText(context),
);
break;
}
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: padding / 2), padding: const EdgeInsets.symmetric(horizontal: padding / 2),
child: OverlayButton( child: OverlayButton(
scale: scale, scale: scale,
child: child, child: _buildButtonIcon(
context: context,
action: action,
mainEntry: mainEntry,
pageEntry: pageEntry,
videoController: videoController,
actionDelegate: actionDelegate,
),
), ),
); );
} }
@ -390,8 +324,6 @@ class ViewerButtonRowContent extends StatelessWidget {
} }
PopupMenuItem<EntryAction> _buildRotateAndFlipMenuItems(BuildContext context) { PopupMenuItem<EntryAction> _buildRotateAndFlipMenuItems(BuildContext context) {
final actionDelegate = _entryActionDelegate;
Widget buildDivider() => const SizedBox( Widget buildDivider() => const SizedBox(
height: 16, height: 16,
child: VerticalDivider( child: VerticalDivider(
@ -443,8 +375,139 @@ class ViewerButtonRowContent extends StatelessWidget {
), ),
); );
} }
}
EntryActionDelegate get _entryActionDelegate => EntryActionDelegate(mainEntry, pageEntry, collection);
Widget _buildButtonIcon({
void _onActionSelected(BuildContext context, EntryAction action) => _entryActionDelegate.onActionSelected(context, action); required BuildContext context,
required EntryAction action,
required AvesEntry mainEntry,
required AvesEntry pageEntry,
required AvesVideoController? videoController,
required EntryActionDelegate actionDelegate,
}) {
Widget? child;
void onPressed() => actionDelegate.onActionSelected(context, action);
ValueListenableBuilder<bool> _buildFromListenable(ValueListenable<bool>? enabledNotifier) {
return ValueListenableBuilder<bool>(
valueListenable: enabledNotifier ?? ValueNotifier(false),
builder: (context, canDo, child) => IconButton(
icon: child!,
onPressed: canDo ? onPressed : null,
tooltip: action.getText(context),
),
child: action.getIcon(),
);
}
final blurred = settings.enableBlurEffect;
switch (action) {
case EntryAction.copy:
child = MoveButton(
copy: true,
blurred: blurred,
onChooserValue: (album) => actionDelegate.quickMove(context, album, copy: true),
onPressed: onPressed,
);
break;
case EntryAction.move:
child = MoveButton(
copy: false,
blurred: blurred,
onChooserValue: (album) => actionDelegate.quickMove(context, album, copy: false),
onPressed: onPressed,
);
break;
case EntryAction.share:
child = ShareButton(
blurred: blurred,
entries: {mainEntry},
onChooserValue: (action) => actionDelegate.quickShare(context, action),
onPressed: onPressed,
);
break;
case EntryAction.toggleFavourite:
final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry;
child = FavouriteToggler(
entries: {favouriteTargetEntry},
onPressed: onPressed,
);
break;
case EntryAction.videoToggleMute:
child = MuteToggler(
controller: videoController,
onPressed: onPressed,
);
break;
case EntryAction.videoTogglePlay:
child = PlayToggler(
controller: videoController,
onPressed: onPressed,
);
break;
case EntryAction.videoCaptureFrame:
child = _buildFromListenable(videoController?.canCaptureFrameNotifier);
break;
case EntryAction.videoSelectStreams:
child = _buildFromListenable(videoController?.canSelectStreamNotifier);
break;
case EntryAction.videoSetSpeed:
child = _buildFromListenable(videoController?.canSetSpeedNotifier);
break;
case EntryAction.editRating:
child = RateButton(
blurred: blurred,
onChooserValue: (rating) => actionDelegate.quickRate(context, rating),
onPressed: onPressed,
);
break;
case EntryAction.editTags:
child = TagButton(
blurred: blurred,
onChooserValue: (filter) => actionDelegate.quickTag(context, filter),
onPressed: onPressed,
);
break;
default:
child = IconButton(
icon: action.getIcon(),
onPressed: onPressed,
tooltip: action.getText(context),
);
break;
}
return child;
}
Widget _buildButtonCaption({
required BuildContext context,
required EntryAction action,
required AvesEntry mainEntry,
required AvesEntry pageEntry,
required AvesVideoController? videoController,
required bool enabled,
}) {
switch (action) {
case EntryAction.toggleFavourite:
final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry;
return FavouriteTogglerCaption(
entries: {favouriteTargetEntry},
enabled: enabled,
);
case EntryAction.videoToggleMute:
return MuteTogglerCaption(
controller: videoController,
enabled: enabled,
);
case EntryAction.videoTogglePlay:
return PlayTogglerCaption(
controller: videoController,
enabled: enabled,
);
default:
return CaptionedButtonText(
text: action.getText(context),
enabled: enabled,
);
}
} }

View file

@ -8,8 +8,8 @@ import 'package:aves/model/wallpaper_target.dart';
import 'package:aves/services/wallpaper_service.dart'; import 'package:aves/services/wallpaper_service.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/dialogs/wallpaper_settings_dialog.dart'; import 'package:aves/widgets/dialogs/wallpaper_settings_dialog.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart'; import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart';
import 'package:aves/widgets/viewer/video/conductor.dart'; import 'package:aves/widgets/viewer/video/conductor.dart';
import 'package:aves/widgets/viewer/visual/conductor.dart'; import 'package:aves/widgets/viewer/visual/conductor.dart';

View file

@ -9,7 +9,7 @@ import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/basic/insets.dart'; import 'package:aves/widgets/common/basic/insets.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/extensions/media_query.dart'; import 'package:aves/widgets/common/extensions/media_query.dart';
import 'package:aves/widgets/viewer/overlay/common.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';

View file

@ -10,7 +10,7 @@ import 'package:aves/widgets/common/basic/link_chip.dart';
import 'package:aves/widgets/common/basic/markdown_container.dart'; import 'package:aves/widgets/common/basic/markdown_container.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_logo.dart'; import 'package:aves/widgets/common/identity/aves_logo.dart';
import 'package:aves/widgets/common/identity/buttons.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
import 'package:aves/widgets/home_page.dart'; import 'package:aves/widgets/home_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';