packages upgrade, static analysis migration

This commit is contained in:
Thibault Deckers 2021-06-25 21:38:56 +09:00
parent cdb676144c
commit 1841c60c09
162 changed files with 706 additions and 441 deletions

View file

@ -1,4 +1,4 @@
include: package:pedantic/analysis_options.yaml
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
@ -12,23 +12,21 @@ analyzer:
linter:
rules:
# from 'effective dart', excluded
avoid_classes_with_only_static_members: false # too strict
avoid_function_literals_in_foreach_calls: false # benefit?
lines_longer_than_80_chars: false # nope
avoid_classes_with_only_static_members: false # too strict
public_member_api_docs: false # this project is not a library
# from 'effective dart', undecided
prefer_relative_imports: false # check IDE support (auto import, file move)
public_member_api_docs: false # maybe?
# from 'effective dart', included
avoid_types_on_closure_parameters: true
constant_identifier_names: true
prefer_function_declarations_over_variables: true
prefer_interpolation_to_compose_strings: true
unnecessary_brace_in_string_interps: true
unnecessary_lambdas: true
# misc
prefer_const_constructors: true # should specify `const` as Dart does not build constants when using const constructors without it
prefer_const_constructors_in_immutables: true
prefer_const_declarations: true
# from 'pedantic', included
always_declare_return_types: true
prefer_single_quotes: true
sort_child_properties_last: true
unawaited_futures: true

View file

