tuple record migration

This commit is contained in:
Thibault Deckers 2023-08-16 23:42:52 +02:00
parent 1117da068b
commit a1a7b8e9e1
39 changed files with 163 additions and 241 deletions

View file

@ -370,11 +370,6 @@ class Dependencies {
license: mit, license: mit,
sourceUrl: 'https://github.com/brianegan/transparent_image', sourceUrl: 'https://github.com/brianegan/transparent_image',
), ),
Dependency(
name: 'Tuple',
license: bsd2,
sourceUrl: 'https://github.com/google/tuple.dart',
),
Dependency( Dependency(
name: 'Vector Math', name: 'Vector Math',
license: '$zlib, $bsd3', license: '$zlib, $bsd3',

View file

@ -49,7 +49,6 @@ import 'package:material_color_utilities/material_color_utilities.dart';
import 'package:overlay_support/overlay_support.dart'; import 'package:overlay_support/overlay_support.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:screen_brightness/screen_brightness.dart'; import 'package:screen_brightness/screen_brightness.dart';
import 'package:tuple/tuple.dart';
import 'package:url_launcher/url_launcher.dart' as ul; import 'package:url_launcher/url_launcher.dart' as ul;
class AvesApp extends StatefulWidget { class AvesApp extends StatefulWidget {
@ -236,16 +235,14 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
: AvesScaffold( : AvesScaffold(
body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(), body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(),
); );
return Selector<Settings, Tuple3<Locale?, AvesThemeBrightness, bool>>( return Selector<Settings, (Locale?, AvesThemeBrightness, bool)>(
selector: (context, s) => Tuple3( selector: (context, s) => (
s.locale, s.locale,
s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness, s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness,
s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor, s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor,
), ),
builder: (context, s, child) { builder: (context, s, child) {
final settingsLocale = s.item1; final (settingsLocale, themeBrightness, enableDynamicColor) = s;
final themeBrightness = s.item2;
final enableDynamicColor = s.item3;
AStyles.updateStylesForLocale(settings.appliedLocale); AStyles.updateStylesForLocale(settings.appliedLocale);

View file

@ -37,7 +37,6 @@ import 'package:collection/collection.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';
import 'package:tuple/tuple.dart';
class CollectionAppBar extends StatefulWidget { class CollectionAppBar extends StatefulWidget {
final ValueNotifier<double> appBarHeightNotifier; final ValueNotifier<double> appBarHeightNotifier;
@ -647,14 +646,14 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
} }
Future<void> _configureView() async { Future<void> _configureView() async {
final initialValue = Tuple4( final initialValue = (
settings.collectionSortFactor, settings.collectionSortFactor,
settings.collectionSectionFactor, settings.collectionSectionFactor,
settings.getTileLayout(CollectionPage.routeName), settings.getTileLayout(CollectionPage.routeName),
settings.collectionSortReverse, settings.collectionSortReverse,
); );
final extentController = context.read<TileExtentController>(); final extentController = context.read<TileExtentController>();
final value = await showDialog<Tuple4<EntrySortFactor?, EntryGroupFactor?, TileLayout?, bool>>( final value = await showDialog<(EntrySortFactor?, EntryGroupFactor?, TileLayout?, bool)>(
context: context, context: context,
builder: (context) { builder: (context) {
return TileViewDialog<EntrySortFactor, EntryGroupFactor, TileLayout>( return TileViewDialog<EntrySortFactor, EntryGroupFactor, TileLayout>(
@ -672,10 +671,10 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
// wait for the dialog to hide as applying the change may block the UI // wait for the dialog to hide as applying the change may block the UI
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation); await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
if (value != null && initialValue != value) { if (value != null && initialValue != value) {
settings.collectionSortFactor = value.item1!; settings.collectionSortFactor = value.$1!;
settings.collectionSectionFactor = value.item2!; settings.collectionSectionFactor = value.$2!;
settings.setTileLayout(CollectionPage.routeName, value.item3!); settings.setTileLayout(CollectionPage.routeName, value.$3!);
settings.collectionSortReverse = value.item4; settings.collectionSortReverse = value.$4;
} }
} }

View file

@ -51,7 +51,6 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class CollectionGrid extends StatefulWidget { class CollectionGrid extends StatefulWidget {
final String settingsRouteKey; final String settingsRouteKey;
@ -132,13 +131,10 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier), valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
builder: (context, thumbnailExtent, child) { builder: (context, thumbnailExtent, child) {
assert(thumbnailExtent > 0); assert(thumbnailExtent > 0);
return Selector<TileExtentController, Tuple4<double, int, double, double>>( return Selector<TileExtentController, (double, int, double, double)>(
selector: (context, c) => Tuple4(c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding), selector: (context, c) => (c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
builder: (context, c, child) { builder: (context, c, child) {
final scrollableWidth = c.item1; final (scrollableWidth, columnCount, tileSpacing, horizontalPadding) = c;
final columnCount = c.item2;
final tileSpacing = c.item3;
final horizontalPadding = c.item4;
final source = collection.source; final source = collection.source;
return GridTheme( return GridTheme(
extent: thumbnailExtent, extent: thumbnailExtent,
@ -369,9 +365,7 @@ class _CollectionScaler extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final metrics = context.select<TileExtentController, Tuple2<double, double>>((v) => Tuple2(v.spacing, v.horizontalPadding)); final (tileSpacing, horizontalPadding) = context.select<TileExtentController, (double, double)>((v) => (v.spacing, v.horizontalPadding));
final tileSpacing = metrics.item1;
final horizontalPadding = metrics.item2;
final brightness = Theme.of(context).brightness; final brightness = Theme.of(context).brightness;
final borderColor = DecoratedThumbnail.borderColor; final borderColor = DecoratedThumbnail.borderColor;
final borderWidth = DecoratedThumbnail.borderWidth(context); final borderWidth = DecoratedThumbnail.borderWidth(context);

View file

@ -45,7 +45,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, EntryEditorMixin, EntryStorageMixin { class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, EntryEditorMixin, EntryStorageMixin {
bool isVisible( bool isVisible(
@ -709,7 +708,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
final sortedFilters = List<CollectionFilter>.from(filters)..sort(); final sortedFilters = List<CollectionFilter>.from(filters)..sort();
defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' '); defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' ');
} }
final result = await showDialog<Tuple2<AvesEntry?, String>>( final result = await showDialog<(AvesEntry?, String)>(
context: context, context: context,
builder: (context) => AddShortcutDialog( builder: (context) => AddShortcutDialog(
defaultName: defaultName ?? '', defaultName: defaultName ?? '',
@ -719,8 +718,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
); );
if (result == null) return; if (result == null) return;
final coverEntry = result.item1; final (coverEntry, name) = result;
final name = result.item2;
if (name.isEmpty) return; if (name.isEmpty) return;
await appService.pinToHomeScreen(name, coverEntry, filters: filters); await appService.pinToHomeScreen(name, coverEntry, filters: filters);

View file

@ -3,7 +3,6 @@ import 'dart:math';
import 'package:aves/utils/diff_match.dart'; import 'package:aves/utils/diff_match.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:tuple/tuple.dart';
class AnimatedDiffText extends StatefulWidget { class AnimatedDiffText extends StatefulWidget {
final String text; final String text;
@ -71,10 +70,7 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
return Text.rich( return Text.rich(
TextSpan( TextSpan(
children: _diffs.map((diff) { children: _diffs.map((diff) {
final oldText = diff.item1; final (oldText, newText, oldSize, newSize) = diff;
final newText = diff.item2;
final oldSize = diff.item3;
final newSize = diff.item4;
final text = (_animation.value == 0 ? oldText : newText) ?? ''; final text = (_animation.value == 0 ? oldText : newText) ?? '';
return WidgetSpan( return WidgetSpan(
child: AnimatedSize( child: AnimatedSize(
@ -144,26 +140,22 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
..addAll(d.map((diff) { ..addAll(d.map((diff) {
final text = diff.text; final text = diff.text;
final size = textSize(text); final size = textSize(text);
switch (diff.operation) { return switch (diff.operation) {
case Operation.delete: Operation.delete => (text, null, size, Size.zero),
return Tuple4(text, null, size, Size.zero); Operation.insert => (null, text, Size.zero, size),
case Operation.insert: Operation.equal || _ => (text, text, size, size),
return Tuple4(null, text, Size.zero, size); };
case Operation.equal:
default:
return Tuple4(text, text, size, size);
}
}).fold<List<_TextDiff>>([], (prev, v) { }).fold<List<_TextDiff>>([], (prev, v) {
if (prev.isNotEmpty) { if (prev.isNotEmpty) {
final last = prev.last; final last = prev.last;
final prevNewText = last.item2; final prevNewText = last.$2;
if (prevNewText == null) { if (prevNewText == null) {
// previous diff is a deletion // previous diff is a deletion
final thisOldText = v.item1; final thisOldText = v.$1;
if (thisOldText == null) { if (thisOldText == null) {
// this diff is an insertion // this diff is an insertion
// merge deletion and insertion as a change operation // merge deletion and insertion as a change operation
final change = Tuple4(last.item1, v.item2, last.item3, v.item4); final change = (last.$1, v.$2, last.$3, v.$4);
return [...prev.take(prev.length - 1), change]; return [...prev.take(prev.length - 1), change];
} }
} }
@ -173,4 +165,4 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
} }
} }
typedef _TextDiff = Tuple4<String?, String?, Size, Size>; typedef _TextDiff = (String?, String?, Size, Size);

View file

@ -6,7 +6,6 @@ import 'package:aves/widgets/common/grid/sections/list_layout.dart';
import 'package:aves/widgets/common/grid/sections/section_layout.dart'; import 'package:aves/widgets/common/grid/sections/section_layout.dart';
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart'; import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> { class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
int _currentIndex = 0; int _currentIndex = 0;
@ -88,7 +87,7 @@ class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
section: section, section: section,
sectionGridIndex: listIndex * columnCount, sectionGridIndex: listIndex * columnCount,
sectionChildIndex: sectionChildIndex, sectionChildIndex: sectionChildIndex,
itemIndexRange: () => Tuple2( itemIndexRange: () => (
(sectionChildIndex - 1) * columnCount, (sectionChildIndex - 1) * columnCount,
sectionChildIndex * columnCount, sectionChildIndex * columnCount,
), ),

View file

@ -11,7 +11,6 @@ import 'package:aves/widgets/common/grid/sections/section_layout_builder.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';
import 'package:tuple/tuple.dart';
class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> { class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
int _currentIndex = 0; int _currentIndex = 0;
@ -110,7 +109,7 @@ class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
section: section, section: section,
sectionGridIndex: sectionGridIndex, sectionGridIndex: sectionGridIndex,
sectionChildIndex: sectionChildIndex, sectionChildIndex: sectionChildIndex,
itemIndexRange: () => isHeader ? const Tuple2(0, 0) : Tuple2(row.firstIndex, row.lastIndex + 1), itemIndexRange: () => isHeader ? const (0, 0) : (row.firstIndex, row.lastIndex + 1),
sectionKey: sectionKey, sectionKey: sectionKey,
headerExtent: headerExtent, headerExtent: headerExtent,
itemSizes: row.itemWidths.map((v) => Size(v, row.height)).toList(), itemSizes: row.itemWidths.map((v) => Size(v, row.height)).toList(),

View file

@ -6,7 +6,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
typedef TileBuilder<T> = Widget Function(T item, Size tileSize); typedef TileBuilder<T> = Widget Function(T item, Size tileSize);
@ -54,7 +53,7 @@ abstract class SectionLayoutBuilder<T> {
required List<T> section, required List<T> section,
required int sectionGridIndex, required int sectionGridIndex,
required int sectionChildIndex, required int sectionChildIndex,
required Tuple2<int, int> Function() itemIndexRange, required (int, int) Function() itemIndexRange,
required SectionKey sectionKey, required SectionKey sectionKey,
required double headerExtent, required double headerExtent,
required List<Size> itemSizes, required List<Size> itemSizes,
@ -68,8 +67,8 @@ abstract class SectionLayoutBuilder<T> {
final sectionItemCount = section.length; final sectionItemCount = section.length;
final itemMinMax = itemIndexRange(); final itemMinMax = itemIndexRange();
final minItemIndex = itemMinMax.item1.clamp(0, sectionItemCount); final minItemIndex = itemMinMax.$1.clamp(0, sectionItemCount);
final maxItemIndex = itemMinMax.item2.clamp(0, sectionItemCount); final maxItemIndex = itemMinMax.$2.clamp(0, sectionItemCount);
final childrenCount = maxItemIndex - minItemIndex; final childrenCount = maxItemIndex - minItemIndex;
final children = <Widget>[]; final children = <Widget>[];
for (var i = 0; i < childrenCount; i++) { for (var i = 0; i < childrenCount; i++) {

View file

@ -8,7 +8,6 @@ import 'package:aves/widgets/dialogs/item_picker.dart';
import 'package:aves/widgets/dialogs/pick_dialogs/item_pick_page.dart'; import 'package:aves/widgets/dialogs/pick_dialogs/item_pick_page.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
import 'aves_dialog.dart'; import 'aves_dialog.dart';
@ -142,7 +141,7 @@ class _AddShortcutDialogState extends State<AddShortcutDialog> {
void _submit(BuildContext context) { void _submit(BuildContext context) {
if (_isValidNotifier.value) { if (_isValidNotifier.value) {
Navigator.maybeOf(context)?.pop(Tuple2<AvesEntry?, String>(_coverEntry, _nameController.text)); Navigator.maybeOf(context)?.pop<(AvesEntry?, String)>((_coverEntry, _nameController.text));
} }
} }
} }

View file

@ -20,7 +20,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class CoverSelectionDialog extends StatefulWidget { class CoverSelectionDialog extends StatefulWidget {
static const routeName = '/dialog/select_cover'; static const routeName = '/dialog/select_cover';
@ -81,8 +80,8 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[ final tabs = <(Tab, Widget)>[
Tuple2( (
_buildTab( _buildTab(
context, context,
const Key('tab-entry'), const Key('tab-entry'),
@ -92,7 +91,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
Column(children: _buildEntryOptions()), Column(children: _buildEntryOptions()),
), ),
if (showAppTab) if (showAppTab)
Tuple2( (
_buildTab( _buildTab(
context, context,
const Key('tab-package'), const Key('tab-package'),
@ -102,7 +101,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
Column(children: _buildAppOptions()), Column(children: _buildAppOptions()),
), ),
if (showColorTab) if (showColorTab)
Tuple2( (
_buildTab( _buildTab(
context, context,
const Key('tab-color'), const Key('tab-color'),
@ -131,7 +130,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: TabBar( child: TabBar(
indicatorWeight: tabIndicatorWeight, indicatorWeight: tabIndicatorWeight,
tabs: tabs.map((t) => t.item1).toList(), tabs: tabs.map((t) => t.$1).toList(),
), ),
), ),
ConstrainedBox( ConstrainedBox(
@ -140,7 +139,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: tabs children: tabs
.map((t) => SingleChildScrollView( .map((t) => SingleChildScrollView(
child: t.item2, child: t.$2,
)) ))
.toList(), .toList(),
), ),
@ -165,7 +164,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
final entry = _isCustomEntry ? _customEntry : null; final entry = _isCustomEntry ? _customEntry : null;
final package = _isCustomPackage ? _customPackage : null; final package = _isCustomPackage ? _customPackage : null;
final color = _isCustomColor ? _customColor : null; final color = _isCustomColor ? _customColor : null;
return Navigator.maybeOf(context)?.pop(Tuple3<AvesEntry?, String?, Color?>(entry, package, color)); return Navigator.maybeOf(context)?.pop<(AvesEntry?, String?, Color?)>((entry, package, color));
}, },
child: Text(l10n.applyButtonLabel), child: Text(l10n.applyButtonLabel),
) )

View file

@ -27,7 +27,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
Future<String?> pickAlbum({ Future<String?> pickAlbum({
required BuildContext context, required BuildContext context,
@ -94,8 +93,8 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListenableProvider<ValueNotifier<AppMode>>.value( return ListenableProvider<ValueNotifier<AppMode>>.value(
value: ValueNotifier(AppMode.pickFilterInternal), value: ValueNotifier(AppMode.pickFilterInternal),
child: Selector<Settings, Tuple2<AlbumChipGroupFactor, ChipSortFactor>>( child: Selector<Settings, (AlbumChipGroupFactor, ChipSortFactor)>(
selector: (context, s) => Tuple2(s.albumGroupFactor, s.albumSortFactor), selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor),
builder: (context, s, child) { builder: (context, s, child) {
return StreamBuilder( return StreamBuilder(
stream: source.eventBus.on<AlbumsChangedEvent>(), stream: source.eventBus.on<AlbumsChangedEvent>(),

View file

@ -11,14 +11,13 @@ import 'package:aves/widgets/common/identity/highlight_title.dart';
import 'package:aves/widgets/common/tile_extent_controller.dart'; import 'package:aves/widgets/common/tile_extent_controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'aves_dialog.dart'; import 'aves_dialog.dart';
class TileViewDialog<S, G, L> extends StatefulWidget { class TileViewDialog<S, G, L> extends StatefulWidget {
static const routeName = '/dialog/tile_view'; static const routeName = '/dialog/tile_view';
final Tuple4<S?, G?, L?, bool> initialValue; final (S? sort, G? group, L? layout, bool reverse) initialValue;
final List<TileViewDialogOption<S>> sortOptions; final List<TileViewDialogOption<S>> sortOptions;
final List<TileViewDialogOption<G>> groupOptions; final List<TileViewDialogOption<G>> groupOptions;
final List<TileViewDialogOption<L>> layoutOptions; final List<TileViewDialogOption<L>> layoutOptions;
@ -63,10 +62,10 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
void initState() { void initState() {
super.initState(); super.initState();
final initialValue = widget.initialValue; final initialValue = widget.initialValue;
_selectedSort = initialValue.item1; _selectedSort = initialValue.$1;
_selectedGroup = initialValue.item2; _selectedGroup = initialValue.$2;
_selectedLayout = initialValue.item3; _selectedLayout = initialValue.$3;
_reverseSort = initialValue.item4; _reverseSort = initialValue.$4;
final extentController = tileExtentController; final extentController = tileExtentController;
final columnRange = extentController.effectiveColumnRange; final columnRange = extentController.effectiveColumnRange;
@ -147,7 +146,7 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
key: const Key('button-apply'), key: const Key('button-apply'),
onPressed: () { onPressed: () {
tileExtentController.setUserPreferredColumnCount(_columnCountNotifier.value); tileExtentController.setUserPreferredColumnCount(_columnCountNotifier.value);
Navigator.maybeOf(context)?.pop(Tuple4(_selectedSort, _selectedGroup, _selectedLayout, _reverseSort)); Navigator.maybeOf(context)?.pop<(S?, G?, L?, bool)>((_selectedSort, _selectedGroup, _selectedLayout, _reverseSort));
}, },
child: Text(l10n.applyButtonLabel), child: Text(l10n.applyButtonLabel),
) )

View file

@ -5,7 +5,6 @@ import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves/widgets/dialogs/selection_dialogs/radio_list_tile.dart'; import 'package:aves/widgets/dialogs/selection_dialogs/radio_list_tile.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class WallpaperSettingsDialog extends StatefulWidget { class WallpaperSettingsDialog extends StatefulWidget {
static const routeName = '/dialog/wallpaper_settings'; static const routeName = '/dialog/wallpaper_settings';
@ -43,7 +42,7 @@ class _WallpaperSettingsDialogState extends State<WallpaperSettingsDialog> {
actions: [ actions: [
const CancelButton(), const CancelButton(),
TextButton( TextButton(
onPressed: () => Navigator.maybeOf(context)?.pop(Tuple2<WallpaperTarget, bool>(_selectedTarget, _useScrollEffect)), onPressed: () => Navigator.maybeOf(context)?.pop<(WallpaperTarget, bool)>((_selectedTarget, _useScrollEffect)),
child: Text(context.l10n.applyButtonLabel), child: Text(context.l10n.applyButtonLabel),
), ),
], ],

View file

@ -13,7 +13,6 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class TransformControlPanel extends StatefulWidget { class TransformControlPanel extends StatefulWidget {
final AvesEntry entry; final AvesEntry entry;
@ -32,7 +31,7 @@ class TransformControlPanel extends StatefulWidget {
} }
class _TransformControlPanelState extends State<TransformControlPanel> with TickerProviderStateMixin { class _TransformControlPanelState extends State<TransformControlPanel> with TickerProviderStateMixin {
late final List<Tuple2<WidgetBuilder, WidgetBuilder>> _tabs; late final List<(WidgetBuilder, WidgetBuilder)> _tabs;
late final TabController _tabController; late final TabController _tabController;
static const padding = EditorControlPanel.padding; static const padding = EditorControlPanel.padding;
@ -41,11 +40,11 @@ class _TransformControlPanelState extends State<TransformControlPanel> with Tick
void initState() { void initState() {
super.initState(); super.initState();
_tabs = [ _tabs = [
Tuple2( (
(context) => Tab(text: context.l10n.editorTransformCrop), (context) => Tab(text: context.l10n.editorTransformCrop),
(context) => const CropControlPanel(), (context) => const CropControlPanel(),
), ),
Tuple2( (
(context) => Tab(text: context.l10n.editorTransformRotate), (context) => Tab(text: context.l10n.editorTransformRotate),
(context) => const RotationControlPanel(), (context) => const RotationControlPanel(),
), ),
@ -74,7 +73,7 @@ class _TransformControlPanelState extends State<TransformControlPanel> with Tick
builder: (context, child) { builder: (context, child) {
return AnimatedSwitcher( return AnimatedSwitcher(
duration: context.select<DurationsData, Duration>((v) => v.formTransition), duration: context.select<DurationsData, Duration>((v) => v.formTransition),
child: _tabs[_tabController.index].item2(context), child: _tabs[_tabController.index].$2(context),
); );
}, },
), ),
@ -87,7 +86,7 @@ class _TransformControlPanelState extends State<TransformControlPanel> with Tick
), ),
Expanded( Expanded(
child: TabBar( child: TabBar(
tabs: _tabs.map((v) => v.item1(context)).toList(), tabs: _tabs.map((v) => v.$1(context)).toList(),
controller: _tabController, controller: _tabController,
padding: const EdgeInsets.symmetric(horizontal: padding), padding: const EdgeInsets.symmetric(horizontal: padding),
indicatorSize: TabBarIndicatorSize.label, indicatorSize: TabBarIndicatorSize.label,

View file

@ -17,7 +17,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
class AlbumListPage extends StatelessWidget { class AlbumListPage extends StatelessWidget {
static const routeName = '/albums'; static const routeName = '/albums';
@ -27,12 +26,12 @@ class AlbumListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
return Selector<Settings, Tuple4<AlbumChipGroupFactor, ChipSortFactor, bool, Set<CollectionFilter>>>( return Selector<Settings, (AlbumChipGroupFactor, ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => Tuple4(s.albumGroupFactor, s.albumSortFactor, s.albumSortReverse, s.pinnedFilters), selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor, s.albumSortReverse, s.pinnedFilters),
shouldRebuild: (t1, t2) { shouldRebuild: (t1, t2) {
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN` // `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
const eq = DeepCollectionEquality(); const eq = DeepCollectionEquality();
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3) && eq.equals(t1.item4, t2.item4)); return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3) && eq.equals(t1.$4, t2.$4));
}, },
builder: (context, s, child) { builder: (context, s, child) {
return ValueListenableBuilder<bool>( return ValueListenableBuilder<bool>(

View file

@ -32,7 +32,6 @@ import 'package:collection/collection.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';
import 'package:tuple/tuple.dart';
class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with EntryStorageMixin { class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with EntryStorageMixin {
final Iterable<FilterGridItem<AlbumFilter>> _items; final Iterable<FilterGridItem<AlbumFilter>> _items;
@ -168,14 +167,14 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
@override @override
Future<void> configureView(BuildContext context) async { Future<void> configureView(BuildContext context) async {
final initialValue = Tuple4( final initialValue = (
sortFactor, sortFactor,
settings.albumGroupFactor, settings.albumGroupFactor,
tileLayout, tileLayout,
sortReverse, sortReverse,
); );
final extentController = context.read<TileExtentController>(); final extentController = context.read<TileExtentController>();
final value = await showDialog<Tuple4<ChipSortFactor?, AlbumChipGroupFactor?, TileLayout?, bool>>( final value = await showDialog<(ChipSortFactor?, AlbumChipGroupFactor?, TileLayout?, bool)>(
context: context, context: context,
builder: (context) { builder: (context) {
return TileViewDialog<ChipSortFactor, AlbumChipGroupFactor, TileLayout>( return TileViewDialog<ChipSortFactor, AlbumChipGroupFactor, TileLayout>(
@ -192,10 +191,10 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
// wait for the dialog to hide as applying the change may block the UI // wait for the dialog to hide as applying the change may block the UI
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation); await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
if (value != null && initialValue != value) { if (value != null && initialValue != value) {
sortFactor = value.item1!; sortFactor = value.$1!;
settings.albumGroupFactor = value.item2!; settings.albumGroupFactor = value.$2!;
tileLayout = value.item3!; tileLayout = value.$3!;
sortReverse = value.item4; sortReverse = value.$4;
} }
} }

View file

@ -31,7 +31,6 @@ import 'package:collection/collection.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';
import 'package:tuple/tuple.dart';
abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, VaultAwareMixin { abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, VaultAwareMixin {
Iterable<FilterGridItem<T>> get allItems; Iterable<FilterGridItem<T>> get allItems;
@ -216,14 +215,14 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
} }
Future<void> configureView(BuildContext context) async { Future<void> configureView(BuildContext context) async {
final initialValue = Tuple4( final initialValue = (
sortFactor, sortFactor,
null, null,
tileLayout, tileLayout,
sortReverse, sortReverse,
); );
final extentController = context.read<TileExtentController>(); final extentController = context.read<TileExtentController>();
final value = await showDialog<Tuple4<ChipSortFactor?, void, TileLayout?, bool>>( final value = await showDialog<(ChipSortFactor?, void, TileLayout?, bool)>(
context: context, context: context,
builder: (context) { builder: (context) {
return TileViewDialog<ChipSortFactor, void, TileLayout>( return TileViewDialog<ChipSortFactor, void, TileLayout>(
@ -239,9 +238,9 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
// wait for the dialog to hide as applying the change may block the UI // wait for the dialog to hide as applying the change may block the UI
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation); await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
if (value != null && initialValue != value) { if (value != null && initialValue != value) {
sortFactor = value.item1!; sortFactor = value.$1!;
tileLayout = value.item3!; tileLayout = value.$3!;
sortReverse = value.item4; sortReverse = value.$4;
} }
} }
@ -328,7 +327,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
final existingCover = covers.of(filter); final existingCover = covers.of(filter);
final entryId = existingCover?.$1; final entryId = existingCover?.$1;
final customEntry = entryId != null ? context.read<CollectionSource>().visibleEntries.firstWhereOrNull((entry) => entry.id == entryId) : null; final customEntry = entryId != null ? context.read<CollectionSource>().visibleEntries.firstWhereOrNull((entry) => entry.id == entryId) : null;
final selectedCover = await showDialog<Tuple3<AvesEntry?, String?, Color?>>( final selectedCover = await showDialog<(AvesEntry?, String?, Color?)>(
context: context, context: context,
builder: (context) => CoverSelectionDialog( builder: (context) => CoverSelectionDialog(
filter: filter, filter: filter,
@ -344,9 +343,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
context.read<AvesColorsData>().clearAppColor(filter.album); context.read<AvesColorsData>().clearAppColor(filter.album);
} }
final selectedEntry = selectedCover.item1; final (selectedEntry, selectedPackage, selectedColor) = selectedCover;
final selectedPackage = selectedCover.item2;
final selectedColor = selectedCover.item3;
await covers.set( await covers.set(
filter: filter, filter: filter,
entryId: selectedEntry?.id, entryId: selectedEntry?.id,

View file

@ -46,7 +46,6 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
typedef QueryTest<T extends CollectionFilter> = List<FilterGridItem<T>> Function(BuildContext context, List<FilterGridItem<T>> filters, String query); typedef QueryTest<T extends CollectionFilter> = List<FilterGridItem<T>> Function(BuildContext context, List<FilterGridItem<T>> filters, String query);
@ -317,13 +316,10 @@ class _FilterGridContentState<T extends CollectionFilter> extends State<_FilterG
final sectionedListLayoutProvider = ValueListenableBuilder<double>( final sectionedListLayoutProvider = ValueListenableBuilder<double>(
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier), valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
builder: (context, thumbnailExtent, child) { builder: (context, thumbnailExtent, child) {
return Selector<TileExtentController, Tuple4<double, int, double, double>>( return Selector<TileExtentController, (double, int, double, double)>(
selector: (context, c) => Tuple4(c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding), selector: (context, c) => (c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
builder: (context, c, child) { builder: (context, c, child) {
final scrollableWidth = c.item1; final (scrollableWidth, columnCount, tileSpacing, horizontalPadding) = c;
final columnCount = c.item2;
final tileSpacing = c.item3;
final horizontalPadding = c.item4;
// do not listen for animation delay change // do not listen for animation delay change
final target = context.read<DurationsData>().staggeredAnimationPageTarget; final target = context.read<DurationsData>().staggeredAnimationPageTarget;
final tileAnimationDelay = context.read<TileExtentController>().getTileAnimationDelay(target); final tileAnimationDelay = context.read<TileExtentController>().getTileAnimationDelay(target);
@ -571,9 +567,7 @@ class _FilterScaler<T extends CollectionFilter> extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textScaleFactor = MediaQuery.textScaleFactorOf(context); final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final metrics = context.select<TileExtentController, Tuple2<double, double>>((v) => Tuple2(v.spacing, v.horizontalPadding)); final (tileSpacing, horizontalPadding) = context.select<TileExtentController, (double, double)>((v) => (v.spacing, v.horizontalPadding));
final tileSpacing = metrics.item1;
final horizontalPadding = metrics.item2;
final brightness = Theme.of(context).brightness; final brightness = Theme.of(context).brightness;
return GridScaleGestureDetector<FilterGridItem<T>>( return GridScaleGestureDetector<FilterGridItem<T>>(
scrollableKey: scrollableKey, scrollableKey: scrollableKey,

View file

@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
class CountryListPage extends StatelessWidget { class CountryListPage extends StatelessWidget {
static const routeName = '/countries'; static const routeName = '/countries';
@ -23,12 +22,12 @@ class CountryListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>( return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => Tuple3(s.countrySortFactor, s.countrySortReverse, s.pinnedFilters), selector: (context, s) => (s.countrySortFactor, s.countrySortReverse, s.pinnedFilters),
shouldRebuild: (t1, t2) { shouldRebuild: (t1, t2) {
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN` // `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
const eq = DeepCollectionEquality(); const eq = DeepCollectionEquality();
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3)); return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
}, },
builder: (context, s, child) { builder: (context, s, child) {
return StreamBuilder( return StreamBuilder(

View file

@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
class PlaceListPage extends StatelessWidget { class PlaceListPage extends StatelessWidget {
static const routeName = '/places'; static const routeName = '/places';
@ -23,12 +22,12 @@ class PlaceListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>( return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => Tuple3(s.placeSortFactor, s.placeSortReverse, s.pinnedFilters), selector: (context, s) => (s.placeSortFactor, s.placeSortReverse, s.pinnedFilters),
shouldRebuild: (t1, t2) { shouldRebuild: (t1, t2) {
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN` // `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
const eq = DeepCollectionEquality(); const eq = DeepCollectionEquality();
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3)); return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
}, },
builder: (context, s, child) { builder: (context, s, child) {
return StreamBuilder( return StreamBuilder(

View file

@ -14,7 +14,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
class StateListPage extends StatelessWidget { class StateListPage extends StatelessWidget {
static const routeName = '/states'; static const routeName = '/states';
@ -29,12 +28,12 @@ class StateListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>( return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => Tuple3(s.stateSortFactor, s.stateSortReverse, s.pinnedFilters), selector: (context, s) => (s.stateSortFactor, s.stateSortReverse, s.pinnedFilters),
shouldRebuild: (t1, t2) { shouldRebuild: (t1, t2) {
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN` // `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
const eq = DeepCollectionEquality(); const eq = DeepCollectionEquality();
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3)); return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
}, },
builder: (context, s, child) { builder: (context, s, child) {
return StreamBuilder( return StreamBuilder(

View file

@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
class TagListPage extends StatelessWidget { class TagListPage extends StatelessWidget {
static const routeName = '/tags'; static const routeName = '/tags';
@ -23,12 +22,12 @@ class TagListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>( return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => Tuple3(s.tagSortFactor, s.tagSortReverse, s.pinnedFilters), selector: (context, s) => (s.tagSortFactor, s.tagSortReverse, s.pinnedFilters),
shouldRebuild: (t1, t2) { shouldRebuild: (t1, t2) {
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN` // `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
const eq = DeepCollectionEquality(); const eq = DeepCollectionEquality();
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3)); return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
}, },
builder: (context, s, child) { builder: (context, s, child) {
return StreamBuilder( return StreamBuilder(

View file

@ -15,7 +15,6 @@ import 'package:aves/widgets/settings/navigation/drawer_tab_albums.dart';
import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart'; import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class NavigationDrawerEditorPage extends StatefulWidget { class NavigationDrawerEditorPage extends StatefulWidget {
static const routeName = '/settings/navigation_drawer'; static const routeName = '/settings/navigation_drawer';
@ -65,8 +64,8 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[ final tabs = <(Tab, Widget)>[
Tuple2( (
Tab(text: l10n.settingsNavigationDrawerTabTypes), Tab(text: l10n.settingsNavigationDrawerTabTypes),
DrawerFixedListTab<CollectionFilter?>( DrawerFixedListTab<CollectionFilter?>(
items: _typeItems, items: _typeItems,
@ -75,13 +74,13 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
title: (item) => DrawerFilterTitle(filter: item), title: (item) => DrawerFilterTitle(filter: item),
), ),
), ),
Tuple2( (
Tab(text: l10n.settingsNavigationDrawerTabAlbums), Tab(text: l10n.settingsNavigationDrawerTabAlbums),
DrawerAlbumTab( DrawerAlbumTab(
items: _albumItems, items: _albumItems,
), ),
), ),
Tuple2( (
Tab(text: l10n.settingsNavigationDrawerTabPages), Tab(text: l10n.settingsNavigationDrawerTabPages),
DrawerFixedListTab<String>( DrawerFixedListTab<String>(
items: _pageItems, items: _pageItems,
@ -99,7 +98,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
automaticallyImplyLeading: !settings.useTvLayout, automaticallyImplyLeading: !settings.useTvLayout,
title: Text(l10n.settingsNavigationDrawerEditorPageTitle), title: Text(l10n.settingsNavigationDrawerEditorPageTitle),
bottom: TabBar( bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(), tabs: tabs.map((t) => t.$1).toList(),
), ),
), ),
body: WillPopScope( body: WillPopScope(
@ -111,7 +110,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
}, },
child: SafeArea( child: SafeArea(
child: TabBarView( child: TabBarView(
children: tabs.map((t) => t.item2).toList(), children: tabs.map((t) => t.$2).toList(),
), ),
), ),
), ),

View file

@ -13,7 +13,6 @@ import 'package:aves/widgets/settings/privacy/file_picker/file_picker_page.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';
import 'package:tuple/tuple.dart';
class HiddenItemsPage extends StatelessWidget { class HiddenItemsPage extends StatelessWidget {
static const routeName = '/settings/hidden_items'; static const routeName = '/settings/hidden_items';
@ -23,12 +22,12 @@ class HiddenItemsPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[ final tabs = <(Tab, Widget)>[
Tuple2( (
Tab(text: l10n.settingsHiddenItemsTabFilters), Tab(text: l10n.settingsHiddenItemsTabFilters),
const _HiddenFilters(), const _HiddenFilters(),
), ),
Tuple2( (
Tab(text: l10n.settingsHiddenItemsTabPaths), Tab(text: l10n.settingsHiddenItemsTabPaths),
const _HiddenPaths(), const _HiddenPaths(),
), ),
@ -41,12 +40,12 @@ class HiddenItemsPage extends StatelessWidget {
automaticallyImplyLeading: !settings.useTvLayout, automaticallyImplyLeading: !settings.useTvLayout,
title: Text(l10n.settingsHiddenItemsPageTitle), title: Text(l10n.settingsHiddenItemsPageTitle),
bottom: TabBar( bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(), tabs: tabs.map((t) => t.$1).toList(),
), ),
), ),
body: SafeArea( body: SafeArea(
child: TabBarView( child: TabBarView(
children: tabs.map((t) => t.item2).toList(), children: tabs.map((t) => t.$2).toList(),
), ),
), ),
), ),

View file

@ -5,7 +5,6 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/quick_actions/editor_page.dart'; import 'package:aves/widgets/settings/common/quick_actions/editor_page.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class CollectionActionEditorPage extends StatelessWidget { class CollectionActionEditorPage extends StatelessWidget {
static const routeName = '/settings/collection_actions'; static const routeName = '/settings/collection_actions';
@ -15,8 +14,8 @@ class CollectionActionEditorPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[ final tabs = <(Tab, Widget)>[
Tuple2( (
Tab(text: l10n.settingsCollectionQuickActionTabBrowsing), Tab(text: l10n.settingsCollectionQuickActionTabBrowsing),
QuickActionEditorBody<EntrySetAction>( QuickActionEditorBody<EntrySetAction>(
bannerText: context.l10n.settingsCollectionBrowsingQuickActionEditorBanner, bannerText: context.l10n.settingsCollectionBrowsingQuickActionEditorBanner,
@ -27,7 +26,7 @@ class CollectionActionEditorPage extends StatelessWidget {
save: (actions) => settings.collectionBrowsingQuickActions = actions, save: (actions) => settings.collectionBrowsingQuickActions = actions,
), ),
), ),
Tuple2( (
Tab(text: l10n.settingsCollectionQuickActionTabSelecting), Tab(text: l10n.settingsCollectionQuickActionTabSelecting),
QuickActionEditorBody<EntrySetAction>( QuickActionEditorBody<EntrySetAction>(
bannerText: context.l10n.settingsCollectionSelectionQuickActionEditorBanner, bannerText: context.l10n.settingsCollectionSelectionQuickActionEditorBanner,
@ -49,12 +48,12 @@ class CollectionActionEditorPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(context.l10n.settingsCollectionQuickActionEditorPageTitle), title: Text(context.l10n.settingsCollectionQuickActionEditorPageTitle),
bottom: TabBar( bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(), tabs: tabs.map((t) => t.$1).toList(),
), ),
), ),
body: SafeArea( body: SafeArea(
child: TabBarView( child: TabBarView(
children: tabs.map((t) => t.item2).toList(), children: tabs.map((t) => t.$2).toList(),
), ),
), ),
), ),

View file

@ -6,7 +6,6 @@ import 'package:aves/widgets/settings/common/tiles.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class ViewerOverlayPage extends StatelessWidget { class ViewerOverlayPage extends StatelessWidget {
static const routeName = '/settings/viewer/overlay'; static const routeName = '/settings/viewer/overlay';
@ -36,11 +35,10 @@ class ViewerOverlayPage extends StatelessWidget {
title: context.l10n.settingsViewerShowInformation, title: context.l10n.settingsViewerShowInformation,
subtitle: context.l10n.settingsViewerShowInformationSubtitle, subtitle: context.l10n.settingsViewerShowInformationSubtitle,
), ),
Selector<Settings, Tuple2<bool, bool>>( Selector<Settings, (bool, bool)>(
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayRatingTags), selector: (context, s) => (s.showOverlayInfo, s.showOverlayRatingTags),
builder: (context, s, child) { builder: (context, s, child) {
final showInfo = s.item1; final (showInfo, current) = s;
final current = s.item2;
return SwitchListTile( return SwitchListTile(
value: current, value: current,
onChanged: showInfo ? (v) => settings.showOverlayRatingTags = v : null, onChanged: showInfo ? (v) => settings.showOverlayRatingTags = v : null,
@ -48,11 +46,10 @@ class ViewerOverlayPage extends StatelessWidget {
); );
}, },
), ),
Selector<Settings, Tuple2<bool, bool>>( Selector<Settings, (bool, bool)>(
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayShootingDetails), selector: (context, s) => (s.showOverlayInfo, s.showOverlayShootingDetails),
builder: (context, s, child) { builder: (context, s, child) {
final showInfo = s.item1; final (showInfo, current) = s;
final current = s.item2;
return SwitchListTile( return SwitchListTile(
value: current, value: current,
onChanged: showInfo ? (v) => settings.showOverlayShootingDetails = v : null, onChanged: showInfo ? (v) => settings.showOverlayShootingDetails = v : null,
@ -60,11 +57,10 @@ class ViewerOverlayPage extends StatelessWidget {
); );
}, },
), ),
Selector<Settings, Tuple2<bool, bool>>( Selector<Settings, (bool, bool)>(
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayDescription), selector: (context, s) => (s.showOverlayInfo, s.showOverlayDescription),
builder: (context, s, child) { builder: (context, s, child) {
final showInfo = s.item1; final (showInfo, current) = s;
final current = s.item2;
return SwitchListTile( return SwitchListTile(
value: current, value: current,
onChanged: showInfo ? (v) => settings.showOverlayDescription = v : null, onChanged: showInfo ? (v) => settings.showOverlayDescription = v : null,

View file

@ -39,7 +39,6 @@ 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';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, SingleEntryEditorMixin, EntryStorageMixin, VaultAwareMixin { class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, SingleEntryEditorMixin, EntryStorageMixin, VaultAwareMixin {
final AvesEntry mainEntry, pageEntry; final AvesEntry mainEntry, pageEntry;
@ -325,7 +324,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
} }
Future<void> _addShortcut(BuildContext context, AvesEntry targetEntry) async { Future<void> _addShortcut(BuildContext context, AvesEntry targetEntry) async {
final result = await showDialog<Tuple2<AvesEntry?, String>>( final result = await showDialog<(AvesEntry?, String)>(
context: context, context: context,
builder: (context) => AddShortcutDialog( builder: (context) => AddShortcutDialog(
defaultName: targetEntry.bestTitle ?? '', defaultName: targetEntry.bestTitle ?? '',
@ -334,7 +333,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
); );
if (result == null) return; if (result == null) return;
final name = result.item2; final name = result.$2;
if (name.isEmpty) return; if (name.isEmpty) return;
await appService.pinToHomeScreen(name, targetEntry, uri: targetEntry.uri); await appService.pinToHomeScreen(name, targetEntry, uri: targetEntry.uri);

View file

@ -12,7 +12,6 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class ViewerDebugPage extends StatelessWidget { class ViewerDebugPage extends StatelessWidget {
static const routeName = '/viewer/debug'; static const routeName = '/viewer/debug';
@ -26,11 +25,11 @@ class ViewerDebugPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final tabs = <Tuple2<Tab, Widget>>[ final tabs = <(Tab, Widget)>[
Tuple2(const Tab(text: 'Entry'), _buildEntryTabView()), (const Tab(text: 'Entry'), _buildEntryTabView()),
if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) Tuple2(const Tab(text: 'DB'), DbTab(entry: entry)), if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) (const Tab(text: 'DB'), DbTab(entry: entry)),
Tuple2(const Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)), (const Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)),
Tuple2(const Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()), (const Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()),
]; ];
return Directionality( return Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -40,12 +39,12 @@ class ViewerDebugPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: const Text('Debug'), title: const Text('Debug'),
bottom: TabBar( bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(), tabs: tabs.map((t) => t.$1).toList(),
), ),
), ),
body: SafeArea( body: SafeArea(
child: TabBarView( child: TabBarView(
children: tabs.map((t) => t.item2).toList(), children: tabs.map((t) => t.$2).toList(),
), ),
), ),
), ),

View file

@ -9,9 +9,8 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
typedef XmpExtractedCard = Tuple2<Map<String, XmpProp>, List<XmpCardData>?>; typedef XmpExtractedCard = (Map<String, XmpProp>, List<XmpCardData>?);
class XmpCard extends StatefulWidget { class XmpCard extends StatefulWidget {
final String title; final String title;
@ -30,7 +29,7 @@ class XmpCard extends StatefulWidget {
directStruct = structByIndex[null]; directStruct = structByIndex[null];
final length = structByIndex.keys.whereNotNull().fold(0, max); final length = structByIndex.keys.whereNotNull().fold(0, max);
indexedStructs = length > 0 ? [for (var i = 0; i < length; i++) structByIndex[i + 1] ?? const Tuple2({}, null)] : null; indexedStructs = length > 0 ? [for (var i = 0; i < length; i++) structByIndex[i + 1] ?? const ({}, null)] : null;
} }
@override @override
@ -77,8 +76,8 @@ class _XmpCardState extends State<XmpCard> {
valueListenable: _indexNotifier, valueListenable: _indexNotifier,
builder: (context, index, child) { builder: (context, index, child) {
final data = _isIndexed ? indexedStructs![index] : widget.directStruct!; final data = _isIndexed ? indexedStructs![index] : widget.directStruct!;
final props = data.item1.entries.map((kv) => XmpProp(kv.key, kv.value.value)).toList()..sort(); final props = data.$1.entries.map((kv) => XmpProp(kv.key, kv.value.value)).toList()..sort();
final cards = data.item2; final cards = data.$2;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -139,7 +138,7 @@ class _XmpCardState extends State<XmpCard> {
title: card.title, title: card.title,
structByIndex: card.data, structByIndex: card.data,
formatValue: widget.formatValue, formatValue: widget.formatValue,
spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.item1) : null, spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.$1) : null,
), ),
); );
}), }),

View file

@ -19,7 +19,6 @@ import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
@immutable @immutable
class XmpNamespace extends Equatable { class XmpNamespace extends Equatable {
@ -114,7 +113,7 @@ class XmpNamespace extends Equatable {
title: card.title, title: card.title,
structByIndex: card.data, structByIndex: card.data,
formatValue: formatValue, formatValue: formatValue,
spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.item1) : null, spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.$1) : null,
), ),
); );
}), }),
@ -206,8 +205,8 @@ class XmpCardData {
final match = matches.first; final match = matches.first;
final field = match.group(1)!; final field = match.group(1)!;
final fields = data.putIfAbsent(null, () => Tuple2(<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList())); final fields = data.putIfAbsent(null, () => (<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
final _cards = fields.item2; final _cards = fields.$2;
if (_cards != null) { if (_cards != null) {
final fieldProp = XmpProp(field, prop.value); final fieldProp = XmpProp(field, prop.value);
if (_cards.any((v) => v.extract(fieldProp))) { if (_cards.any((v) => v.extract(fieldProp))) {
@ -215,7 +214,7 @@ class XmpCardData {
} }
} }
fields.item1[field] = prop; fields.$1[field] = prop;
return true; return true;
} }
@ -227,8 +226,8 @@ class XmpCardData {
final index = int.parse(match.group(1)!); final index = int.parse(match.group(1)!);
final field = match.group(2)!; final field = match.group(2)!;
final fields = data.putIfAbsent(index, () => Tuple2(<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList())); final fields = data.putIfAbsent(index, () => (<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
final _cards = fields.item2; final _cards = fields.$2;
if (_cards != null) { if (_cards != null) {
final fieldProp = XmpProp(field, prop.value); final fieldProp = XmpProp(field, prop.value);
if (_cards.any((v) => v.extract(fieldProp))) { if (_cards.any((v) => v.extract(fieldProp))) {
@ -236,7 +235,7 @@ class XmpCardData {
} }
} }
fields.item1[field] = prop; fields.$1[field] = prop;
return true; return true;
} }
} }

View file

@ -4,7 +4,6 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:aves/widgets/viewer/info/embedded/notifications.dart'; import 'package:aves/widgets/viewer/info/embedded/notifications.dart';
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:tuple/tuple.dart';
abstract class XmpGoogleNamespace extends XmpNamespace { abstract class XmpGoogleNamespace extends XmpNamespace {
XmpGoogleNamespace({ XmpGoogleNamespace({
@ -13,13 +12,12 @@ abstract class XmpGoogleNamespace extends XmpNamespace {
required super.rawProps, required super.rawProps,
}); });
List<Tuple2<String, String>> get dataProps; List<(String, String)> get dataProps;
@override @override
Map<String, InfoValueSpanBuilder> linkifyValues(List<XmpProp> props) { Map<String, InfoValueSpanBuilder> linkifyValues(List<XmpProp> props) {
return Map.fromEntries(dataProps.map((t) { return Map.fromEntries(dataProps.map((t) {
final dataPropPath = t.item1; final (dataPropPath, mimePropPath) = t;
final mimePropPath = t.item2;
final dataProp = props.firstWhereOrNull((prop) => prop.path == dataPropPath); final dataProp = props.firstWhereOrNull((prop) => prop.path == dataPropPath);
final mimeProp = props.firstWhereOrNull((prop) => prop.path == mimePropPath); final mimeProp = props.firstWhereOrNull((prop) => prop.path == mimePropPath);
return (dataProp != null && mimeProp != null) return (dataProp != null && mimeProp != null)
@ -60,8 +58,8 @@ class XmpGAudioNamespace extends XmpGoogleNamespace {
XmpGAudioNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gAudio); XmpGAudioNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gAudio);
@override @override
List<Tuple2<String, String>> get dataProps => [ List<(String, String)> get dataProps => [
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'), ('${nsPrefix}Data', '${nsPrefix}Mime'),
]; ];
} }
@ -69,8 +67,8 @@ class XmpGCameraNamespace extends XmpGoogleNamespace {
XmpGCameraNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gCamera); XmpGCameraNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gCamera);
@override @override
List<Tuple2<String, String>> get dataProps => [ List<(String, String)> get dataProps => [
Tuple2('${nsPrefix}RelitInputImageData', '${nsPrefix}RelitInputImageMime'), ('${nsPrefix}RelitInputImageData', '${nsPrefix}RelitInputImageMime'),
]; ];
} }
@ -87,9 +85,9 @@ class XmpGDepthNamespace extends XmpGoogleNamespace {
XmpGDepthNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gDepth); XmpGDepthNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gDepth);
@override @override
List<Tuple2<String, String>> get dataProps => [ List<(String, String)> get dataProps => [
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'), ('${nsPrefix}Data', '${nsPrefix}Mime'),
Tuple2('${nsPrefix}Confidence', '${nsPrefix}ConfidenceMime'), ('${nsPrefix}Confidence', '${nsPrefix}ConfidenceMime'),
]; ];
} }
@ -156,7 +154,7 @@ class XmpGImageNamespace extends XmpGoogleNamespace {
XmpGImageNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gImage); XmpGImageNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gImage);
@override @override
List<Tuple2<String, String>> get dataProps => [ List<(String, String)> get dataProps => [
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'), ('${nsPrefix}Data', '${nsPrefix}Mime'),
]; ];
} }

View file

@ -18,7 +18,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class WallpaperButtons extends StatelessWidget with FeedbackMixin { class WallpaperButtons extends StatelessWidget with FeedbackMixin {
final AvesEntry entry; final AvesEntry entry;
@ -57,16 +56,13 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
Future<void> _setWallpaper(BuildContext context) async { Future<void> _setWallpaper(BuildContext context) async {
final l10n = context.l10n; final l10n = context.l10n;
final value = await showDialog<Tuple2<WallpaperTarget, bool>>( final value = await showDialog<(WallpaperTarget, bool)>(
context: context, context: context,
builder: (context) => const WallpaperSettingsDialog(), builder: (context) => const WallpaperSettingsDialog(),
routeSettings: const RouteSettings(name: WallpaperSettingsDialog.routeName), routeSettings: const RouteSettings(name: WallpaperSettingsDialog.routeName),
); );
if (value == null) return; if (value == null) return;
final target = value.item1;
final useScrollEffect = value.item2;
final reportController = StreamController.broadcast(); final reportController = StreamController.broadcast();
unawaited(showOpReport( unawaited(showOpReport(
context: context, context: context,
@ -76,6 +72,7 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
var region = _getVisibleRegion(context); var region = _getVisibleRegion(context);
if (region == null) return; if (region == null) return;
final (target, useScrollEffect) = value;
if (useScrollEffect) { if (useScrollEffect) {
final deltaX = min(region.left, entry.displaySize.width - region.right); final deltaX = min(region.left, entry.displaySize.width - region.right);
region = Rect.fromLTRB(region.left - deltaX, region.top, region.right + deltaX, region.bottom); region = Rect.fromLTRB(region.left - deltaX, region.top, region.right + deltaX, region.bottom);

View file

@ -19,7 +19,6 @@ import 'package:aves_model/aves_model.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';
import 'package:tuple/tuple.dart';
class SlideshowPage extends StatefulWidget { class SlideshowPage extends StatefulWidget {
static const routeName = '/collection/slideshow'; static const routeName = '/collection/slideshow';
@ -151,7 +150,7 @@ class _SlideshowPageState extends State<SlideshowPage> {
); );
} }
Tuple2<bool, bool> get collectionSettings => Tuple2(settings.slideshowShuffle, settings.slideshowVideoPlayback == SlideshowVideoPlayback.skip); (bool, bool) get collectionSettings => (settings.slideshowShuffle, settings.slideshowVideoPlayback == SlideshowVideoPlayback.skip);
Future<void> _showSettings(BuildContext context) async { Future<void> _showSettings(BuildContext context) async {
final oldCollectionSettings = collectionSettings; final oldCollectionSettings = collectionSettings;

View file

@ -5,6 +5,7 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/view_state.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/services/media/media_session_service.dart'; import 'package:aves/services/media/media_session_service.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
@ -18,7 +19,6 @@ import 'package:aves/widgets/viewer/video/conductor.dart';
import 'package:aves/widgets/viewer/view/conductor.dart'; import 'package:aves/widgets/viewer/view/conductor.dart';
import 'package:aves/widgets/viewer/visual/error.dart'; import 'package:aves/widgets/viewer/visual/error.dart';
import 'package:aves/widgets/viewer/visual/raster.dart'; import 'package:aves/widgets/viewer/visual/raster.dart';
import 'package:aves/model/view_state.dart';
import 'package:aves/widgets/viewer/visual/vector.dart'; import 'package:aves/widgets/viewer/visual/vector.dart';
import 'package:aves/widgets/viewer/visual/video/cover.dart'; import 'package:aves/widgets/viewer/visual/video/cover.dart';
import 'package:aves/widgets/viewer/visual/video/subtitle/subtitle.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/subtitle.dart';
@ -29,7 +29,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:decorated_icon/decorated_icon.dart'; import 'package:decorated_icon/decorated_icon.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class EntryPageView extends StatefulWidget { class EntryPageView extends StatefulWidget {
final AvesEntry mainEntry, pageEntry; final AvesEntry mainEntry, pageEntry;
@ -203,16 +202,14 @@ class _EntryPageViewState extends State<EntryPageView> with SingleTickerProvider
final videoDisplaySize = entry.videoDisplaySize(sar); final videoDisplaySize = entry.videoDisplaySize(sar);
final isPureVideo = entry.isPureVideo; final isPureVideo = entry.isPureVideo;
return Selector<Settings, Tuple3<bool, bool, bool>>( return Selector<Settings, (bool, bool, bool)>(
selector: (context, s) => Tuple3( selector: (context, s) => (
isPureVideo && s.videoGestureDoubleTapTogglePlay, isPureVideo && s.videoGestureDoubleTapTogglePlay,
isPureVideo && s.videoGestureSideDoubleTapSeek, isPureVideo && s.videoGestureSideDoubleTapSeek,
isPureVideo && s.videoGestureVerticalDragBrightnessVolume, isPureVideo && s.videoGestureVerticalDragBrightnessVolume,
), ),
builder: (context, s, child) { builder: (context, s, child) {
final playGesture = s.item1; final (playGesture, seekGesture, useVerticalDragGesture) = s;
final seekGesture = s.item2;
final useVerticalDragGesture = s.item3;
final useTapGesture = playGesture || seekGesture; final useTapGesture = playGesture || seekGesture;
MagnifierDoubleTapCallback? onDoubleTap; MagnifierDoubleTapCallback? onDoubleTap;

View file

@ -14,7 +14,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:tuple/tuple.dart';
class RasterImageView extends StatefulWidget { class RasterImageView extends StatefulWidget {
final AvesEntry entry; final AvesEntry entry;
@ -259,10 +258,11 @@ class _RasterImageViewState extends State<RasterImageView> {
viewRect: viewRect, viewRect: viewRect,
); );
if (rects != null) { if (rects != null) {
final (tileRect, regionRect) = rects;
tiles.add(_RegionTile( tiles.add(_RegionTile(
entry: entry, entry: entry,
tileRect: rects.item1, tileRect: tileRect,
regionRect: rects.item2, regionRect: regionRect,
sampleSize: sampleSize, sampleSize: sampleSize,
)); ));
} }
@ -283,7 +283,7 @@ class _RasterImageViewState extends State<RasterImageView> {
return viewOrigin & viewportSize; return viewOrigin & viewportSize;
} }
Tuple2<Rect, Rectangle<int>>? _getTileRects({ (Rect tileRect, Rectangle<int> regionRect)? _getTileRects({
required int x, required int x,
required int y, required int y,
required int regionSide, required int regionSide,
@ -314,7 +314,7 @@ class _RasterImageViewState extends State<RasterImageView> {
} else { } else {
regionRect = Rectangle<int>(x, y, thisRegionWidth, thisRegionHeight); regionRect = Rectangle<int>(x, y, thisRegionWidth, thisRegionHeight);
} }
return Tuple2<Rect, Rectangle<int>>(tileRect, regionRect); return (tileRect, regionRect);
} }
} }

View file

@ -12,7 +12,6 @@ import 'package:aves/widgets/viewer/visual/entry_page_view.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class VectorImageView extends StatefulWidget { class VectorImageView extends StatefulWidget {
final AvesEntry entry; final AvesEntry entry;
@ -233,10 +232,11 @@ class _VectorImageViewState extends State<VectorImageView> {
viewRect: viewRect, viewRect: viewRect,
); );
if (rects != null) { if (rects != null) {
final (tileRect, regionRect) = rects;
tiles.add(_RegionTile( tiles.add(_RegionTile(
entry: entry, entry: entry,
tileRect: rects.item1, tileRect: tileRect,
regionRect: rects.item2, regionRect: regionRect,
scale: svgScale, scale: svgScale,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
backgroundFrameBuilder: backgroundFrameBuilder, backgroundFrameBuilder: backgroundFrameBuilder,
@ -259,7 +259,7 @@ class _VectorImageViewState extends State<VectorImageView> {
return viewOrigin & viewportSize; return viewOrigin & viewportSize;
} }
Tuple2<Rect, Rectangle<double>>? _getTileRects({ (Rect tileRect, Rectangle<double> regionRect)? _getTileRects({
required double x, required double x,
required double y, required double y,
required double regionSide, required double regionSide,
@ -278,7 +278,7 @@ class _VectorImageViewState extends State<VectorImageView> {
if (!viewRect.overlaps(tileRect)) return null; if (!viewRect.overlaps(tileRect)) return null;
final regionRect = Rectangle<double>(x, y, thisRegionWidth, thisRegionHeight); final regionRect = Rectangle<double>(x, y, thisRegionWidth, thisRegionHeight);
return Tuple2<Rect, Rectangle<double>>(tileRect, regionRect); return (tileRect, regionRect);
} }
double _imageScaleForViewScale({ double _imageScaleForViewScale({

View file

@ -1479,14 +1479,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
tuple:
dependency: "direct main"
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View file

@ -111,7 +111,6 @@ dependencies:
git: git:
url: https://github.com/deckerst/aves_streams_channel.git url: https://github.com/deckerst/aves_streams_channel.git
transparent_image: transparent_image:
tuple:
url_launcher: url_launcher:
vector_math: vector_math:
volume_controller: volume_controller: