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,
sourceUrl: 'https://github.com/brianegan/transparent_image',
),
Dependency(
name: 'Tuple',
license: bsd2,
sourceUrl: 'https://github.com/google/tuple.dart',
),
Dependency(
name: 'Vector Math',
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:provider/provider.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:tuple/tuple.dart';
import 'package:url_launcher/url_launcher.dart' as ul;
class AvesApp extends StatefulWidget {
@ -236,16 +235,14 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
: AvesScaffold(
body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(),
);
return Selector<Settings, Tuple3<Locale?, AvesThemeBrightness, bool>>(
selector: (context, s) => Tuple3(
return Selector<Settings, (Locale?, AvesThemeBrightness, bool)>(
selector: (context, s) => (
s.locale,
s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness,
s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor,
),
builder: (context, s, child) {
final settingsLocale = s.item1;
final themeBrightness = s.item2;
final enableDynamicColor = s.item3;
final (settingsLocale, themeBrightness, enableDynamicColor) = s;
AStyles.updateStylesForLocale(settings.appliedLocale);

View file

@ -37,7 +37,6 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class CollectionAppBar extends StatefulWidget {
final ValueNotifier<double> appBarHeightNotifier;
@ -647,14 +646,14 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
}
Future<void> _configureView() async {
final initialValue = Tuple4(
final initialValue = (
settings.collectionSortFactor,
settings.collectionSectionFactor,
settings.getTileLayout(CollectionPage.routeName),
settings.collectionSortReverse,
);
final extentController = context.read<TileExtentController>();
final value = await showDialog<Tuple4<EntrySortFactor?, EntryGroupFactor?, TileLayout?, bool>>(
final value = await showDialog<(EntrySortFactor?, EntryGroupFactor?, TileLayout?, bool)>(
context: context,
builder: (context) {
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
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
if (value != null && initialValue != value) {
settings.collectionSortFactor = value.item1!;
settings.collectionSectionFactor = value.item2!;
settings.setTileLayout(CollectionPage.routeName, value.item3!);
settings.collectionSortReverse = value.item4;
settings.collectionSortFactor = value.$1!;
settings.collectionSectionFactor = value.$2!;
settings.setTileLayout(CollectionPage.routeName, value.$3!);
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:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class CollectionGrid extends StatefulWidget {
final String settingsRouteKey;
@ -132,13 +131,10 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
builder: (context, thumbnailExtent, child) {
assert(thumbnailExtent > 0);
return Selector<TileExtentController, Tuple4<double, int, double, double>>(
selector: (context, c) => Tuple4(c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
return Selector<TileExtentController, (double, int, double, double)>(
selector: (context, c) => (c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
builder: (context, c, child) {
final scrollableWidth = c.item1;
final columnCount = c.item2;
final tileSpacing = c.item3;
final horizontalPadding = c.item4;
final (scrollableWidth, columnCount, tileSpacing, horizontalPadding) = c;
final source = collection.source;
return GridTheme(
extent: thumbnailExtent,
@ -369,9 +365,7 @@ class _CollectionScaler extends StatelessWidget {
@override
Widget build(BuildContext context) {
final metrics = context.select<TileExtentController, Tuple2<double, double>>((v) => Tuple2(v.spacing, v.horizontalPadding));
final tileSpacing = metrics.item1;
final horizontalPadding = metrics.item2;
final (tileSpacing, horizontalPadding) = context.select<TileExtentController, (double, double)>((v) => (v.spacing, v.horizontalPadding));
final brightness = Theme.of(context).brightness;
final borderColor = DecoratedThumbnail.borderColor;
final borderWidth = DecoratedThumbnail.borderWidth(context);

View file

@ -45,7 +45,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, EntryEditorMixin, EntryStorageMixin {
bool isVisible(
@ -709,7 +708,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
final sortedFilters = List<CollectionFilter>.from(filters)..sort();
defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' ');
}
final result = await showDialog<Tuple2<AvesEntry?, String>>(
final result = await showDialog<(AvesEntry?, String)>(
context: context,
builder: (context) => AddShortcutDialog(
defaultName: defaultName ?? '',
@ -719,8 +718,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
);
if (result == null) return;
final coverEntry = result.item1;
final name = result.item2;
final (coverEntry, name) = result;
if (name.isEmpty) return;
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:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:tuple/tuple.dart';
class AnimatedDiffText extends StatefulWidget {
final String text;
@ -71,10 +70,7 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
return Text.rich(
TextSpan(
children: _diffs.map((diff) {
final oldText = diff.item1;
final newText = diff.item2;
final oldSize = diff.item3;
final newSize = diff.item4;
final (oldText, newText, oldSize, newSize) = diff;
final text = (_animation.value == 0 ? oldText : newText) ?? '';
return WidgetSpan(
child: AnimatedSize(
@ -144,26 +140,22 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
..addAll(d.map((diff) {
final text = diff.text;
final size = textSize(text);
switch (diff.operation) {
case Operation.delete:
return Tuple4(text, null, size, Size.zero);
case Operation.insert:
return Tuple4(null, text, Size.zero, size);
case Operation.equal:
default:
return Tuple4(text, text, size, size);
}
return switch (diff.operation) {
Operation.delete => (text, null, size, Size.zero),
Operation.insert => (null, text, Size.zero, size),
Operation.equal || _ => (text, text, size, size),
};
}).fold<List<_TextDiff>>([], (prev, v) {
if (prev.isNotEmpty) {
final last = prev.last;
final prevNewText = last.item2;
final prevNewText = last.$2;
if (prevNewText == null) {
// previous diff is a deletion
final thisOldText = v.item1;
final thisOldText = v.$1;
if (thisOldText == null) {
// this diff is an insertion
// 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];
}
}
@ -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_builder.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
int _currentIndex = 0;
@ -88,7 +87,7 @@ class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
section: section,
sectionGridIndex: listIndex * columnCount,
sectionChildIndex: sectionChildIndex,
itemIndexRange: () => Tuple2(
itemIndexRange: () => (
(sectionChildIndex - 1) * 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:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
int _currentIndex = 0;
@ -110,7 +109,7 @@ class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
section: section,
sectionGridIndex: sectionGridIndex,
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,
headerExtent: headerExtent,
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_staggered_animations/flutter_staggered_animations.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
typedef TileBuilder<T> = Widget Function(T item, Size tileSize);
@ -54,7 +53,7 @@ abstract class SectionLayoutBuilder<T> {
required List<T> section,
required int sectionGridIndex,
required int sectionChildIndex,
required Tuple2<int, int> Function() itemIndexRange,
required (int, int) Function() itemIndexRange,
required SectionKey sectionKey,
required double headerExtent,
required List<Size> itemSizes,
@ -68,8 +67,8 @@ abstract class SectionLayoutBuilder<T> {
final sectionItemCount = section.length;
final itemMinMax = itemIndexRange();
final minItemIndex = itemMinMax.item1.clamp(0, sectionItemCount);
final maxItemIndex = itemMinMax.item2.clamp(0, sectionItemCount);
final minItemIndex = itemMinMax.$1.clamp(0, sectionItemCount);
final maxItemIndex = itemMinMax.$2.clamp(0, sectionItemCount);
final childrenCount = maxItemIndex - minItemIndex;
final children = <Widget>[];
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:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
import 'aves_dialog.dart';
@ -142,7 +141,7 @@ class _AddShortcutDialogState extends State<AddShortcutDialog> {
void _submit(BuildContext context) {
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/rendering.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class CoverSelectionDialog extends StatefulWidget {
static const routeName = '/dialog/select_cover';
@ -81,8 +80,8 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[
Tuple2(
final tabs = <(Tab, Widget)>[
(
_buildTab(
context,
const Key('tab-entry'),
@ -92,7 +91,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
Column(children: _buildEntryOptions()),
),
if (showAppTab)
Tuple2(
(
_buildTab(
context,
const Key('tab-package'),
@ -102,7 +101,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
Column(children: _buildAppOptions()),
),
if (showColorTab)
Tuple2(
(
_buildTab(
context,
const Key('tab-color'),
@ -131,7 +130,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
clipBehavior: Clip.antiAlias,
child: TabBar(
indicatorWeight: tabIndicatorWeight,
tabs: tabs.map((t) => t.item1).toList(),
tabs: tabs.map((t) => t.$1).toList(),
),
),
ConstrainedBox(
@ -140,7 +139,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
physics: const NeverScrollableScrollPhysics(),
children: tabs
.map((t) => SingleChildScrollView(
child: t.item2,
child: t.$2,
))
.toList(),
),
@ -165,7 +164,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
final entry = _isCustomEntry ? _customEntry : null;
final package = _isCustomPackage ? _customPackage : 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),
)

View file

@ -27,7 +27,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
Future<String?> pickAlbum({
required BuildContext context,
@ -94,8 +93,8 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
Widget build(BuildContext context) {
return ListenableProvider<ValueNotifier<AppMode>>.value(
value: ValueNotifier(AppMode.pickFilterInternal),
child: Selector<Settings, Tuple2<AlbumChipGroupFactor, ChipSortFactor>>(
selector: (context, s) => Tuple2(s.albumGroupFactor, s.albumSortFactor),
child: Selector<Settings, (AlbumChipGroupFactor, ChipSortFactor)>(
selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor),
builder: (context, s, child) {
return StreamBuilder(
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:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'aves_dialog.dart';
class TileViewDialog<S, G, L> extends StatefulWidget {
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<G>> groupOptions;
final List<TileViewDialogOption<L>> layoutOptions;
@ -63,10 +62,10 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
void initState() {
super.initState();
final initialValue = widget.initialValue;
_selectedSort = initialValue.item1;
_selectedGroup = initialValue.item2;
_selectedLayout = initialValue.item3;
_reverseSort = initialValue.item4;
_selectedSort = initialValue.$1;
_selectedGroup = initialValue.$2;
_selectedLayout = initialValue.$3;
_reverseSort = initialValue.$4;
final extentController = tileExtentController;
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'),
onPressed: () {
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),
)

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_model/aves_model.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class WallpaperSettingsDialog extends StatefulWidget {
static const routeName = '/dialog/wallpaper_settings';
@ -43,7 +42,7 @@ class _WallpaperSettingsDialogState extends State<WallpaperSettingsDialog> {
actions: [
const CancelButton(),
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),
),
],

View file

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

View file

@ -17,7 +17,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class AlbumListPage extends StatelessWidget {
static const routeName = '/albums';
@ -27,12 +26,12 @@ class AlbumListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();
return Selector<Settings, Tuple4<AlbumChipGroupFactor, ChipSortFactor, bool, Set<CollectionFilter>>>(
selector: (context, s) => Tuple4(s.albumGroupFactor, s.albumSortFactor, s.albumSortReverse, s.pinnedFilters),
return Selector<Settings, (AlbumChipGroupFactor, ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor, s.albumSortReverse, s.pinnedFilters),
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();
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) {
return ValueListenableBuilder<bool>(

View file

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

View file

@ -31,7 +31,6 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, VaultAwareMixin {
Iterable<FilterGridItem<T>> get allItems;
@ -216,14 +215,14 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
}
Future<void> configureView(BuildContext context) async {
final initialValue = Tuple4(
final initialValue = (
sortFactor,
null,
tileLayout,
sortReverse,
);
final extentController = context.read<TileExtentController>();
final value = await showDialog<Tuple4<ChipSortFactor?, void, TileLayout?, bool>>(
final value = await showDialog<(ChipSortFactor?, void, TileLayout?, bool)>(
context: context,
builder: (context) {
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
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
if (value != null && initialValue != value) {
sortFactor = value.item1!;
tileLayout = value.item3!;
sortReverse = value.item4;
sortFactor = value.$1!;
tileLayout = value.$3!;
sortReverse = value.$4;
}
}
@ -328,7 +327,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
final existingCover = covers.of(filter);
final entryId = existingCover?.$1;
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,
builder: (context) => CoverSelectionDialog(
filter: filter,
@ -344,9 +343,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
context.read<AvesColorsData>().clearAppColor(filter.album);
}
final selectedEntry = selectedCover.item1;
final selectedPackage = selectedCover.item2;
final selectedColor = selectedCover.item3;
final (selectedEntry, selectedPackage, selectedColor) = selectedCover;
await covers.set(
filter: filter,
entryId: selectedEntry?.id,

View file

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

View file

@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class CountryListPage extends StatelessWidget {
static const routeName = '/countries';
@ -23,12 +22,12 @@ class CountryListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
selector: (context, s) => Tuple3(s.countrySortFactor, s.countrySortReverse, s.pinnedFilters),
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => (s.countrySortFactor, s.countrySortReverse, s.pinnedFilters),
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();
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) {
return StreamBuilder(

View file

@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class PlaceListPage extends StatelessWidget {
static const routeName = '/places';
@ -23,12 +22,12 @@ class PlaceListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
selector: (context, s) => Tuple3(s.placeSortFactor, s.placeSortReverse, s.pinnedFilters),
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => (s.placeSortFactor, s.placeSortReverse, s.pinnedFilters),
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();
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) {
return StreamBuilder(

View file

@ -14,7 +14,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class StateListPage extends StatelessWidget {
static const routeName = '/states';
@ -29,12 +28,12 @@ class StateListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
selector: (context, s) => Tuple3(s.stateSortFactor, s.stateSortReverse, s.pinnedFilters),
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => (s.stateSortFactor, s.stateSortReverse, s.pinnedFilters),
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();
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) {
return StreamBuilder(

View file

@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class TagListPage extends StatelessWidget {
static const routeName = '/tags';
@ -23,12 +22,12 @@ class TagListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
selector: (context, s) => Tuple3(s.tagSortFactor, s.tagSortReverse, s.pinnedFilters),
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
selector: (context, s) => (s.tagSortFactor, s.tagSortReverse, s.pinnedFilters),
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();
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) {
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:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class NavigationDrawerEditorPage extends StatefulWidget {
static const routeName = '/settings/navigation_drawer';
@ -65,8 +64,8 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[
Tuple2(
final tabs = <(Tab, Widget)>[
(
Tab(text: l10n.settingsNavigationDrawerTabTypes),
DrawerFixedListTab<CollectionFilter?>(
items: _typeItems,
@ -75,13 +74,13 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
title: (item) => DrawerFilterTitle(filter: item),
),
),
Tuple2(
(
Tab(text: l10n.settingsNavigationDrawerTabAlbums),
DrawerAlbumTab(
items: _albumItems,
),
),
Tuple2(
(
Tab(text: l10n.settingsNavigationDrawerTabPages),
DrawerFixedListTab<String>(
items: _pageItems,
@ -99,7 +98,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
automaticallyImplyLeading: !settings.useTvLayout,
title: Text(l10n.settingsNavigationDrawerEditorPageTitle),
bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(),
tabs: tabs.map((t) => t.$1).toList(),
),
),
body: WillPopScope(
@ -111,7 +110,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
},
child: SafeArea(
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/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class HiddenItemsPage extends StatelessWidget {
static const routeName = '/settings/hidden_items';
@ -23,12 +22,12 @@ class HiddenItemsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[
Tuple2(
final tabs = <(Tab, Widget)>[
(
Tab(text: l10n.settingsHiddenItemsTabFilters),
const _HiddenFilters(),
),
Tuple2(
(
Tab(text: l10n.settingsHiddenItemsTabPaths),
const _HiddenPaths(),
),
@ -41,12 +40,12 @@ class HiddenItemsPage extends StatelessWidget {
automaticallyImplyLeading: !settings.useTvLayout,
title: Text(l10n.settingsHiddenItemsPageTitle),
bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(),
tabs: tabs.map((t) => t.$1).toList(),
),
),
body: SafeArea(
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_model/aves_model.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
class CollectionActionEditorPage extends StatelessWidget {
static const routeName = '/settings/collection_actions';
@ -15,8 +14,8 @@ class CollectionActionEditorPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final tabs = <Tuple2<Tab, Widget>>[
Tuple2(
final tabs = <(Tab, Widget)>[
(
Tab(text: l10n.settingsCollectionQuickActionTabBrowsing),
QuickActionEditorBody<EntrySetAction>(
bannerText: context.l10n.settingsCollectionBrowsingQuickActionEditorBanner,
@ -27,7 +26,7 @@ class CollectionActionEditorPage extends StatelessWidget {
save: (actions) => settings.collectionBrowsingQuickActions = actions,
),
),
Tuple2(
(
Tab(text: l10n.settingsCollectionQuickActionTabSelecting),
QuickActionEditorBody<EntrySetAction>(
bannerText: context.l10n.settingsCollectionSelectionQuickActionEditorBanner,
@ -49,12 +48,12 @@ class CollectionActionEditorPage extends StatelessWidget {
appBar: AppBar(
title: Text(context.l10n.settingsCollectionQuickActionEditorPageTitle),
bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(),
tabs: tabs.map((t) => t.$1).toList(),
),
),
body: SafeArea(
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:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class ViewerOverlayPage extends StatelessWidget {
static const routeName = '/settings/viewer/overlay';
@ -36,11 +35,10 @@ class ViewerOverlayPage extends StatelessWidget {
title: context.l10n.settingsViewerShowInformation,
subtitle: context.l10n.settingsViewerShowInformationSubtitle,
),
Selector<Settings, Tuple2<bool, bool>>(
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayRatingTags),
Selector<Settings, (bool, bool)>(
selector: (context, s) => (s.showOverlayInfo, s.showOverlayRatingTags),
builder: (context, s, child) {
final showInfo = s.item1;
final current = s.item2;
final (showInfo, current) = s;
return SwitchListTile(
value: current,
onChanged: showInfo ? (v) => settings.showOverlayRatingTags = v : null,
@ -48,11 +46,10 @@ class ViewerOverlayPage extends StatelessWidget {
);
},
),
Selector<Settings, Tuple2<bool, bool>>(
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayShootingDetails),
Selector<Settings, (bool, bool)>(
selector: (context, s) => (s.showOverlayInfo, s.showOverlayShootingDetails),
builder: (context, s, child) {
final showInfo = s.item1;
final current = s.item2;
final (showInfo, current) = s;
return SwitchListTile(
value: current,
onChanged: showInfo ? (v) => settings.showOverlayShootingDetails = v : null,
@ -60,11 +57,10 @@ class ViewerOverlayPage extends StatelessWidget {
);
},
),
Selector<Settings, Tuple2<bool, bool>>(
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayDescription),
Selector<Settings, (bool, bool)>(
selector: (context, s) => (s.showOverlayInfo, s.showOverlayDescription),
builder: (context, s, child) {
final showInfo = s.item1;
final current = s.item2;
final (showInfo, current) = s;
return SwitchListTile(
value: current,
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/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, SingleEntryEditorMixin, EntryStorageMixin, VaultAwareMixin {
final AvesEntry mainEntry, pageEntry;
@ -325,7 +324,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
}
Future<void> _addShortcut(BuildContext context, AvesEntry targetEntry) async {
final result = await showDialog<Tuple2<AvesEntry?, String>>(
final result = await showDialog<(AvesEntry?, String)>(
context: context,
builder: (context) => AddShortcutDialog(
defaultName: targetEntry.bestTitle ?? '',
@ -334,7 +333,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
);
if (result == null) return;
final name = result.item2;
final name = result.$2;
if (name.isEmpty) return;
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/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class ViewerDebugPage extends StatelessWidget {
static const routeName = '/viewer/debug';
@ -26,11 +25,11 @@ class ViewerDebugPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tabs = <Tuple2<Tab, Widget>>[
Tuple2(const Tab(text: 'Entry'), _buildEntryTabView()),
if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) Tuple2(const Tab(text: 'DB'), DbTab(entry: entry)),
Tuple2(const Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)),
Tuple2(const Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()),
final tabs = <(Tab, Widget)>[
(const Tab(text: 'Entry'), _buildEntryTabView()),
if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) (const Tab(text: 'DB'), DbTab(entry: entry)),
(const Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)),
(const Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()),
];
return Directionality(
textDirection: TextDirection.ltr,
@ -40,12 +39,12 @@ class ViewerDebugPage extends StatelessWidget {
appBar: AppBar(
title: const Text('Debug'),
bottom: TabBar(
tabs: tabs.map((t) => t.item1).toList(),
tabs: tabs.map((t) => t.$1).toList(),
),
),
body: SafeArea(
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:collection/collection.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 {
final String title;
@ -30,7 +29,7 @@ class XmpCard extends StatefulWidget {
directStruct = structByIndex[null];
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
@ -77,8 +76,8 @@ class _XmpCardState extends State<XmpCard> {
valueListenable: _indexNotifier,
builder: (context, index, child) {
final data = _isIndexed ? indexedStructs![index] : widget.directStruct!;
final props = data.item1.entries.map((kv) => XmpProp(kv.key, kv.value.value)).toList()..sort();
final cards = data.item2;
final props = data.$1.entries.map((kv) => XmpProp(kv.key, kv.value.value)).toList()..sort();
final cards = data.$2;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -139,7 +138,7 @@ class _XmpCardState extends State<XmpCard> {
title: card.title,
structByIndex: card.data,
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/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
@immutable
class XmpNamespace extends Equatable {
@ -114,7 +113,7 @@ class XmpNamespace extends Equatable {
title: card.title,
structByIndex: card.data,
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 field = match.group(1)!;
final fields = data.putIfAbsent(null, () => Tuple2(<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
final _cards = fields.item2;
final fields = data.putIfAbsent(null, () => (<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
final _cards = fields.$2;
if (_cards != null) {
final fieldProp = XmpProp(field, prop.value);
if (_cards.any((v) => v.extract(fieldProp))) {
@ -215,7 +214,7 @@ class XmpCardData {
}
}
fields.item1[field] = prop;
fields.$1[field] = prop;
return true;
}
@ -227,8 +226,8 @@ class XmpCardData {
final index = int.parse(match.group(1)!);
final field = match.group(2)!;
final fields = data.putIfAbsent(index, () => Tuple2(<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
final _cards = fields.item2;
final fields = data.putIfAbsent(index, () => (<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
final _cards = fields.$2;
if (_cards != null) {
final fieldProp = XmpProp(field, prop.value);
if (_cards.any((v) => v.extract(fieldProp))) {
@ -236,7 +235,7 @@ class XmpCardData {
}
}
fields.item1[field] = prop;
fields.$1[field] = prop;
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/metadata/xmp_namespaces.dart';
import 'package:collection/collection.dart';
import 'package:tuple/tuple.dart';
abstract class XmpGoogleNamespace extends XmpNamespace {
XmpGoogleNamespace({
@ -13,13 +12,12 @@ abstract class XmpGoogleNamespace extends XmpNamespace {
required super.rawProps,
});
List<Tuple2<String, String>> get dataProps;
List<(String, String)> get dataProps;
@override
Map<String, InfoValueSpanBuilder> linkifyValues(List<XmpProp> props) {
return Map.fromEntries(dataProps.map((t) {
final dataPropPath = t.item1;
final mimePropPath = t.item2;
final (dataPropPath, mimePropPath) = t;
final dataProp = props.firstWhereOrNull((prop) => prop.path == dataPropPath);
final mimeProp = props.firstWhereOrNull((prop) => prop.path == mimePropPath);
return (dataProp != null && mimeProp != null)
@ -60,8 +58,8 @@ class XmpGAudioNamespace extends XmpGoogleNamespace {
XmpGAudioNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gAudio);
@override
List<Tuple2<String, String>> get dataProps => [
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'),
List<(String, String)> get dataProps => [
('${nsPrefix}Data', '${nsPrefix}Mime'),
];
}
@ -69,8 +67,8 @@ class XmpGCameraNamespace extends XmpGoogleNamespace {
XmpGCameraNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gCamera);
@override
List<Tuple2<String, String>> get dataProps => [
Tuple2('${nsPrefix}RelitInputImageData', '${nsPrefix}RelitInputImageMime'),
List<(String, String)> get dataProps => [
('${nsPrefix}RelitInputImageData', '${nsPrefix}RelitInputImageMime'),
];
}
@ -87,9 +85,9 @@ class XmpGDepthNamespace extends XmpGoogleNamespace {
XmpGDepthNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gDepth);
@override
List<Tuple2<String, String>> get dataProps => [
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'),
Tuple2('${nsPrefix}Confidence', '${nsPrefix}ConfidenceMime'),
List<(String, String)> get dataProps => [
('${nsPrefix}Data', '${nsPrefix}Mime'),
('${nsPrefix}Confidence', '${nsPrefix}ConfidenceMime'),
];
}
@ -156,7 +154,7 @@ class XmpGImageNamespace extends XmpGoogleNamespace {
XmpGImageNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gImage);
@override
List<Tuple2<String, String>> get dataProps => [
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'),
List<(String, String)> get dataProps => [
('${nsPrefix}Data', '${nsPrefix}Mime'),
];
}

View file

@ -18,7 +18,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class WallpaperButtons extends StatelessWidget with FeedbackMixin {
final AvesEntry entry;
@ -57,16 +56,13 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
Future<void> _setWallpaper(BuildContext context) async {
final l10n = context.l10n;
final value = await showDialog<Tuple2<WallpaperTarget, bool>>(
final value = await showDialog<(WallpaperTarget, bool)>(
context: context,
builder: (context) => const WallpaperSettingsDialog(),
routeSettings: const RouteSettings(name: WallpaperSettingsDialog.routeName),
);
if (value == null) return;
final target = value.item1;
final useScrollEffect = value.item2;
final reportController = StreamController.broadcast();
unawaited(showOpReport(
context: context,
@ -76,6 +72,7 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
var region = _getVisibleRegion(context);
if (region == null) return;
final (target, useScrollEffect) = value;
if (useScrollEffect) {
final deltaX = min(region.left, entry.displaySize.width - region.right);
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:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class SlideshowPage extends StatefulWidget {
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 {
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/settings/enums/accessibility_animations.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/media/media_session_service.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/visual/error.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/video/cover.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:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class EntryPageView extends StatefulWidget {
final AvesEntry mainEntry, pageEntry;
@ -203,16 +202,14 @@ class _EntryPageViewState extends State<EntryPageView> with SingleTickerProvider
final videoDisplaySize = entry.videoDisplaySize(sar);
final isPureVideo = entry.isPureVideo;
return Selector<Settings, Tuple3<bool, bool, bool>>(
selector: (context, s) => Tuple3(
return Selector<Settings, (bool, bool, bool)>(
selector: (context, s) => (
isPureVideo && s.videoGestureDoubleTapTogglePlay,
isPureVideo && s.videoGestureSideDoubleTapSeek,
isPureVideo && s.videoGestureVerticalDragBrightnessVolume,
),
builder: (context, s, child) {
final playGesture = s.item1;
final seekGesture = s.item2;
final useVerticalDragGesture = s.item3;
final (playGesture, seekGesture, useVerticalDragGesture) = s;
final useTapGesture = playGesture || seekGesture;
MagnifierDoubleTapCallback? onDoubleTap;

View file

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

View file

@ -1479,14 +1479,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:

View file

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