@ -64,6 +64,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
if (isSupportedByExifInterface(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
@Suppress("BlockingMethodInNonBlockingContext")
val exif = ExifInterface(input)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
exif.thumbnailBitmap?.let { bitmap ->

View file

@ -67,6 +67,7 @@ class RegionFetcher internal constructor(
try {
if (currentDecoderRef == null) {
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
@Suppress("BlockingMethodInNonBlockingContext")
BitmapRegionDecoder.newInstance(input, false)
}
if (newDecoder == null) {

View file

@ -19,6 +19,7 @@ class TiffRegionFetcher internal constructor(
result: MethodChannel.Result,
) {
try {
@Suppress("BlockingMethodInNonBlockingContext")
val fd = context.contentResolver.openFileDescriptor(uri, "r")?.detachFd()
if (fd == null) {
result.error("getRegion-tiff-fd", "failed to get file descriptor for uri=$uri", null)

View file

@ -129,6 +129,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
.load(model)
.submit()
try {
@Suppress("BlockingMethodInNonBlockingContext")
var bitmap = target.get()
if (needRotationAfterGlide(mimeType)) {
bitmap = applyExifOrientation(activity, bitmap, rotationDegrees, isFlipped)
@ -152,6 +153,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen
.load(VideoThumbnail(activity, uri))
.submit()
try {
@Suppress("BlockingMethodInNonBlockingContext")
val bitmap = target.get()
if (bitmap != null) {
success(bitmap.getBytes(canHaveAlpha = false, recycle = false))

View file

@ -3,15 +3,15 @@ package deckers.thibault.aves.metadata
object TiffTags {
// XPosition
// Tag = 286 (011E.H)
const val TAG_X_POSITION = 0x011e
private const val TAG_X_POSITION = 0x011e
// YPosition
// Tag = 287 (011F.H)
const val TAG_Y_POSITION = 0x011f
private const val TAG_Y_POSITION = 0x011f
// ColorMap
// Tag = 320 (0140.H)
const val TAG_COLOR_MAP = 0x0140
private const val TAG_COLOR_MAP = 0x0140
// ExtraSamples
// Tag = 338 (0152.H)
@ -19,7 +19,7 @@ object TiffTags {
// EXTRASAMPLE_UNSPECIFIED 0 // unspecified data
// EXTRASAMPLE_ASSOCALPHA 1 // associated alpha data
// EXTRASAMPLE_UNASSALPHA 2 // unassociated alpha data
const val TAG_EXTRA_SAMPLES = 0x0152
private const val TAG_EXTRA_SAMPLES = 0x0152
// SampleFormat
// Tag = 339 (0153.H)
@ -30,7 +30,7 @@ object TiffTags {
// SAMPLEFORMAT_VOID 4 // untyped data
// SAMPLEFORMAT_COMPLEXINT 5 // complex signed int
// SAMPLEFORMAT_COMPLEXIEEEFP 6 // complex ieee floating
const val TAG_SAMPLE_FORMAT = 0x0153
private const val TAG_SAMPLE_FORMAT = 0x0153
/*
SGI
@ -40,7 +40,7 @@ object TiffTags {
// Matteing
// Tag = 32995 (80E3.H)
// obsoleted by the 6.0 ExtraSamples (338)
const val TAG_MATTEING = 0x80e3
private const val TAG_MATTEING = 0x80e3
/*
GeoTIFF
@ -74,13 +74,13 @@ object TiffTags {
// Tag = 34736 (87BO.H)
// Type = DOUBLE
// Count = variable
const val TAG_GEO_DOUBLE_PARAMS = 0x87b0
private const val TAG_GEO_DOUBLE_PARAMS = 0x87b0
// GeoAsciiParamsTag (optional)
// Tag = 34737 (87B1.H)
// Type = ASCII
// Count = variable
const val TAG_GEO_ASCII_PARAMS = 0x87b1
private const val TAG_GEO_ASCII_PARAMS = 0x87b1
/*
Photoshop
@ -91,7 +91,7 @@ object TiffTags {
// ImageSourceData
// Tag = 37724 (935C.H)
// Type = UNDEFINED
const val TAG_IMAGE_SOURCE_DATA = 0x935c
private const val TAG_IMAGE_SOURCE_DATA = 0x935c
/*
DNG
@ -102,13 +102,13 @@ object TiffTags {
// Tag = 50735 (C62F.H)
// Type = ASCII
// Count = variable
const val TAG_CAMERA_SERIAL_NUMBER = 0xc62f
private const val TAG_CAMERA_SERIAL_NUMBER = 0xc62f
// OriginalRawFileName (optional)
// Tag = 50827 (C68B.H)
// Type = ASCII or BYTE
// Count = variable
const val TAG_ORIGINAL_RAW_FILE_NAME = 0xc68b
private const val TAG_ORIGINAL_RAW_FILE_NAME = 0xc68b
private val geotiffTags = listOf(
TAG_GEO_ASCII_PARAMS,

View file

@ -6,13 +6,13 @@ import deckers.thibault.aves.model.SourceEntry
import java.io.File
internal class FileImageProvider : ImageProvider() {
override fun fetchSingle(context: Context, uri: Uri, mimeType: String?, callback: ImageOpCallback) {
if (mimeType == null) {
override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) {
if (sourceMimeType == null) {
callback.onFailure(Exception("MIME type is null for uri=$uri"))
return
}
val entry = SourceEntry(uri, mimeType)
val entry = SourceEntry(uri, sourceMimeType)
val path = uri.path
if (path != null) {

View file

@ -39,7 +39,7 @@ import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
abstract class ImageProvider {
open fun fetchSingle(context: Context, uri: Uri, mimeType: String?, callback: ImageOpCallback) {
open fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) {
callback.onFailure(UnsupportedOperationException())
}

View file

@ -38,7 +38,7 @@ class MediaStoreImageProvider : ImageProvider() {
fetchFrom(context, isModified, handleNewEntry, VIDEO_CONTENT_URI, VIDEO_PROJECTION)
}
override fun fetchSingle(context: Context, uri: Uri, mimeType: String?, callback: ImageOpCallback) {
override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) {
val id = uri.tryParseId()
val onSuccess = fun(entry: FieldMap) {
entry["uri"] = uri.toString()
@ -46,11 +46,11 @@ class MediaStoreImageProvider : ImageProvider() {
}
val alwaysValid = { _: Int, _: Int -> true }
if (id != null) {
if (mimeType == null || isImage(mimeType)) {
if (sourceMimeType == null || isImage(sourceMimeType)) {
val contentUri = ContentUris.withAppendedId(IMAGE_CONTENT_URI, id)
if (fetchFrom(context, alwaysValid, onSuccess, contentUri, IMAGE_PROJECTION)) return
}
if (mimeType == null || isVideo(mimeType)) {
if (sourceMimeType == null || isVideo(sourceMimeType)) {
val contentUri = ContentUris.withAppendedId(VIDEO_CONTENT_URI, id)
if (fetchFrom(context, alwaysValid, onSuccess, contentUri, VIDEO_PROJECTION)) return
}
@ -58,7 +58,7 @@ class MediaStoreImageProvider : ImageProvider() {
// the uri can be a file media URI (e.g. "content://0@media/external/file/30050")
// without an equivalent image/video if it is shared from a file browser
// but the file is not publicly visible
if (fetchFrom(context, alwaysValid, onSuccess, uri, BASE_PROJECTION, fileMimeType = mimeType)) return
if (fetchFrom(context, alwaysValid, onSuccess, uri, BASE_PROJECTION, fileMimeType = sourceMimeType)) return
callback.onFailure(Exception("failed to fetch entry at uri=$uri"))
}

View file

@ -11,7 +11,7 @@ object BmpWriter {
private val pad = ByteArray(3)
// file header
private val bfType = byteArrayOf('B'.toByte(), 'M'.toByte())
private val bfType = byteArrayOf('B'.code.toByte(), 'M'.code.toByte())
private val bfReserved1 = intToWord(0)
private val bfReserved2 = intToWord(0)
private val bfOffBits = intToDWord(FILE_HEADER_SIZE + INFO_HEADER_SIZE)

View file

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.5.20'
repositories {
google()
mavenCentral()
@ -9,7 +9,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.8'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.0'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
}
}

View file

@ -2,9 +2,9 @@ import 'dart:async';
import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:pedantic/pedantic.dart';
class UriImage extends ImageProvider<UriImage> {
final String uri, mimeType;

View file

@ -13,7 +13,7 @@ class UriPicture extends PictureProvider<UriPicture> {
final String uri, mimeType;
@override
Future<UriPicture> obtainKey(PictureConfiguration configuration) {
Future<UriPicture> obtainKey(PictureConfiguration picture) {
return SynchronousFuture<UriPicture>(this);
}

View file

@ -29,5 +29,5 @@ void main() {
);
}).sendPort);
runApp(AvesApp());
runApp(const AvesApp());
}

View file

@ -62,7 +62,7 @@ class LocationFilter extends CollectionFilter {
if (flag != null) {
return Text(
flag,
style: TextStyle(fontSize: size, shadows: []),
style: TextStyle(fontSize: size, shadows: const []),
textScaleFactor: 1.0,
);
}

View file

@ -139,11 +139,10 @@ class OverlayMetadata {
OverlayMetadata({
double? aperture,
String? exposureTime,
this.exposureTime,
double? focalLength,
int? iso,
}) : aperture = aperture != null ? 'ƒ/${apertureFormat.format(aperture)}' : null,
exposureTime = exposureTime,
focalLength = focalLength != null ? '${focalLengthFormat.format(focalLength)} mm' : null,
iso = iso != null ? 'ISO$iso' : null;

View file

@ -4,13 +4,13 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/enums.dart';
import 'package:aves/model/settings/screen_on.dart';
import 'package:aves/model/source/enums.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:collection/collection.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:pedantic/pedantic.dart';
import 'package:shared_preferences/shared_preferences.dart';
final Settings settings = Settings._private();

View file

@ -12,7 +12,7 @@ class AppShortcutService {
// this ability will not change over the lifetime of the app
static bool? _canPin;
static Future<bool > canPin() async {
static Future<bool> canPin() async {
if (_canPin != null) {
return SynchronousFuture(_canPin!);
}

View file

@ -26,7 +26,7 @@ class ServicePolicy {
Object? key,
}) {
Completer<T> completer;
_Task<T > task;
_Task<T> task;
key ??= platformCall.hashCode;
final toResume = _paused.remove(key);
if (toResume != null) {
@ -57,7 +57,7 @@ class ServicePolicy {
final toResume = _paused.remove(key);
if (toResume != null) {
final priority = toResume.item1;
final task = toResume.item2 as _Task<T >;
final task = toResume.item2 as _Task<T>;
_getQueue(priority)[key] = task;
_pickNext();
return task.completer.future;

View file

@ -67,7 +67,7 @@ class SvgMetadataService {
final docDir = Map.fromEntries([
...root.attributes.where((a) => _attributes.contains(a.name.qualified)).map((a) => MapEntry(formatKey(a.name.qualified), a.value)),
..._textElements.map((name) => MapEntry(formatKey(name), root.getElement(name)?.text)).where((kv) => kv.value != null).cast<MapEntry<String, String >>(),
..._textElements.map((name) => MapEntry(formatKey(name), root.getElement(name)?.text)).where((kv) => kv.value != null).cast<MapEntry<String, String>>(),
]);
final metadata = root.getElement(_metadataElement);

View file

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
// ignore: import_of_legacy_library_into_null_safe
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';

View file

@ -254,6 +254,12 @@ class Constants {
licenseUrl: 'https://github.com/marcojakob/dart-event-bus/blob/master/LICENSE',
sourceUrl: 'https://github.com/marcojakob/dart-event-bus',
),
Dependency(
name: 'Flutter Lints',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/flutter/packages/blob/master/packages/flutter_lints/LICENSE',
sourceUrl: 'https://github.com/flutter/packages/tree/master/packages/flutter_lints',
),
Dependency(
name: 'Get It',
license: 'MIT',
@ -284,12 +290,6 @@ class Constants {
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
sourceUrl: 'https://github.com/DavBfr/dart_pdf',
),
Dependency(
name: 'Pedantic',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dart-lang/pedantic/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/pedantic',
),
Dependency(
name: 'Tuple',
license: 'BSD 2-Clause',

2
lib/utils/pedantic.dart Normal file
View file

@ -0,0 +1,2 @@
// cf https://github.com/google/pedantic/blob/master/lib/pedantic.dart
void unawaited(Future<void>? future) {}

View file

@ -8,6 +8,8 @@ import 'package:flutter/material.dart';
class AboutPage extends StatelessWidget {
static const routeName = '/about';
const AboutPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
@ -21,17 +23,17 @@ class AboutPage extends StatelessWidget {
padding: const EdgeInsets.only(top: 16),
sliver: SliverList(
delegate: SliverChildListDelegate(
[
const [
AppReference(),
const Divider(),
Divider(),
AboutUpdate(),
AboutCredits(),
const Divider(),
Divider(),
],
),
),
),
Licenses(),
const Licenses(),
],
),
),

View file

@ -8,6 +8,8 @@ import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
class AppReference extends StatefulWidget {
const AppReference({Key? key}) : super(key: key);
@override
_AppReferenceState createState() => _AppReferenceState();
}

View file

@ -6,6 +6,8 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart';
class AboutCredits extends StatelessWidget {
const AboutCredits({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(

View file

@ -7,6 +7,8 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
class Licenses extends StatefulWidget {
const Licenses({Key? key}) : super(key: key);
@override
_LicensesState createState() => _LicensesState();
}
@ -46,25 +48,25 @@ class _LicensesState extends State<Licenses> {
title: context.l10n.aboutLicensesAndroidLibraries,
color: BrandColors.android,
expandedNotifier: _expandedNotifier,
children: _platform.map((package) => LicenseRow(package)).toList(),
children: _platform.map((package) => LicenseRow(package: package)).toList(),
),
AvesExpansionTile(
title: context.l10n.aboutLicensesFlutterPlugins,
color: BrandColors.flutter,
expandedNotifier: _expandedNotifier,
children: _flutterPlugins.map((package) => LicenseRow(package)).toList(),
children: _flutterPlugins.map((package) => LicenseRow(package: package)).toList(),
),
AvesExpansionTile(
title: context.l10n.aboutLicensesFlutterPackages,
color: BrandColors.flutter,
expandedNotifier: _expandedNotifier,
children: _flutterPackages.map((package) => LicenseRow(package)).toList(),
children: _flutterPackages.map((package) => LicenseRow(package: package)).toList(),
),
AvesExpansionTile(
title: context.l10n.aboutLicensesDartPackages,
color: BrandColors.flutter,
expandedNotifier: _expandedNotifier,
children: _dartPackages.map((package) => LicenseRow(package)).toList(),
children: _dartPackages.map((package) => LicenseRow(package: package)).toList(),
),
Center(
child: TextButton(
@ -113,7 +115,10 @@ class _LicensesState extends State<Licenses> {
class LicenseRow extends StatelessWidget {
final Dependency package;
const LicenseRow(this.package);
const LicenseRow({
Key? key,
required this.package,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
class AboutNewsBadge extends StatelessWidget {
const AboutNewsBadge();
const AboutNewsBadge({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -6,6 +6,8 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart';
class AboutUpdate extends StatefulWidget {
const AboutUpdate({Key? key}) : super(key: key);
@override
_AboutUpdateState createState() => _AboutUpdateState();
}

View file

@ -27,6 +27,8 @@ import 'package:overlay_support/overlay_support.dart';
import 'package:provider/provider.dart';
class AvesApp extends StatefulWidget {
const AvesApp({Key? key}) : super(key: key);
@override
_AvesAppState createState() => _AvesAppState();
}
@ -88,7 +90,7 @@ class _AvesAppState extends State<AvesApp> {
darkTheme: Themes.darkTheme,
themeMode: ThemeMode.dark,
locale: settingsLocale,
localizationsDelegates: [
localizationsDelegates: const [
...AppLocalizations.localizationsDelegates,
],
supportedLocales: AppLocalizations.supportedLocales,

View file

@ -12,6 +12,7 @@ import 'package:aves/model/source/enums.dart';
import 'package:aves/services/app_shortcut_service.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
import 'package:aves/widgets/collection/filter_bar.dart';
import 'package:aves/widgets/common/app_bar_subtitle.dart';
@ -26,7 +27,6 @@ import 'package:aves/widgets/stats/stats.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:pedantic/pedantic.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
@ -175,7 +175,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
return [
if (collection.isBrowsing && appMode.canSearch)
CollectionSearchButton(
source,
source: source,
parentCollection: collection,
),
if (collection.isSelecting)
@ -349,7 +349,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
Future<void> _showShortcutDialog(BuildContext context) async {
final filters = collection.filters;
var defaultName;
String? defaultName;
if (filters.isNotEmpty) {
// we compute the default name beforehand
// because some filter labels need localization

View file

@ -324,8 +324,8 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
hasScrollBody: false,
child: _buildEmptyCollectionPlaceholder(collection),
)
: SectionedListSliver<AvesEntry>(),
BottomPaddingSliver(),
: const SectionedListSliver<AvesEntry>(),
const BottomPaddingSliver(),
],
);
}

View file

@ -13,7 +13,10 @@ class CollectionPage extends StatefulWidget {
final CollectionLens collection;
const CollectionPage(this.collection);
const CollectionPage({
Key? key,
required this.collection,
}) : super(key: key);
@override
_CollectionPageState createState() => _CollectionPageState();
@ -54,7 +57,7 @@ class _CollectionPageState extends State<CollectionPage> {
),
),
),
drawer: AppDrawer(),
drawer: const AppDrawer(),
resizeToAvoidBottomInset: false,
),
);

View file

@ -13,9 +13,10 @@ class CollectionDraggableThumbLabel extends StatelessWidget {
final double offsetY;
const CollectionDraggableThumbLabel({
Key? key,
required this.collection,
required this.offsetY,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -11,6 +11,7 @@ import 'package:aves/services/image_op_events.dart';
import 'package:aves/services/services.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/action_mixins/permission_aware.dart';
@ -21,7 +22,6 @@ import 'package:aves/widgets/filter_grids/album_pick.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:pedantic/pedantic.dart';
import 'package:provider/provider.dart';
class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
@ -155,11 +155,9 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
context,
MaterialPageRoute(
settings: const RouteSettings(name: CollectionPage.routeName),
builder: (context) {
return CollectionPage(
targetCollection,
);
},
builder: (context) => CollectionPage(
collection: targetCollection,
),
),
));
await Future.delayed(Durations.staggeredAnimationPageTarget);

View file

@ -9,6 +9,7 @@ class SectionedEntryListLayoutProvider extends SectionedListLayoutProvider<AvesE
final CollectionLens collection;
const SectionedEntryListLayoutProvider({
Key? key,
required this.collection,
required double scrollableWidth,
required int columnCount,
@ -18,6 +19,7 @@ class SectionedEntryListLayoutProvider extends SectionedListLayoutProvider<AvesE
required Duration tileAnimationDelay,
required Widget child,
}) : super(
key: key,
scrollableWidth: scrollableWidth,
columnCount: columnCount,
spacing: spacing,

View file

@ -18,12 +18,13 @@ class GridSelectionGestureDetector extends StatefulWidget {
final Widget child;
const GridSelectionGestureDetector({
Key? key,
this.selectable = true,
required this.collection,
required this.scrollController,
required this.appBarHeightNotifier,
required this.child,
});
}) : super(key: key);
@override
_GridSelectionGestureDetectorState createState() => _GridSelectionGestureDetectorState();
@ -39,7 +40,7 @@ class _GridSelectionGestureDetectorState extends State<GridSelectionGestureDetec
CollectionLens get collection => widget.collection;
List<AvesEntry > get entries => collection.sortedEntries;
List<AvesEntry> get entries => collection.sortedEntries;
ScrollController get scrollController => widget.scrollController;

View file

@ -13,10 +13,11 @@ class ErrorThumbnail extends StatefulWidget {
final String tooltip;
const ErrorThumbnail({
Key? key,
required this.entry,
required this.extent,
required this.tooltip,
});
}) : super(key: key);
@override
_ErrorThumbnailState createState() => _ErrorThumbnailState();

View file

@ -10,10 +10,11 @@ class ThumbnailTheme extends StatelessWidget {
final Widget child;
const ThumbnailTheme({
Key? key,
required this.extent,
this.showLocation,
required this.child,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -50,10 +50,11 @@ class ReportOverlay<T> extends StatefulWidget {
final void Function(Set<T> processed) onDone;
const ReportOverlay({
Key? key,
required this.opStream,
required this.itemCount,
required this.onDone,
});
}) : super(key: key);
@override
_ReportOverlayState createState() => _ReportOverlayState<T>();

View file

@ -49,7 +49,10 @@ class SourceStateAwareAppBarTitle extends StatelessWidget {
class SourceStateSubtitle extends StatelessWidget {
final CollectionSource source;
const SourceStateSubtitle({required this.source});
const SourceStateSubtitle({
Key? key,
required this.source,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -5,9 +5,10 @@ class InteractiveAppBarTitle extends StatelessWidget {
final Widget child;
const InteractiveAppBarTitle({
Key? key,
this.onTap,
required this.child,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -29,14 +29,16 @@ class AvesHighlightView extends StatelessWidget {
/// Specify text styles such as font family and font size
final TextStyle? textStyle;
AvesHighlightView(
String input, {
AvesHighlightView({
Key? key,
required String input,
this.language,
this.theme = const {},
this.padding,
this.textStyle,
int tabSize = 8, // TODO: https://github.com/flutter/flutter/issues/50087
}) : source = input.replaceAll('\t', ' ' * tabSize);
}) : source = input.replaceAll('\t', ' ' * tabSize),
super(key: key);
List<TextSpan> _convert(List<Node> nodes) {
final spans = <TextSpan>[];

View file

@ -7,6 +7,8 @@ import 'package:provider/provider.dart';
// - a vertically scrollable body.
// It will prevent the body from scrolling when a user swipe from bottom to use Android Q style navigation gestures.
class BottomGestureAreaProtector extends StatelessWidget {
const BottomGestureAreaProtector({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Selector<MediaQueryData, double>(
@ -27,20 +29,25 @@ class BottomGestureAreaProtector extends StatelessWidget {
class GestureAreaProtectorStack extends StatelessWidget {
final Widget child;
const GestureAreaProtectorStack({required this.child});
const GestureAreaProtectorStack({
Key? key,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
child,
BottomGestureAreaProtector(),
const BottomGestureAreaProtector(),
],
);
}
}
class BottomPaddingSliver extends StatelessWidget {
const BottomPaddingSliver({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(

View file

@ -7,12 +7,13 @@ class MultiCrossFader extends StatefulWidget {
final Widget child;
const MultiCrossFader({
Key? key,
required this.duration,
this.fadeCurve = Curves.linear,
this.sizeCurve = Curves.linear,
this.alignment = Alignment.topCenter,
required this.child,
});
}) : super(key: key);
@override
_MultiCrossFaderState createState() => _MultiCrossFaderState();

View file

@ -9,7 +9,10 @@ import 'package:flutter/widgets.dart';
class QueryBar extends StatefulWidget {
final ValueNotifier<String> filterNotifier;
const QueryBar({required this.filterNotifier});
const QueryBar({
Key? key,
required this.filterNotifier,
}) : super(key: key);
@override
_QueryBarState createState() => _QueryBarState();

View file

@ -12,8 +12,9 @@ class DoubleBackPopScope extends StatefulWidget {
final Widget child;
const DoubleBackPopScope({
Key? key,
required this.child,
});
}) : super(key: key);
@override
_DoubleBackPopScopeState createState() => _DoubleBackPopScopeState();

View file

@ -1,9 +1,9 @@
import 'dart:math';
import 'package:aves/theme/durations.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:pedantic/pedantic.dart';
class Sweeper extends StatefulWidget {
final WidgetBuilder builder;

View file

@ -15,11 +15,12 @@ class TransitionImage extends StatefulWidget {
final bool gaplessPlayback = false;
const TransitionImage({
Key? key,
required this.image,
required this.animation,
this.width,
this.height,
});
}) : super(key: key);
@override
_TransitionImageState createState() => _TransitionImageState();

View file

@ -9,9 +9,10 @@ class DraggableThumbLabel<T> extends StatelessWidget {
final List<String> Function(BuildContext context, T item) lineBuilder;
const DraggableThumbLabel({
Key? key,
required this.offsetY,
required this.lineBuilder,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -103,10 +103,11 @@ class SectionHeader extends StatelessWidget {
// as of Flutter v1.22.3, `RenderParagraph` fails to lay out `WidgetSpan` offscreen
// so we use a hair space times a magic number to match width
TextSpan(
// 23 hair spaces match a width of 40.0
text: '\u200A' * (hasLeading ? 23 : 1),
// force a higher first line to match leading icon/selector dimension
style: TextStyle(height: 2.3 * textScaleFactor),
), // 23 hair spaces match a width of 40.0
),
if (hasTrailing) TextSpan(text: '\u200A' * 17),
TextSpan(
text: title,

View file

@ -18,6 +18,7 @@ abstract class SectionedListLayoutProvider<T> extends StatelessWidget {
final Widget child;
const SectionedListLayoutProvider({
Key? key,
required this.scrollableWidth,
required this.columnCount,
required this.spacing,
@ -25,7 +26,8 @@ abstract class SectionedListLayoutProvider<T> extends StatelessWidget {
required this.tileBuilder,
required this.tileAnimationDelay,
required this.child,
}) : assert(scrollableWidth != 0);
}) : assert(scrollableWidth != 0),
super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -14,6 +14,8 @@ import 'package:provider/provider.dart';
// cf https://github.com/flutter/flutter/issues/49027
// adapted from `RenderSliverFixedExtentBoxAdaptor`
class SectionedListSliver<T> extends StatelessWidget {
const SectionedListSliver({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final sectionLayouts = context.watch<SectionedListLayout<T>>().sectionLayouts;

View file

@ -12,6 +12,7 @@ class AvesExpansionTile extends StatelessWidget {
final List<Widget> children;
const AvesExpansionTile({
Key? key,
String? value,
this.leading,
required this.title,
@ -20,13 +21,14 @@ class AvesExpansionTile extends StatelessWidget {
this.initiallyExpanded = false,
this.showHighlight = true,
required this.children,
}) : value = value ?? title;
}) : value = value ?? title,
super(key: key);
@override
Widget build(BuildContext context) {
final enabled = children.isNotEmpty == true;
Widget titleChild = HighlightTitle(
title,
title: title,
color: color,
enabled: enabled,
showHighlight: showHighlight,

View file

@ -4,7 +4,10 @@ import 'package:flutter_svg/flutter_svg.dart';
class AvesLogo extends StatelessWidget {
final double size;
const AvesLogo({required this.size});
const AvesLogo({
Key? key,
required this.size,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -6,10 +6,11 @@ class EmptyContent extends StatelessWidget {
final AlignmentGeometry alignment;
const EmptyContent({
Key? key,
this.icon,
required this.text,
this.alignment = const FractionalOffset(.5, .35),
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -11,14 +11,15 @@ class HighlightTitle extends StatelessWidget {
final bool enabled, selectable;
final bool showHighlight;
const HighlightTitle(
this.title, {
const HighlightTitle({
Key? key,
required this.title,
this.color,
this.fontSize = 18,
this.enabled = true,
this.selectable = false,
this.showHighlight = true,
});
}) : super(key: key);
static const disabledColor = Colors.grey;

View file

@ -96,7 +96,7 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer {
}
void _decideIfWeAcceptEvent(PointerEvent event) {
if (!(event is PointerMoveEvent)) return;
if (event is! PointerMoveEvent) return;
if (_pointerLocations.keys.length >= 2) {
// when there are multiple pointers, we always accept the gesture to scale

View file

@ -8,10 +8,11 @@ import 'package:flutter/widgets.dart';
/// such as [PageView], [Dismissible], [BottomSheet].
class MagnifierGestureDetectorScope extends InheritedWidget {
const MagnifierGestureDetectorScope({
Key? key,
required this.axis,
this.touchSlopFactor = .8,
required Widget child,
}) : super(child: child);
}) : super(key: key, child: child);
static MagnifierGestureDetectorScope? of(BuildContext context) {
final scope = context.dependOnInheritedWidgetOfExactType<MagnifierGestureDetectorScope>();

View file

@ -5,7 +5,10 @@ import 'package:provider/provider.dart';
class HighlightInfoProvider extends StatelessWidget {
final Widget child;
const HighlightInfoProvider({required this.child});
const HighlightInfoProvider({
Key? key,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -4,7 +4,10 @@ import 'package:provider/provider.dart';
class MediaQueryDataProvider extends StatelessWidget {
final Widget child;
const MediaQueryDataProvider({required this.child});
const MediaQueryDataProvider({
Key? key,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -7,9 +7,10 @@ class TileExtentControllerProvider extends StatelessWidget {
final Widget child;
const TileExtentControllerProvider({
Key? key,
required this.controller,
required this.child,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -24,12 +24,13 @@ class GridScaleGestureDetector<T> extends StatefulWidget {
final Widget child;
const GridScaleGestureDetector({
Key? key,
required this.scrollableKey,
required this.gridBuilder,
required this.scaledBuilder,
this.highlightItem,
required this.child,
});
}) : super(key: key);
@override
_GridScaleGestureDetectorState createState() => _GridScaleGestureDetectorState<T>();
@ -140,12 +141,13 @@ class ScaleOverlay extends StatefulWidget {
final Widget Function(Offset center, double extent, Widget child) gridBuilder;
const ScaleOverlay({
Key? key,
required this.builder,
required this.center,
required this.viewportWidth,
required this.scaledExtentNotifier,
required this.gridBuilder,
});
}) : super(key: key);
@override
_ScaleOverlayState createState() => _ScaleOverlayState();
@ -175,7 +177,7 @@ class _ScaleOverlayState extends State<ScaleOverlay> {
gradient: RadialGradient(
center: FractionalOffset.fromOffsetAndSize(center, context.select<MediaQueryData, Size>((mq) => mq.size)),
radius: 1,
colors: [
colors: const [
Colors.black,
Colors.black54,
],

View file

@ -7,6 +7,8 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
class DebugAndroidAppSection extends StatefulWidget {
const DebugAndroidAppSection({Key? key}) : super(key: key);
@override
_DebugAndroidAppSectionState createState() => _DebugAndroidAppSectionState();
}

View file

@ -6,6 +6,8 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/material.dart';
class DebugAndroidDirSection extends StatefulWidget {
const DebugAndroidDirSection({Key? key}) : super(key: key);
@override
_DebugAndroidDirSectionState createState() => _DebugAndroidDirSectionState();
}
@ -34,7 +36,7 @@ class _DebugAndroidDirSectionState extends State<DebugAndroidDirSection> with Au
if (snapshot.hasError) return Text(snapshot.error.toString());
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
final data = SplayTreeMap.of(snapshot.data!.map((k, v) => MapEntry(k.toString(), v?.toString() ?? 'null')));
return InfoRowGroup(data);
return InfoRowGroup(info: data);
},
),
),

View file

@ -6,6 +6,8 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/material.dart';
class DebugAndroidEnvironmentSection extends StatefulWidget {
const DebugAndroidEnvironmentSection({Key? key}) : super(key: key);
@override
_DebugAndroidEnvironmentSectionState createState() => _DebugAndroidEnvironmentSectionState();
}
@ -34,7 +36,7 @@ class _DebugAndroidEnvironmentSectionState extends State<DebugAndroidEnvironment
if (snapshot.hasError) return Text(snapshot.error.toString());
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
final data = SplayTreeMap.of(snapshot.data!.map((k, v) => MapEntry(k.toString(), v?.toString() ?? 'null')));
return InfoRowGroup(data);
return InfoRowGroup(info: data);
},
),
),

View file

@ -19,6 +19,8 @@ import 'package:provider/provider.dart';
class AppDebugPage extends StatefulWidget {
static const routeName = '/debug';
const AppDebugPage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _AppDebugPageState();
}
@ -42,14 +44,14 @@ class _AppDebugPageState extends State<AppDebugPage> {
padding: const EdgeInsets.all(8),
children: [
_buildGeneralTabView(),
DebugAndroidAppSection(),
DebugAndroidDirSection(),
DebugAndroidEnvironmentSection(),
DebugCacheSection(),
DebugAppDatabaseSection(),
DebugFirebaseSection(),
DebugSettingsSection(),
DebugStorageSection(),
const DebugAndroidAppSection(),
const DebugAndroidDirSection(),
const DebugAndroidEnvironmentSection(),
const DebugCacheSection(),
const DebugAppDatabaseSection(),
const DebugFirebaseSection(),
const DebugSettingsSection(),
const DebugStorageSection(),
],
),
),
@ -83,7 +85,7 @@ class _AppDebugPageState extends State<AppDebugPage> {
_taskQueueOverlayEntry?.remove();
if (v) {
_taskQueueOverlayEntry = OverlayEntry(
builder: (context) => DebugTaskQueueOverlay(),
builder: (context) => const DebugTaskQueueOverlay(),
);
Overlay.of(context)!.insert(_taskQueueOverlayEntry!);
} else {
@ -97,7 +99,7 @@ class _AppDebugPageState extends State<AppDebugPage> {
Padding(
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: InfoRowGroup(
{
info: {
'All entries': '${source.allEntries.length}',
'Visible entries': '${visibleEntries.length}',
'Catalogued': '${catalogued.length}',

View file

@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class DebugCacheSection extends StatefulWidget {
const DebugCacheSection({Key? key}) : super(key: key);
@override
_DebugCacheSectionState createState() => _DebugCacheSectionState();
}

View file

@ -8,6 +8,8 @@ import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:flutter/material.dart';
class DebugAppDatabaseSection extends StatefulWidget {
const DebugAppDatabaseSection({Key? key}) : super(key: key);
@override
_DebugAppDatabaseSectionState createState() => _DebugAppDatabaseSectionState();
}

View file

@ -6,6 +6,8 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
class DebugFirebaseSection extends StatelessWidget {
const DebugFirebaseSection({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AvesExpansionTile(
@ -32,10 +34,12 @@ class DebugFirebaseSection extends StatelessWidget {
),
Padding(
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: InfoRowGroup({
'Firebase data collection enabled': '${Firebase.app().isAutomaticDataCollectionEnabled}',
'Crashlytics collection enabled': '${FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled}',
}),
child: InfoRowGroup(
info: {
'Firebase data collection enabled': '${Firebase.app().isAutomaticDataCollectionEnabled}',
'Crashlytics collection enabled': '${FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled}',
},
),
)
],
);

View file

@ -2,6 +2,8 @@ import 'package:aves/services/service_policy.dart';
import 'package:flutter/material.dart';
class DebugTaskQueueOverlay extends StatelessWidget {
const DebugTaskQueueOverlay({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return IgnorePointer(

View file

@ -9,6 +9,8 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class DebugSettingsSection extends StatelessWidget {
const DebugSettingsSection({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer<Settings>(
@ -36,21 +38,23 @@ class DebugSettingsSection extends StatelessWidget {
),
Padding(
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: InfoRowGroup({
'tileExtent - Collection': '${settings.getTileExtent(CollectionPage.routeName)}',
'tileExtent - Albums': '${settings.getTileExtent(AlbumListPage.routeName)}',
'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}',
'tileExtent - Tags': '${settings.getTileExtent(TagListPage.routeName)}',
'infoMapZoom': '${settings.infoMapZoom}',
'viewerQuickActions': '${settings.viewerQuickActions}',
'videoQuickActions': '${settings.videoQuickActions}',
'pinnedFilters': toMultiline(settings.pinnedFilters),
'hiddenFilters': toMultiline(settings.hiddenFilters),
'searchHistory': toMultiline(settings.searchHistory),
'lastVersionCheckDate': '${settings.lastVersionCheckDate}',
'locale': '${settings.locale}',
'systemLocale': '${WidgetsBinding.instance!.window.locale}',
}),
child: InfoRowGroup(
info: {
'tileExtent - Collection': '${settings.getTileExtent(CollectionPage.routeName)}',
'tileExtent - Albums': '${settings.getTileExtent(AlbumListPage.routeName)}',
'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}',
'tileExtent - Tags': '${settings.getTileExtent(TagListPage.routeName)}',
'infoMapZoom': '${settings.infoMapZoom}',
'viewerQuickActions': '${settings.viewerQuickActions}',
'videoQuickActions': '${settings.videoQuickActions}',
'pinnedFilters': toMultiline(settings.pinnedFilters),
'hiddenFilters': toMultiline(settings.hiddenFilters),
'searchHistory': toMultiline(settings.searchHistory),
'lastVersionCheckDate': '${settings.lastVersionCheckDate}',
'locale': '${settings.locale}',
'systemLocale': '${WidgetsBinding.instance!.window.locale}',
},
),
),
],
);

View file

@ -6,6 +6,8 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/material.dart';
class DebugStorageSection extends StatefulWidget {
const DebugStorageSection({Key? key}) : super(key: key);
@override
_DebugStorageSectionState createState() => _DebugStorageSectionState();
}
@ -38,13 +40,15 @@ class _DebugStorageSectionState extends State<DebugStorageSection> with Automati
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: InfoRowGroup({
'description': '${v.getDescription(context)}',
'isPrimary': '${v.isPrimary}',
'isRemovable': '${v.isRemovable}',
'state': '${v.state}',
if (freeSpace != null) 'freeSpace': formatFilesize(freeSpace),
}),
child: InfoRowGroup(
info: {
'description': v.getDescription(context),
'isPrimary': '${v.isPrimary}',
'isRemovable': '${v.isRemovable}',
'state': v.state,
if (freeSpace != null) 'freeSpace': formatFilesize(freeSpace),
},
),
),
const Divider(),
];

View file

@ -19,9 +19,10 @@ class AddShortcutDialog extends StatefulWidget {
final String defaultName;
const AddShortcutDialog({
Key? key,
required this.collection,
required this.defaultName,
});
}) : super(key: key);
@override
_AddShortcutDialogState createState() => _AddShortcutDialogState();
@ -133,7 +134,7 @@ class _AddShortcutDialogState extends State<AddShortcutDialog> {
MaterialPageRoute(
settings: const RouteSettings(name: ItemPickDialog.routeName),
builder: (context) => ItemPickDialog(
CollectionLens(
collection: CollectionLens(
source: collection.source,
filters: filters,
),

View file

@ -9,6 +9,7 @@ class AvesDialog extends AlertDialog {
static const borderWidth = 1.0;
AvesDialog({
Key? key,
required BuildContext context,
String? title,
ScrollController? scrollController,
@ -17,6 +18,7 @@ class AvesDialog extends AlertDialog {
required List<Widget> actions,
}) : assert((scrollableContent != null) ^ (content != null)),
super(
key: key,
title: title != null
? Padding(
// padding to avoid transparent border overlapping
@ -64,7 +66,10 @@ class AvesDialog extends AlertDialog {
class DialogTitle extends StatelessWidget {
final String title;
const DialogTitle({required this.title});
const DialogTitle({
Key? key,
required this.title,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -13,11 +13,12 @@ class AvesSelectionDialog<T> extends StatefulWidget {
final String title;
const AvesSelectionDialog({
Key? key,
required this.initialValue,
required this.options,
this.optionSubtitleBuilder,
required this.title,
});
}) : super(key: key);
@override
_AvesSelectionDialogState<T> createState() => _AvesSelectionDialogState<T>();

View file

@ -17,9 +17,10 @@ class CoverSelectionDialog extends StatefulWidget {
final AvesEntry? customEntry;
const CoverSelectionDialog({
Key? key,
required this.filter,
required this.customEntry,
});
}) : super(key: key);
@override
_CoverSelectionDialogState createState() => _CoverSelectionDialogState();
@ -118,7 +119,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
MaterialPageRoute(
settings: const RouteSettings(name: ItemPickDialog.routeName),
builder: (context) => ItemPickDialog(
CollectionLens(
collection: CollectionLens(
source: context.read<CollectionSource>(),
filters: [filter],
),

View file

@ -11,6 +11,8 @@ import 'package:flutter/widgets.dart';
import 'aves_dialog.dart';
class CreateAlbumDialog extends StatefulWidget {
const CreateAlbumDialog({Key? key}) : super(key: key);
@override
_CreateAlbumDialogState createState() => _CreateAlbumDialogState();
}

View file

@ -12,7 +12,10 @@ class ItemPickDialog extends StatefulWidget {
final CollectionLens collection;
const ItemPickDialog(this.collection);
const ItemPickDialog({
Key? key,
required this.collection,
}) : super(key: key);
@override
_ItemPickDialogState createState() => _ItemPickDialogState();

View file

@ -8,7 +8,10 @@ import 'package:flutter/material.dart';
class RenameAlbumDialog extends StatefulWidget {
final String album;
const RenameAlbumDialog(this.album);
const RenameAlbumDialog({
Key? key,
required this.album,
}) : super(key: key);
@override
_RenameAlbumDialogState createState() => _RenameAlbumDialogState();

View file

@ -10,7 +10,10 @@ import 'aves_dialog.dart';
class RenameEntryDialog extends StatefulWidget {
final AvesEntry entry;
const RenameEntryDialog(this.entry);
const RenameEntryDialog({
Key? key,
required this.entry,
}) : super(key: key);
@override
_RenameEntryDialogState createState() => _RenameEntryDialogState();

View file

@ -7,10 +7,11 @@ class VideoSpeedDialog extends StatefulWidget {
final double current, min, max;
const VideoSpeedDialog({
Key? key,
required this.current,
required this.min,
required this.max,
});
}) : super(key: key);
@override
_VideoSpeedDialogState createState() => _VideoSpeedDialogState();

View file

@ -12,8 +12,9 @@ class VideoStreamSelectionDialog extends StatefulWidget {
final Map<StreamSummary, bool> streams;
const VideoStreamSelectionDialog({
Key? key,
required this.streams,
});
}) : super(key: key);
@override
_VideoStreamSelectionDialogState createState() => _VideoStreamSelectionDialogState();

View file

@ -10,7 +10,10 @@ import 'package:provider/provider.dart';
class AlbumTile extends StatelessWidget {
final String album;
const AlbumTile(this.album);
const AlbumTile({
Key? key,
required this.album,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -28,6 +28,8 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppDrawer extends StatefulWidget {
const AppDrawer({Key? key}) : super(key: key);
@override
_AppDrawerState createState() => _AppDrawerState();
}
@ -141,7 +143,7 @@ class _AppDrawerState extends State<AppDrawer> {
children: [
OutlinedButton.icon(
key: const Key('drawer-about-button'),
onPressed: () => goTo(AboutPage.routeName, (_) => AboutPage()),
onPressed: () => goTo(AboutPage.routeName, (_) => const AboutPage()),
icon: const Icon(AIcons.info),
label: Row(
crossAxisAlignment: CrossAxisAlignment.start,
@ -177,7 +179,7 @@ class _AppDrawerState extends State<AppDrawer> {
),
OutlinedButton.icon(
key: const Key('drawer-settings-button'),
onPressed: () => goTo(SettingsPage.routeName, (_) => SettingsPage()),
onPressed: () => goTo(SettingsPage.routeName, (_) => const SettingsPage()),
icon: const Icon(AIcons.settings),
label: Text(context.l10n.settingsPageTitle),
),
@ -204,7 +206,7 @@ class _AppDrawerState extends State<AppDrawer> {
return Column(
children: [
const Divider(),
...specialAlbums.map((album) => AlbumTile(album)),
...specialAlbums.map((album) => AlbumTile(album: album)),
],
);
});
@ -238,7 +240,7 @@ class _AppDrawerState extends State<AppDrawer> {
builder: (context, _) => Text('${source.rawAlbums.length}'),
),
routeName: AlbumListPage.routeName,
pageBuilder: (_) => AlbumListPage(),
pageBuilder: (_) => const AlbumListPage(),
);
Widget get countryListTile => NavTile(
@ -249,7 +251,7 @@ class _AppDrawerState extends State<AppDrawer> {
builder: (context, _) => Text('${source.sortedCountries.length}'),
),
routeName: CountryListPage.routeName,
pageBuilder: (_) => CountryListPage(),
pageBuilder: (_) => const CountryListPage(),
);
Widget get tagListTile => NavTile(
@ -260,7 +262,7 @@ class _AppDrawerState extends State<AppDrawer> {
builder: (context, _) => Text('${source.sortedTags.length}'),
),
routeName: TagListPage.routeName,
pageBuilder: (_) => TagListPage(),
pageBuilder: (_) => const TagListPage(),
);
Widget get debugTile => NavTile(
@ -268,6 +270,6 @@ class _AppDrawerState extends State<AppDrawer> {
title: 'Debug',
topLevel: false,
routeName: AppDebugPage.routeName,
pageBuilder: (_) => AppDebugPage(),
pageBuilder: (_) => const AppDebugPage(),
);
}

View file

@ -13,12 +13,14 @@ class CollectionNavTile extends StatelessWidget {
final CollectionFilter? filter;
const CollectionNavTile({
Key? key,
required this.leading,
required this.title,
this.trailing,
bool? dense,
required this.filter,
}) : dense = dense ?? false;
}) : dense = dense ?? false,
super(key: key);
@override
Widget build(BuildContext context) {
@ -41,10 +43,12 @@ class CollectionNavTile extends StatelessWidget {
context,
MaterialPageRoute(
settings: const RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(CollectionLens(
source: context.read<CollectionSource>(),
filters: [filter],
)),
builder: (context) => CollectionPage(
collection: CollectionLens(
source: context.read<CollectionSource>(),
filters: [filter],
),
),
),
(route) => false,
);

View file

@ -11,13 +11,14 @@ class NavTile extends StatelessWidget {
final WidgetBuilder pageBuilder;
const NavTile({
Key? key,
required this.icon,
required this.title,
this.trailing,
this.topLevel = true,
required this.routeName,
required this.pageBuilder,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -30,9 +30,10 @@ class AlbumPickPage extends StatefulWidget {
final MoveType moveType;
const AlbumPickPage({
Key? key,
required this.source,
required this.moveType,
});
}) : super(key: key);
@override
_AlbumPickPageState createState() => _AlbumPickPageState();
@ -91,11 +92,12 @@ class AlbumPickAppBar extends StatelessWidget {
static const preferredHeight = kToolbarHeight + AlbumFilterBar.preferredHeight;
const AlbumPickAppBar({
Key? key,
required this.source,
required this.moveType,
required this.actionDelegate,
required this.queryNotifier,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -127,7 +129,7 @@ class AlbumPickAppBar extends StatelessWidget {
onPressed: () async {
final newAlbum = await showDialog<String>(
context: context,
builder: (context) => CreateAlbumDialog(),
builder: (context) => const CreateAlbumDialog(),
);
if (newAlbum != null && newAlbum.isNotEmpty) {
Navigator.pop<String>(context, newAlbum);
@ -169,8 +171,9 @@ class AlbumFilterBar extends StatelessWidget implements PreferredSizeWidget {
static const preferredHeight = kToolbarHeight;
const AlbumFilterBar({
Key? key,
required this.filterNotifier,
});
}) : super(key: key);
@override
Size get preferredSize => const Size.fromHeight(preferredHeight);

View file

@ -21,6 +21,8 @@ import 'package:tuple/tuple.dart';
class AlbumListPage extends StatelessWidget {
static const routeName = '/albums';
const AlbumListPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();

View file

@ -43,13 +43,13 @@ class ChipActionDelegate {
_showCoverSelectionDialog(context, filter);
break;
case ChipAction.goToAlbumPage:
_goTo(context, filter, AlbumListPage.routeName, (context) => AlbumListPage());
_goTo(context, filter, AlbumListPage.routeName, (context) => const AlbumListPage());
break;
case ChipAction.goToCountryPage:
_goTo(context, filter, CountryListPage.routeName, (context) => CountryListPage());
_goTo(context, filter, CountryListPage.routeName, (context) => const CountryListPage());
break;
case ChipAction.goToTagPage:
_goTo(context, filter, TagListPage.routeName, (context) => TagListPage());
_goTo(context, filter, TagListPage.routeName, (context) => const TagListPage());
break;
default:
break;
@ -207,7 +207,7 @@ class AlbumChipActionDelegate extends ChipActionDelegate with FeedbackMixin, Per
final newName = await showDialog<String>(
context: context,
builder: (context) => RenameAlbumDialog(album),
builder: (context) => RenameAlbumDialog(album: album),
);
if (newName == null || newName.isEmpty) return;

View file

@ -11,9 +11,10 @@ class FilterDraggableThumbLabel<T extends CollectionFilter> extends StatelessWid
final double offsetY;
const FilterDraggableThumbLabel({
Key? key,
required this.sortFactor,
required this.offsetY,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -91,7 +91,7 @@ class FilterGridPage<T extends CollectionFilter> extends StatelessWidget {
),
),
),
drawer: AppDrawer(),
drawer: const AppDrawer(),
resizeToAvoidBottomInset: false,
),
);
@ -454,7 +454,7 @@ class _FilterScrollView<T extends CollectionFilter> extends StatelessWidget {
)
: SectionedListSliver<FilterGridItem<T>>();
}),
BottomPaddingSliver(),
const BottomPaddingSliver(),
],
);
}

View file

@ -37,6 +37,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
final Widget Function() emptyBuilder;
const FilterNavigationPage({
Key? key,
required this.source,
required this.title,
required this.sortFactor,
@ -47,7 +48,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
required this.chipActionsBuilder,
required this.filterSections,
required this.emptyBuilder,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -80,10 +81,12 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
context,
MaterialPageRoute(
settings: const RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(CollectionLens(
source: source,
filters: [filter],
)),
builder: (context) => CollectionPage(
collection: CollectionLens(
source: source,
filters: [filter],
),
),
),
),
onLongPress: isMainMode ? _showMenu as OffsetFilterCallback : null,
@ -111,7 +114,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
List<Widget> _buildActions(BuildContext context) {
return [
CollectionSearchButton(source),
CollectionSearchButton(source: source),
PopupMenuButton<ChipSetAction>(
key: const Key('appbar-menu-button'),
itemBuilder: (context) {

View file

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
class SectionedFilterListLayoutProvider<T extends CollectionFilter> extends SectionedListLayoutProvider<FilterGridItem<T>> {
const SectionedFilterListLayoutProvider({
Key? key,
required this.sections,
required this.showHeaders,
required double scrollableWidth,
@ -17,6 +18,7 @@ class SectionedFilterListLayoutProvider<T extends CollectionFilter> extends Sect
required Duration tileAnimationDelay,
required Widget child,
}) : super(
key: key,
scrollableWidth: scrollableWidth,
columnCount: columnCount,
spacing: spacing,

View file

@ -20,6 +20,8 @@ import 'package:tuple/tuple.dart';
class CountryListPage extends StatelessWidget {
static const routeName = '/countries';
const CountryListPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();

View file

@ -20,6 +20,8 @@ import 'package:tuple/tuple.dart';
class TagListPage extends StatelessWidget {
static const routeName = '/tags';
const TagListPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final source = context.read<CollectionSource>();

View file

@ -9,6 +9,7 @@ import 'package:aves/model/source/collection_source.dart';
import 'package:aves/services/services.dart';
import 'package:aves/services/viewer_service.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/behaviour/routes.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
@ -18,7 +19,6 @@ import 'package:aves/widgets/viewer/entry_viewer_page.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pedantic/pedantic.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
@ -28,7 +28,10 @@ class HomePage extends StatefulWidget {
// untyped map as it is coming from the platform
final Map? intentData;
const HomePage({this.intentData});
const HomePage({
Key? key,
this.intentData,
}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
@ -147,7 +150,7 @@ class _HomePageState extends State<HomePage> {
case AlbumListPage.routeName:
return DirectMaterialPageRoute(
settings: const RouteSettings(name: AlbumListPage.routeName),
builder: (_) => AlbumListPage(),
builder: (_) => const AlbumListPage(),
);
case SearchPage.routeName:
return SearchPageRoute(
@ -158,7 +161,7 @@ class _HomePageState extends State<HomePage> {
return DirectMaterialPageRoute(
settings: const RouteSettings(name: CollectionPage.routeName),
builder: (_) => CollectionPage(
CollectionLens(
collection: CollectionLens(
source: source,
filters: filters,
),

View file

@ -13,12 +13,13 @@ class ExpandableFilterRow extends StatelessWidget {
final FilterCallback onTap;
const ExpandableFilterRow({
Key? key,
this.title,
required this.filters,
required this.expandedNotifier,
this.heroTypeBuilder,
required this.onTap,
});
}) : super(key: key);
static const double horizontalPadding = 8;
static const double verticalPadding = 8;

View file

@ -8,7 +8,11 @@ class CollectionSearchButton extends StatelessWidget {
final CollectionSource source;
final CollectionLens? parentCollection;
const CollectionSearchButton(this.source, {this.parentCollection});
const CollectionSearchButton({
Key? key,
required this.source,
this.parentCollection,
}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -243,10 +243,12 @@ class CollectionSearchDelegate {
context,
MaterialPageRoute(
settings: const RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(CollectionLens(
source: source,
filters: [filter],
)),
builder: (context) => CollectionPage(
collection: CollectionLens(
source: source,
filters: [filter],
),
),
),
(route) => false,
);

Some files were not shown because too many files have changed in this diff Show more