thumbnail theme provider, app mode provider, thumbnail overlay review
This commit is contained in:
parent
ff6aef1e82
commit
cf8d182cfe
16 changed files with 268 additions and 216 deletions
|
@ -6,3 +6,5 @@
|
|||
|
||||
preferred-supported-locales:
|
||||
- en
|
||||
|
||||
# untranslated-messages-file: untranslated.json
|
||||
|
|
|
@ -43,13 +43,12 @@ void main() {
|
|||
enum AppMode { main, pick, view }
|
||||
|
||||
class AvesApp extends StatefulWidget {
|
||||
static AppMode mode;
|
||||
|
||||
@override
|
||||
_AvesAppState createState() => _AvesAppState();
|
||||
}
|
||||
|
||||
class _AvesAppState extends State<AvesApp> {
|
||||
final ValueNotifier<AppMode> appModeNotifier = ValueNotifier(AppMode.main);
|
||||
Future<void> _appSetup;
|
||||
final _mediaStoreSource = MediaStoreSource();
|
||||
final Debouncer _contentChangeDebouncer = Debouncer(delay: Durations.contentChangeDebounceDelay);
|
||||
|
@ -123,6 +122,8 @@ class _AvesAppState extends State<AvesApp> {
|
|||
// so it can be used during navigation transitions
|
||||
return ChangeNotifierProvider<Settings>.value(
|
||||
value: settings,
|
||||
child: ListenableProvider<ValueNotifier<AppMode>>.value(
|
||||
value: appModeNotifier,
|
||||
child: Provider<CollectionSource>.value(
|
||||
value: _mediaStoreSource,
|
||||
child: HighlightInfoProvider(
|
||||
|
@ -159,6 +160,7 @@ class _AvesAppState extends State<AvesApp> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -204,7 +206,7 @@ class _AvesAppState extends State<AvesApp> {
|
|||
debugPrint('$runtimeType onNewIntent with intentData=$intentData');
|
||||
|
||||
// do not reset when relaunching the app
|
||||
if (AvesApp.mode == AppMode.main && (intentData == null || intentData.isEmpty == true)) return;
|
||||
if (appModeNotifier.value == AppMode.main && (intentData == null || intentData.isEmpty == true)) return;
|
||||
|
||||
FirebaseCrashlytics.instance.log('New intent');
|
||||
_navigatorKey.currentState.pushReplacement(DirectMaterialPageRoute(
|
||||
|
|
|
@ -26,6 +26,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CollectionAppBar extends StatefulWidget {
|
||||
final ValueNotifier<double> appBarHeightNotifier;
|
||||
|
@ -141,8 +142,9 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
|
||||
Widget _buildAppBarTitle() {
|
||||
if (collection.isBrowsing) {
|
||||
Widget title = Text(AvesApp.mode == AppMode.pick ? context.l10n.collectionPickPageTitle : context.l10n.collectionPageTitle);
|
||||
if (AvesApp.mode == AppMode.main) {
|
||||
final appMode = context.watch<ValueNotifier<AppMode>>().value;
|
||||
Widget title = Text(appMode == AppMode.pick ? context.l10n.collectionPickPageTitle : context.l10n.collectionPageTitle);
|
||||
if (appMode == AppMode.main) {
|
||||
title = SourceStateAwareAppBarTitle(
|
||||
title: title,
|
||||
source: source,
|
||||
|
@ -191,6 +193,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
itemBuilder: (context) {
|
||||
final isNotEmpty = !collection.isEmpty;
|
||||
final hasSelection = collection.selection.isNotEmpty;
|
||||
final isMainMode = context.read<ValueNotifier<AppMode>>().value == AppMode.main;
|
||||
return [
|
||||
PopupMenuItem(
|
||||
key: Key('menu-sort'),
|
||||
|
@ -204,7 +207,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
child: MenuRow(text: context.l10n.menuActionGroup, icon: AIcons.group),
|
||||
),
|
||||
if (collection.isBrowsing) ...[
|
||||
if (AvesApp.mode == AppMode.main)
|
||||
if (isMainMode)
|
||||
PopupMenuItem(
|
||||
value: CollectionAction.select,
|
||||
enabled: isNotEmpty,
|
||||
|
@ -215,7 +218,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
enabled: isNotEmpty,
|
||||
child: MenuRow(text: context.l10n.menuActionStats, icon: AIcons.stats),
|
||||
),
|
||||
if (AvesApp.mode == AppMode.main && canAddShortcuts)
|
||||
if (isMainMode && canAddShortcuts)
|
||||
PopupMenuItem(
|
||||
value: CollectionAction.addShortcut,
|
||||
child: MenuRow(text: context.l10n.collectionActionAddShortcut, icon: AIcons.addShortcut),
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:aves/widgets/collection/grid/section_layout.dart';
|
|||
import 'package:aves/widgets/collection/grid/selector.dart';
|
||||
import 'package:aves/widgets/collection/grid/thumbnail.dart';
|
||||
import 'package:aves/widgets/collection/thumbnail/decorated.dart';
|
||||
import 'package:aves/widgets/collection/thumbnail/theme.dart';
|
||||
import 'package:aves/widgets/common/basic/draggable_scrollbar.dart';
|
||||
import 'package:aves/widgets/common/basic/insets.dart';
|
||||
import 'package:aves/widgets/common/behaviour/sloppy_scroll_physics.dart';
|
||||
|
@ -65,7 +66,9 @@ class _CollectionGridContent extends StatelessWidget {
|
|||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
|
||||
builder: (context, tileExtent, child) {
|
||||
return SectionedEntryListLayoutProvider(
|
||||
return ThumbnailTheme(
|
||||
extent: tileExtent,
|
||||
child: SectionedEntryListLayoutProvider(
|
||||
collection: collection,
|
||||
scrollableWidth: context.select<TileExtentController, double>((controller) => controller.viewportSize.width),
|
||||
tileExtent: tileExtent,
|
||||
|
@ -82,6 +85,7 @@ class _CollectionGridContent extends StatelessWidget {
|
|||
isScrollingNotifier: _isScrollingNotifier,
|
||||
scrollController: PrimaryScrollController.of(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -136,8 +140,9 @@ class _CollectionSectionedContentState extends State<_CollectionSectionedContent
|
|||
child: scrollView,
|
||||
);
|
||||
|
||||
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
|
||||
final selector = GridSelectionGestureDetector(
|
||||
selectable: AvesApp.mode == AppMode.main,
|
||||
selectable: isMainMode,
|
||||
collection: collection,
|
||||
scrollController: scrollController,
|
||||
appBarHeightNotifier: _appBarHeightNotifier,
|
||||
|
@ -177,12 +182,15 @@ class _CollectionScaler extends StatelessWidget {
|
|||
),
|
||||
child: child,
|
||||
),
|
||||
scaledBuilder: (entry, extent) => DecoratedThumbnail(
|
||||
scaledBuilder: (entry, extent) => ThumbnailTheme(
|
||||
extent: extent,
|
||||
child: DecoratedThumbnail(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
selectable: false,
|
||||
highlightable: false,
|
||||
),
|
||||
),
|
||||
getScaledItemTileRect: (context, entry) {
|
||||
final sectionedListLayout = context.read<SectionedListLayout<AvesEntry>>();
|
||||
return sectionedListLayout.getTileRect(entry) ?? Rect.zero;
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/widgets/common/behaviour/routes.dart';
|
|||
import 'package:aves/widgets/common/scaling.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class InteractiveThumbnail extends StatelessWidget {
|
||||
final CollectionLens collection;
|
||||
|
@ -27,13 +28,14 @@ class InteractiveThumbnail extends StatelessWidget {
|
|||
return GestureDetector(
|
||||
key: ValueKey(entry.uri),
|
||||
onTap: () {
|
||||
if (AvesApp.mode == AppMode.main) {
|
||||
final appMode = context.read<ValueNotifier<AppMode>>().value;
|
||||
if (appMode == AppMode.main) {
|
||||
if (collection.isBrowsing) {
|
||||
_goToViewer(context);
|
||||
} else if (collection.isSelecting) {
|
||||
collection.toggleSelection(entry);
|
||||
}
|
||||
} else if (AvesApp.mode == AppMode.pick) {
|
||||
} else if (appMode == AppMode.pick) {
|
||||
ViewerService.pick(entry.uri);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -31,7 +31,8 @@ class DecoratedThumbnail extends StatelessWidget {
|
|||
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
|
||||
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
|
||||
final heroTag = hashValues(collection?.id, entry);
|
||||
var child = entry.isSvg
|
||||
final isSvg = entry.isSvg;
|
||||
var child = isSvg
|
||||
? VectorImageThumbnail(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
|
@ -45,17 +46,14 @@ class DecoratedThumbnail extends StatelessWidget {
|
|||
);
|
||||
|
||||
child = Stack(
|
||||
alignment: Alignment.center,
|
||||
alignment: isSvg ? Alignment.center : AlignmentDirectional.bottomStart,
|
||||
children: [
|
||||
child,
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
child: ThumbnailEntryOverlay(
|
||||
if (!isSvg)
|
||||
ThumbnailEntryOverlay(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
),
|
||||
),
|
||||
if (selectable)
|
||||
ThumbnailSelectionOverlay(
|
||||
entry: entry,
|
||||
|
|
|
@ -2,16 +2,15 @@ import 'dart:math';
|
|||
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/model/highlight.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/model/source/enums.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/collection/thumbnail/theme.dart';
|
||||
import 'package:aves/widgets/common/fx/sweeper.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class ThumbnailEntryOverlay extends StatelessWidget {
|
||||
final AvesEntry entry;
|
||||
|
@ -25,38 +24,28 @@ class ThumbnailEntryOverlay extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fontSize = min(14.0, (extent / 8)).roundToDouble();
|
||||
final iconSize = fontSize * 2;
|
||||
return Selector<Settings, Tuple3<bool, bool, bool>>(
|
||||
selector: (context, s) => Tuple3(s.showThumbnailLocation, s.showThumbnailRaw, s.showThumbnailVideoDuration),
|
||||
builder: (context, s, child) {
|
||||
final children = [
|
||||
if (entry.hasGps && context.select<ThumbnailThemeData, bool>((t) => t.showLocation)) GpsIcon(),
|
||||
if (entry.isVideo)
|
||||
VideoIcon(
|
||||
entry: entry,
|
||||
)
|
||||
else if (entry.isAnimated)
|
||||
AnimatedImageIcon()
|
||||
else ...[
|
||||
if (entry.isRaw && context.select<ThumbnailThemeData, bool>((t) => t.showRaw)) RawIcon(),
|
||||
if (entry.isMultipage) MultipageIcon(),
|
||||
if (entry.isGeotiff) GeotiffIcon(),
|
||||
if (entry.is360) SphericalImageIcon(),
|
||||
]
|
||||
];
|
||||
if (children.isEmpty) return SizedBox.shrink();
|
||||
if (children.length == 1) return children.first;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (entry.hasGps && settings.showThumbnailLocation) GpsIcon(iconSize: iconSize),
|
||||
if (entry.isRaw && settings.showThumbnailRaw) RawIcon(iconSize: iconSize),
|
||||
if (entry.isMultipage) MultipageIcon(iconSize: iconSize),
|
||||
if (entry.isGeotiff) GeotiffIcon(iconSize: iconSize),
|
||||
if (entry.isAnimated)
|
||||
AnimatedImageIcon(iconSize: iconSize)
|
||||
else if (entry.isVideo)
|
||||
DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
color: Colors.grey[200],
|
||||
fontSize: fontSize,
|
||||
),
|
||||
child: VideoIcon(
|
||||
entry: entry,
|
||||
iconSize: iconSize,
|
||||
showDuration: settings.showThumbnailVideoDuration,
|
||||
),
|
||||
)
|
||||
else if (entry.is360)
|
||||
SphericalImageIcon(iconSize: iconSize),
|
||||
],
|
||||
children: children,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +53,8 @@ class ThumbnailSelectionOverlay extends StatelessWidget {
|
|||
final AvesEntry entry;
|
||||
final double extent;
|
||||
|
||||
static const duration = Durations.thumbnailOverlayAnimation;
|
||||
|
||||
const ThumbnailSelectionOverlay({
|
||||
Key key,
|
||||
@required this.entry,
|
||||
|
@ -72,9 +63,6 @@ class ThumbnailSelectionOverlay extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const duration = Durations.thumbnailOverlayAnimation;
|
||||
final fontSize = min(14.0, (extent / 8)).roundToDouble();
|
||||
final iconSize = fontSize * 2;
|
||||
final collection = context.watch<CollectionLens>();
|
||||
return ValueListenableBuilder<Activity>(
|
||||
valueListenable: collection.activityNotifier,
|
||||
|
@ -88,7 +76,7 @@ class ThumbnailSelectionOverlay extends StatelessWidget {
|
|||
? OverlayIcon(
|
||||
key: ValueKey(selected),
|
||||
icon: selected ? AIcons.selected : AIcons.unselected,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
)
|
||||
: SizedBox.shrink();
|
||||
child = AnimatedSwitcher(
|
||||
|
@ -139,6 +127,8 @@ class _ThumbnailHighlightOverlayState extends State<ThumbnailHighlightOverlay> {
|
|||
|
||||
AvesEntry get entry => widget.entry;
|
||||
|
||||
static const startAngle = pi * -3 / 4;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final highlightInfo = context.watch<HighlightInfo>();
|
||||
|
@ -153,7 +143,7 @@ class _ThumbnailHighlightOverlayState extends State<ThumbnailHighlightOverlay> {
|
|||
),
|
||||
),
|
||||
toggledNotifier: _highlightedNotifier,
|
||||
startAngle: pi * -3 / 4,
|
||||
startAngle: startAngle,
|
||||
centerSweep: false,
|
||||
onSweepEnd: highlightInfo.clear,
|
||||
);
|
||||
|
|
46
lib/widgets/collection/thumbnail/theme.dart
Normal file
46
lib/widgets/collection/thumbnail/theme.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ThumbnailTheme extends StatelessWidget {
|
||||
final double extent;
|
||||
final Widget child;
|
||||
|
||||
const ThumbnailTheme({
|
||||
@required this.extent,
|
||||
@required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProxyProvider<Settings, ThumbnailThemeData>(
|
||||
update: (_, settings, __) {
|
||||
final iconSize = min(28.0, (extent / 4)).roundToDouble();
|
||||
final fontSize = (iconSize / 2).floorToDouble();
|
||||
return ThumbnailThemeData(
|
||||
iconSize: iconSize,
|
||||
fontSize: fontSize,
|
||||
showLocation: settings.showThumbnailLocation,
|
||||
showRaw: settings.showThumbnailRaw,
|
||||
showVideoDuration: settings.showThumbnailVideoDuration,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThumbnailThemeData {
|
||||
final double iconSize, fontSize;
|
||||
final bool showLocation, showRaw, showVideoDuration;
|
||||
|
||||
const ThumbnailThemeData({
|
||||
@required this.iconSize,
|
||||
@required this.fontSize,
|
||||
@required this.showLocation,
|
||||
@required this.showRaw,
|
||||
@required this.showVideoDuration,
|
||||
});
|
||||
}
|
|
@ -11,6 +11,7 @@ import 'package:aves/widgets/common/basic/menu_row.dart';
|
|||
import 'package:aves/widgets/filter_grids/common/chip_action_delegate.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
typedef FilterCallback = void Function(CollectionFilter filter);
|
||||
typedef OffsetFilterCallback = void Function(BuildContext context, CollectionFilter filter, Offset tapPosition);
|
||||
|
@ -52,7 +53,7 @@ class AvesFilterChip extends StatefulWidget {
|
|||
super(key: key);
|
||||
|
||||
static Future<void> showDefaultLongPressMenu(BuildContext context, CollectionFilter filter, Offset tapPosition) async {
|
||||
if (AvesApp.mode == AppMode.main) {
|
||||
if (context.read<ValueNotifier<AppMode>>().value == AppMode.main) {
|
||||
final actions = [
|
||||
if (filter is AlbumFilter) ChipAction.goToAlbumPage,
|
||||
if ((filter is LocationFilter && filter.level == LocationLevel.country)) ChipAction.goToCountryPage,
|
||||
|
|
|
@ -5,114 +5,111 @@ import 'package:aves/model/entry.dart';
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/collection/thumbnail/theme.dart';
|
||||
import 'package:decorated_icon/decorated_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class VideoIcon extends StatelessWidget {
|
||||
final AvesEntry entry;
|
||||
final double iconSize;
|
||||
final bool showDuration;
|
||||
|
||||
const VideoIcon({
|
||||
Key key,
|
||||
this.entry,
|
||||
this.iconSize,
|
||||
this.showDuration,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
final thumbnailTheme = context.watch<ThumbnailThemeData>();
|
||||
final showDuration = thumbnailTheme.showVideoDuration;
|
||||
Widget child = OverlayIcon(
|
||||
icon: entry.is360 ? AIcons.threesixty : AIcons.play,
|
||||
size: iconSize,
|
||||
size: thumbnailTheme.iconSize,
|
||||
text: showDuration ? entry.durationText : null,
|
||||
iconScale: entry.is360 && showDuration ? .9 : 1,
|
||||
);
|
||||
if (showDuration) {
|
||||
child = DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
color: Colors.grey[200],
|
||||
fontSize: thumbnailTheme.fontSize,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
class AnimatedImageIcon extends StatelessWidget {
|
||||
final double iconSize;
|
||||
|
||||
const AnimatedImageIcon({Key key, this.iconSize}) : super(key: key);
|
||||
const AnimatedImageIcon({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
icon: AIcons.animated,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
iconScale: .8,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GeotiffIcon extends StatelessWidget {
|
||||
final double iconSize;
|
||||
|
||||
const GeotiffIcon({Key key, this.iconSize}) : super(key: key);
|
||||
const GeotiffIcon({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
icon: AIcons.geo,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SphericalImageIcon extends StatelessWidget {
|
||||
final double iconSize;
|
||||
|
||||
const SphericalImageIcon({Key key, this.iconSize}) : super(key: key);
|
||||
const SphericalImageIcon({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
icon: AIcons.threesixty,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GpsIcon extends StatelessWidget {
|
||||
final double iconSize;
|
||||
|
||||
const GpsIcon({Key key, this.iconSize}) : super(key: key);
|
||||
const GpsIcon({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
icon: AIcons.location,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RawIcon extends StatelessWidget {
|
||||
final double iconSize;
|
||||
|
||||
const RawIcon({Key key, this.iconSize}) : super(key: key);
|
||||
const RawIcon({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
icon: AIcons.raw,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MultipageIcon extends StatelessWidget {
|
||||
final double iconSize;
|
||||
|
||||
const MultipageIcon({Key key, this.iconSize}) : super(key: key);
|
||||
const MultipageIcon({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayIcon(
|
||||
icon: AIcons.multipage,
|
||||
size: iconSize,
|
||||
size: context.select<ThumbnailThemeData, double>((t) => t.iconSize),
|
||||
iconScale: .8,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import 'package:aves/widgets/search/search_delegate.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
||||
final CollectionSource source;
|
||||
|
@ -47,6 +48,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
|
||||
return FilterGridPage<T>(
|
||||
key: ValueKey('filter-grid-page'),
|
||||
appBar: SliverAppBar(
|
||||
|
@ -80,7 +82,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
|||
)),
|
||||
),
|
||||
),
|
||||
onLongPress: AvesApp.mode == AppMode.main ? _showMenu : null,
|
||||
onLongPress: isMainMode ? _showMenu : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class _HomePageState extends State<HomePage> {
|
|||
await androidFileUtils.init();
|
||||
unawaited(androidFileUtils.initAppNames());
|
||||
|
||||
AvesApp.mode = AppMode.main;
|
||||
var appMode = AppMode.main;
|
||||
final intentData = widget.intentData ?? await ViewerService.getIntentData();
|
||||
if (intentData?.isNotEmpty == true) {
|
||||
final action = intentData['action'];
|
||||
|
@ -77,11 +77,11 @@ class _HomePageState extends State<HomePage> {
|
|||
mimeType: intentData['mimeType'],
|
||||
);
|
||||
if (_viewerEntry != null) {
|
||||
AvesApp.mode = AppMode.view;
|
||||
appMode = AppMode.view;
|
||||
}
|
||||
break;
|
||||
case 'pick':
|
||||
AvesApp.mode = AppMode.pick;
|
||||
appMode = AppMode.pick;
|
||||
// TODO TLAD apply pick mimetype(s)
|
||||
// some apps define multiple types, separated by a space (maybe other signs too, like `,` `;`?)
|
||||
String pickMimeTypes = intentData['mimeType'];
|
||||
|
@ -97,15 +97,16 @@ class _HomePageState extends State<HomePage> {
|
|||
_shortcutFilters = extraFilters != null ? (extraFilters as List).cast<String>() : null;
|
||||
}
|
||||
}
|
||||
unawaited(FirebaseCrashlytics.instance.setCustomKey('app_mode', AvesApp.mode.toString()));
|
||||
context.read<ValueNotifier<AppMode>>().value = appMode;
|
||||
unawaited(FirebaseCrashlytics.instance.setCustomKey('app_mode', appMode.toString()));
|
||||
|
||||
if (AvesApp.mode != AppMode.view) {
|
||||
if (appMode != AppMode.view) {
|
||||
final source = context.read<CollectionSource>();
|
||||
await source.init();
|
||||
unawaited(source.refresh());
|
||||
}
|
||||
|
||||
unawaited(Navigator.pushReplacement(context, _getRedirectRoute()));
|
||||
unawaited(Navigator.pushReplacement(context, _getRedirectRoute(appMode)));
|
||||
}
|
||||
|
||||
Future<AvesEntry> _initViewerEntry({@required String uri, @required String mimeType}) async {
|
||||
|
@ -117,8 +118,8 @@ class _HomePageState extends State<HomePage> {
|
|||
return entry;
|
||||
}
|
||||
|
||||
Route _getRedirectRoute() {
|
||||
if (AvesApp.mode == AppMode.view) {
|
||||
Route _getRedirectRoute(AppMode appMode) {
|
||||
if (appMode == AppMode.view) {
|
||||
return DirectMaterialPageRoute(
|
||||
settings: RouteSettings(name: EntryViewerPage.routeName),
|
||||
builder: (_) => EntryViewerPage(
|
||||
|
@ -129,7 +130,7 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
String routeName;
|
||||
Iterable<CollectionFilter> filters;
|
||||
if (AvesApp.mode == AppMode.pick) {
|
||||
if (appMode == AppMode.pick) {
|
||||
routeName = CollectionPage.routeName;
|
||||
} else {
|
||||
routeName = _shortcutRouteName ?? settings.homePage.routeName;
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:aves/widgets/viewer/debug/metadata.dart';
|
|||
import 'package:aves/widgets/viewer/info/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class ViewerDebugPage extends StatelessWidget {
|
||||
|
@ -21,7 +22,7 @@ class ViewerDebugPage extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final tabs = <Tuple2<Tab, Widget>>[
|
||||
Tuple2(Tab(text: 'Entry'), _buildEntryTabView()),
|
||||
if (AvesApp.mode != AppMode.view) Tuple2(Tab(text: 'DB'), DbTab(entry: entry)),
|
||||
if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) Tuple2(Tab(text: 'DB'), DbTab(entry: entry)),
|
||||
Tuple2(Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)),
|
||||
Tuple2(Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()),
|
||||
];
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/entry.dart';
|
|||
import 'package:aves/model/multipage.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/collection/thumbnail/decorated.dart';
|
||||
import 'package:aves/widgets/collection/thumbnail/theme.dart';
|
||||
import 'package:aves/widgets/viewer/multipage.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -80,7 +81,9 @@ class _MultiPageOverlayState extends State<MultiPageOverlay> {
|
|||
final horizontalMargin = SizedBox(width: marginWidth);
|
||||
final separator = SizedBox(width: separatorWidth);
|
||||
|
||||
return FutureBuilder<MultiPageInfo>(
|
||||
return ThumbnailTheme(
|
||||
extent: extent,
|
||||
child: FutureBuilder<MultiPageInfo>(
|
||||
future: controller.info,
|
||||
builder: (context, snapshot) {
|
||||
final multiPageInfo = snapshot.data;
|
||||
|
@ -140,6 +143,7 @@ class _MultiPageOverlayState extends State<MultiPageOverlay> {
|
|||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -559,7 +559,7 @@ packages:
|
|||
name: overlay_support
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.0"
|
||||
version: "1.2.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -706,7 +706,7 @@ packages:
|
|||
name: printing
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "5.0.3"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1026,7 +1026,7 @@ packages:
|
|||
name: version
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "2.0.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1068,7 +1068,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -7,8 +7,6 @@ publish_to: none
|
|||
environment:
|
||||
sdk: '>=2.10.0 <3.0.0'
|
||||
|
||||
# TODO TLAD remove explicit `overlay_support` version when 1.2.0 is stable (1.0.5 uses deprecated `ancestorWidgetOfExactType`)
|
||||
|
||||
# TODO TLAD switch to Flutter stable when possible, currently on dev/beta because of the following mess:
|
||||
# printing >=5.0.1 depends on pdf ^3.0.1, pdf >=3.0.1 depends on crypto ^3.0.0 and archive ^3.1.0
|
||||
# but `flutter_driver` (shipped with Flutter) dependencies are too old in stable v2.0.1
|
||||
|
@ -57,7 +55,7 @@ dependencies:
|
|||
intl:
|
||||
latlong:
|
||||
material_design_icons_flutter:
|
||||
overlay_support: 1.2.0-nullsafety.0
|
||||
overlay_support:
|
||||
package_info:
|
||||
palette_generator:
|
||||
panorama:
|
||||
|
@ -100,9 +98,6 @@ flutter:
|
|||
# generate `AppLocalizations`
|
||||
# % flutter gen-l10n
|
||||
|
||||
# list untranslated messages
|
||||
# % flutter gen-l10n --untranslated-messages-file untranslated.json
|
||||
|
||||
################################################################################
|
||||
# Test driver
|
||||
|
||||
|
|
Loading…
Reference in a new issue