diff --git a/lib/account.dart b/lib/account.dart index 8f06dd0e..922b104c 100644 --- a/lib/account.dart +++ b/lib/account.dart @@ -18,11 +18,11 @@ class Account with EquatableMixin { } Account copyWith({ - String scheme, - String address, - String username, - String password, - List roots, + String? scheme, + String? address, + String? username, + String? password, + List? roots, }) { return Account( scheme ?? this.scheme, @@ -39,7 +39,7 @@ class Account with EquatableMixin { "scheme: '$scheme', " "address: '$address', " "username: '$username', " - "password: '${password?.isNotEmpty == true ? (kDebugMode ? password : '***') : null}', " + "password: '${password.isNotEmpty == true ? (kDebugMode ? password : '***') : null}', " "roots: List {'${roots.join('\', \'')}'}, " "}"; } diff --git a/lib/api/api.dart b/lib/api/api.dart index 9e7ad0ca..7f64d4a4 100644 --- a/lib/api/api.dart +++ b/lib/api/api.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; @@ -43,9 +42,9 @@ class Api { Future request( String method, String endpoint, { - Map header, - String body, - Uint8List bodyBytes, + Map? header, + String? body, + Uint8List? bodyBytes, bool isResponseString = true, }) async { final url = _makeUri(endpoint); @@ -114,7 +113,7 @@ class _Files { Api _api; Future delete({ - @required String path, + required String path, }) async { try { return await _api.request("DELETE", path); @@ -125,7 +124,7 @@ class _Files { } Future get({ - @required String path, + required String path, }) async { try { return await _api.request("GET", path, isResponseString: false); @@ -136,9 +135,9 @@ class _Files { } Future put({ - @required String path, + required String path, String mime = "application/octet-stream", - Uint8List content, + required Uint8List content, }) async { try { return await _api.request( @@ -156,8 +155,8 @@ class _Files { } Future propfind({ - @required String path, - int depth, + required String path, + int? depth, getlastmodified, getetag, getcontenttype, @@ -176,8 +175,8 @@ class _Files { hasPreview, size, richWorkspace, - Map customNamespaces, - List customProperties, + Map? customNamespaces, + List? customProperties, }) async { try { final bool hasDavNs = (getlastmodified != null || @@ -287,10 +286,10 @@ class _Files { /// [namespaces] should be specified in the format {"URI": "prefix"}, eg, /// {"DAV:": "d"} Future proppatch({ - @required String path, - Map namespaces, - Map set, - List remove, + required String path, + Map? namespaces, + Map? set, + List? remove, }) async { try { final ns = { @@ -300,7 +299,7 @@ class _Files { builder ..processing("xml", "version=\"1.0\"") ..element("d:propertyupdate", namespaces: ns, nest: () { - if (set?.isNotEmpty == true) { + if (set != null && set.isNotEmpty) { builder.element("d:set", nest: () { builder.element("d:prop", nest: () { for (final e in set.entries) { @@ -311,7 +310,7 @@ class _Files { }); }); } - if (remove?.isNotEmpty == true) { + if (remove != null && remove.isNotEmpty) { builder.element("d:remove", nest: () { builder.element("d:prop", nest: () { for (final e in remove) { @@ -331,7 +330,7 @@ class _Files { /// A folder can be created by sending a MKCOL request to the folder Future mkcol({ - @required String path, + required String path, }) async { try { return await _api.request("MKCOL", path); @@ -344,9 +343,9 @@ class _Files { /// A file or folder can be copied by sending a COPY request to the file or /// folder and specifying the [destinationUrl] as full url Future copy({ - @required String path, - @required String destinationUrl, - bool overwrite, + required String path, + required String destinationUrl, + bool? overwrite, }) async { try { return await _api.request("COPY", path, header: { @@ -362,9 +361,9 @@ class _Files { /// A file or folder can be moved by sending a MOVE request to the file or /// folder and specifying the [destinationUrl] as full url Future move({ - @required String path, - @required String destinationUrl, - bool overwrite, + required String path, + required String destinationUrl, + bool? overwrite, }) async { try { return await _api.request("MOVE", path, header: { diff --git a/lib/api/api_util.dart b/lib/api/api_util.dart index 65b416a3..c9f56d94 100644 --- a/lib/api/api_util.dart +++ b/lib/api/api_util.dart @@ -1,5 +1,4 @@ /// Helper functions working with remote Nextcloud server -import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/api/api.dart'; @@ -10,10 +9,10 @@ import 'package:nc_photos/exception.dart'; String getFilePreviewUrl( Account account, File file, { - @required int width, - @required int height, - String mode, - bool a, + required int width, + required int height, + String? mode, + bool? a, }) { return "${account.url}/" "${getFilePreviewUrlRelative(file, width: width, height: height, mode: mode, a: a)}"; @@ -24,10 +23,10 @@ String getFilePreviewUrl( /// cropped String getFilePreviewUrlRelative( File file, { - @required int width, - @required int height, - String mode, - bool a, + required int width, + required int height, + String? mode, + bool? a, }) { String url; if (file.fileId != null) { @@ -70,7 +69,7 @@ Future exchangePassword(Account account) async { try { final appPwdRegex = RegExp(r"(.*)"); final appPwdMatch = appPwdRegex.firstMatch(response.body); - return appPwdMatch.group(1); + return appPwdMatch!.group(1)!; } catch (_) { // this happens when the address is not the base URL and so Nextcloud // returned the login page diff --git a/lib/app_db.dart b/lib/app_db.dart index 16d890b6..d8ca8a55 100644 --- a/lib/app_db.dart +++ b/lib/app_db.dart @@ -8,7 +8,6 @@ import 'package:nc_photos/entity/album/upgrader.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/mobile/platform.dart' if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform; -import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util; import 'package:synchronized/synchronized.dart'; class AppDb { @@ -140,7 +139,7 @@ class AppDbAlbumEntry { upgraderV1: AlbumUpgraderV1(), upgraderV2: AlbumUpgraderV2(), upgraderV3: AlbumUpgraderV3(), - ), + )!, ); } diff --git a/lib/bloc/album_search.dart b/lib/bloc/album_search.dart index 8e526c17..6dbfeceb 100644 --- a/lib/bloc/album_search.dart +++ b/lib/bloc/album_search.dart @@ -91,12 +91,12 @@ class AlbumSearchBloc extends Bloc { _albums = ev.albums; if (_lastSearch != null) { // search again - yield* _onEventSearch(_lastSearch); + yield* _onEventSearch(_lastSearch!); } } var _albums = []; - AlbumSearchBlocSearchEvent _lastSearch; + AlbumSearchBlocSearchEvent? _lastSearch; static final _log = Logger("bloc.album_search.AlbumSearchBloc"); } diff --git a/lib/bloc/album_search_suggestion.dart b/lib/bloc/album_search_suggestion.dart index cc3f51c4..57a5d8b6 100644 --- a/lib/bloc/album_search_suggestion.dart +++ b/lib/bloc/album_search_suggestion.dart @@ -112,12 +112,12 @@ class AlbumSearchSuggestionBloc extends Bloc albums; } @@ -56,18 +56,18 @@ class ListAlbumBlocInit extends ListAlbumBlocState { } class ListAlbumBlocLoading extends ListAlbumBlocState { - const ListAlbumBlocLoading(Account account, List albums) + const ListAlbumBlocLoading(Account? account, List albums) : super(account, albums); } class ListAlbumBlocSuccess extends ListAlbumBlocState { - const ListAlbumBlocSuccess(Account account, List albums) + const ListAlbumBlocSuccess(Account? account, List albums) : super(account, albums); } class ListAlbumBlocFailure extends ListAlbumBlocState { const ListAlbumBlocFailure( - Account account, List albums, this.exception) + Account? account, List albums, this.exception) : super(account, albums); @override @@ -84,7 +84,7 @@ class ListAlbumBlocFailure extends ListAlbumBlocState { /// The state of this bloc is inconsistent. This typically means that the data /// may have been changed externally class ListAlbumBlocInconsistent extends ListAlbumBlocState { - const ListAlbumBlocInconsistent(Account account, List albums) + const ListAlbumBlocInconsistent(Account? account, List albums) : super(account, albums); } @@ -134,7 +134,7 @@ class ListAlbumBloc extends Bloc { if (newState is ListAlbumBlocFailure) { yield ListAlbumBlocFailure( ev.account, - newState.albums?.isNotEmpty == true ? newState.albums : state.albums, + newState.albums.isNotEmpty ? newState.albums : state.albums, newState.exception); } else { yield newState; @@ -206,9 +206,9 @@ class ListAlbumBloc extends Bloc { } } - AppEventListener _albumUpdatedListener; - AppEventListener _fileRemovedListener; - AppEventListener _albumCreatedListener; + late AppEventListener _albumUpdatedListener; + late AppEventListener _fileRemovedListener; + late AppEventListener _albumCreatedListener; static final _log = Logger("bloc.list_album.ListAlbumBloc"); } diff --git a/lib/bloc/ls_dir.dart b/lib/bloc/ls_dir.dart index 2c32eec2..aa0a2214 100644 --- a/lib/bloc/ls_dir.dart +++ b/lib/bloc/ls_dir.dart @@ -13,9 +13,11 @@ class LsDirBlocItem { if (isDeep) { return "$runtimeType:${_toDeepString(0)}"; } else { + final childrenStr = + children == null ? "null" : "List {length: ${children!.length}}"; return "$runtimeType {" "file: '${file.path}', " - "children: List {length: ${children.length}}, " + "children: $childrenStr, " "}"; } } @@ -23,19 +25,19 @@ class LsDirBlocItem { String _toDeepString(int level) { String product = "\n" + " " * (level * 2) + "-${file.path}"; if (children != null) { - for (final c in children) { + for (final c in children!) { product += c._toDeepString(level + 1); } } return product; } - File file; + final File file; /// Child directories under this directory /// /// Null if this dir is not listed, due to things like depth limitation - List children; + List? children; } abstract class LsDirBlocEvent { @@ -59,9 +61,9 @@ class LsDirBlocQuery extends LsDirBlocEvent { } LsDirBlocQuery copyWith({ - Account account, - File root, - int depth, + Account? account, + File? root, + int? depth, }) { return LsDirBlocQuery( account ?? this.account, @@ -76,11 +78,7 @@ class LsDirBlocQuery extends LsDirBlocEvent { } abstract class LsDirBlocState { - const LsDirBlocState(this._account, this._root, this._items); - - Account get account => _account; - File get root => _root; - List get items => _items; + const LsDirBlocState(this.account, this.root, this.items); @override toString() { @@ -91,9 +89,9 @@ abstract class LsDirBlocState { "}"; } - final Account _account; - final File _root; - final List _items; + final Account? account; + final File root; + final List items; } class LsDirBlocInit extends LsDirBlocState { @@ -101,18 +99,18 @@ class LsDirBlocInit extends LsDirBlocState { } class LsDirBlocLoading extends LsDirBlocState { - const LsDirBlocLoading(Account account, File root, List items) + const LsDirBlocLoading(Account? account, File root, List items) : super(account, root, items); } class LsDirBlocSuccess extends LsDirBlocState { - const LsDirBlocSuccess(Account account, File root, List items) + const LsDirBlocSuccess(Account? account, File root, List items) : super(account, root, items); } class LsDirBlocFailure extends LsDirBlocState { const LsDirBlocFailure( - Account account, File root, List items, this.exception) + Account? account, File root, List items, this.exception) : super(account, root, items); @override @@ -153,12 +151,12 @@ class LsDirBloc extends Bloc { var files = _cache[ev.root.path]; if (files == null) { files = (await Ls(FileRepo(FileWebdavDataSource()))(ev.account, ev.root)) - .where((f) => f.isCollection) + .where((f) => f.isCollection ?? false) .toList(); _cache[ev.root.path] = files; } for (final f in files) { - List children; + List? children; if (ev.depth > 1) { children = await _query(ev.copyWith(root: f, depth: ev.depth - 1)); } diff --git a/lib/bloc/scan_dir.dart b/lib/bloc/scan_dir.dart index 0a561d5b..16318fe9 100644 --- a/lib/bloc/scan_dir.dart +++ b/lib/bloc/scan_dir.dart @@ -58,10 +58,7 @@ class _ScanDirBlocExternalEvent extends ScanDirBlocEvent { } abstract class ScanDirBlocState { - const ScanDirBlocState(this._account, this._files); - - Account get account => _account; - List get files => _files; + const ScanDirBlocState(this.account, this.files); @override toString() { @@ -71,8 +68,8 @@ abstract class ScanDirBlocState { "}"; } - final Account _account; - final List _files; + final Account? account; + final List files; } class ScanDirBlocInit extends ScanDirBlocState { @@ -80,17 +77,17 @@ class ScanDirBlocInit extends ScanDirBlocState { } class ScanDirBlocLoading extends ScanDirBlocState { - const ScanDirBlocLoading(Account account, List files) + const ScanDirBlocLoading(Account? account, List files) : super(account, files); } class ScanDirBlocSuccess extends ScanDirBlocState { - const ScanDirBlocSuccess(Account account, List files) + const ScanDirBlocSuccess(Account? account, List files) : super(account, files); } class ScanDirBlocFailure extends ScanDirBlocState { - const ScanDirBlocFailure(Account account, List files, this.exception) + const ScanDirBlocFailure(Account? account, List files, this.exception) : super(account, files); @override @@ -107,7 +104,7 @@ class ScanDirBlocFailure extends ScanDirBlocState { /// The state of this bloc is inconsistent. This typically means that the data /// may have been changed externally class ScanDirBlocInconsistent extends ScanDirBlocState { - const ScanDirBlocInconsistent(Account account, List files) + const ScanDirBlocInconsistent(Account? account, List files) : super(account, files); } @@ -285,11 +282,12 @@ class ScanDirBloc extends Bloc { } } - AppEventListener _fileRemovedEventListener; - AppEventListener _filePropertyUpdatedEventListener; + late AppEventListener _fileRemovedEventListener; + late AppEventListener + _filePropertyUpdatedEventListener; int _successivePropertyUpdatedCount = 0; - StreamSubscription _propertyUpdatedSubscription; + StreamSubscription? _propertyUpdatedSubscription; bool _shouldCheckCache = true; diff --git a/lib/cache_manager_util.dart b/lib/cache_manager_util.dart index 823fedee..cd231a05 100644 --- a/lib/cache_manager_util.dart +++ b/lib/cache_manager_util.dart @@ -7,12 +7,16 @@ class CancelableGetFile { Future getFileUntil(String key, {bool ignoreMemCache = false}) async { - FileInfo product; + FileInfo? product; while (product == null && _shouldRun) { product = await store.getFile(key, ignoreMemCache: ignoreMemCache); await Future.delayed(Duration(milliseconds: 500)); } - return product ?? Future.error("Interrupted"); + if (product == null) { + return Future.error("Interrupted"); + } else { + return product; + } } void cancel() { diff --git a/lib/entity/album.dart b/lib/entity/album.dart index cd4dd7f9..ef4c4fca 100644 --- a/lib/entity/album.dart +++ b/lib/entity/album.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'dart:math'; import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; import 'package:idb_sqflite/idb_sqflite.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; @@ -31,57 +30,57 @@ bool isAlbumFile(Account account, File file) => /// Immutable object that represents an album class Album with EquatableMixin { Album({ - DateTime lastUpdated, - @required String name, - @required this.provider, - @required this.coverProvider, - @required this.sortProvider, + DateTime? lastUpdated, + required this.name, + required this.provider, + required this.coverProvider, + required this.sortProvider, this.albumFile, - }) : this.lastUpdated = (lastUpdated ?? DateTime.now()).toUtc(), - this.name = name ?? ""; + }) : this.lastUpdated = (lastUpdated ?? DateTime.now()).toUtc(); - factory Album.fromJson( + static Album? fromJson( Map json, { - AlbumUpgraderV1 upgraderV1, - AlbumUpgraderV2 upgraderV2, - AlbumUpgraderV3 upgraderV3, + required AlbumUpgraderV1? upgraderV1, + required AlbumUpgraderV2? upgraderV2, + required AlbumUpgraderV3? upgraderV3, }) { final jsonVersion = json["version"]; + Map? result = json; if (jsonVersion < 2) { - json = upgraderV1?.call(json); - if (json == null) { + result = upgraderV1?.call(result); + if (result == null) { _log.info("[fromJson] Version $jsonVersion not compatible"); return null; } } if (jsonVersion < 3) { - json = upgraderV2?.call(json); - if (json == null) { + result = upgraderV2?.call(result); + if (result == null) { _log.info("[fromJson] Version $jsonVersion not compatible"); return null; } } if (jsonVersion < 4) { - json = upgraderV3?.call(json); - if (json == null) { + result = upgraderV3?.call(result); + if (result == null) { _log.info("[fromJson] Version $jsonVersion not compatible"); return null; } } return Album( - lastUpdated: json["lastUpdated"] == null + lastUpdated: result["lastUpdated"] == null ? null - : DateTime.parse(json["lastUpdated"]), - name: json["name"], + : DateTime.parse(result["lastUpdated"]), + name: result["name"], provider: - AlbumProvider.fromJson(json["provider"].cast()), + AlbumProvider.fromJson(result["provider"].cast()), coverProvider: AlbumCoverProvider.fromJson( - json["coverProvider"].cast()), + result["coverProvider"].cast()), sortProvider: AlbumSortProvider.fromJson( - json["sortProvider"].cast()), - albumFile: json["albumFile"] == null + result["sortProvider"].cast()), + albumFile: result["albumFile"] == null ? null - : File.fromJson(json["albumFile"].cast()), + : File.fromJson(result["albumFile"].cast()), ); } @@ -103,12 +102,12 @@ class Album with EquatableMixin { /// will be used. In order to keep [lastUpdated], you must explicitly assign /// it with value from this or a null value Album copyWith({ - OrNull lastUpdated, - String name, - AlbumProvider provider, - AlbumCoverProvider coverProvider, - AlbumSortProvider sortProvider, - File albumFile, + OrNull? lastUpdated, + String? name, + AlbumProvider? provider, + AlbumCoverProvider? coverProvider, + AlbumSortProvider? sortProvider, + File? albumFile, }) { return Album( lastUpdated: @@ -141,7 +140,7 @@ class Album with EquatableMixin { "provider": provider.toJson(), "coverProvider": coverProvider.toJson(), "sortProvider": sortProvider.toJson(), - if (albumFile != null) "albumFile": albumFile.toJson(), + if (albumFile != null) "albumFile": albumFile!.toJson(), }; } @@ -165,7 +164,7 @@ class Album with EquatableMixin { /// How is this album stored on server /// /// This field is typically only meaningful when returned by [AlbumRepo.get] - final File albumFile; + final File? albumFile; /// versioning of this class, use to upgrade old persisted album static const version = 4; @@ -224,7 +223,8 @@ class AlbumRemoteDataSource implements AlbumDataSource { upgraderV1: AlbumUpgraderV1(), upgraderV2: AlbumUpgraderV2(), upgraderV3: AlbumUpgraderV3(), - ).copyWith( + )! + .copyWith( lastUpdated: OrNull(null), albumFile: albumFile, ); @@ -245,8 +245,8 @@ class AlbumRemoteDataSource implements AlbumDataSource { final filePath = "${remote_storage_util.getRemoteAlbumsDir(account)}/$fileName"; final fileRepo = FileRepo(FileWebdavDataSource()); - await PutFileBinary(fileRepo)( - account, filePath, utf8.encode(jsonEncode(album.toRemoteJson())), + await PutFileBinary(fileRepo)(account, filePath, + Utf8Encoder().convert(jsonEncode(album.toRemoteJson())), shouldCreateMissingDir: true); // query album file final list = await Ls(fileRepo)(account, File(path: filePath), @@ -256,10 +256,10 @@ class AlbumRemoteDataSource implements AlbumDataSource { @override update(Account account, Album album) async { - _log.info("[update] ${album.albumFile.path}"); + _log.info("[update] ${album.albumFile!.path}"); final fileRepo = FileRepo(FileWebdavDataSource()); - await PutFileBinary(fileRepo)(account, album.albumFile.path, - utf8.encode(jsonEncode(album.toRemoteJson()))); + await PutFileBinary(fileRepo)(account, album.albumFile!.path, + Utf8Encoder().convert(jsonEncode(album.toRemoteJson()))); } @override @@ -286,7 +286,7 @@ class AlbumAppDbDataSource implements AlbumDataSource { final path = AppDbAlbumEntry.toPathFromFile(account, albumFile); final range = KeyRange.bound([path, 0], [path, int_util.int32Max]); final List results = await index.getAll(range); - if (results?.isNotEmpty == true) { + if (results.isNotEmpty == true) { final entries = results .map((e) => AppDbAlbumEntry.fromJson(e.cast())); if (entries.length > 1) { @@ -317,7 +317,7 @@ class AlbumAppDbDataSource implements AlbumDataSource { @override update(Account account, Album album) { - _log.info("[update] ${album.albumFile.path}"); + _log.info("[update] ${album.albumFile!.path}"); return AppDb.use((db) async { final transaction = db.transaction(AppDb.albumStoreName, idbModeReadWrite); @@ -337,8 +337,8 @@ class AlbumCachedDataSource implements AlbumDataSource { get(Account account, File albumFile) async { try { final cache = await _appDbSrc.get(account, albumFile); - if (cache.albumFile.etag?.isNotEmpty == true && - cache.albumFile.etag == albumFile.etag) { + if (cache.albumFile!.etag?.isNotEmpty == true && + cache.albumFile!.etag == albumFile.etag) { // cache is good _log.fine( "[get] etag matched for ${AppDbAlbumEntry.toPathFromFile(account, albumFile)}"); @@ -412,7 +412,7 @@ class AlbumCachedDataSource implements AlbumDataSource { Future _cacheAlbum( ObjectStore store, Account account, Album album) async { final index = store.index(AppDbAlbumEntry.indexName); - final path = AppDbAlbumEntry.toPathFromFile(account, album.albumFile); + final path = AppDbAlbumEntry.toPathFromFile(account, album.albumFile!); final range = KeyRange.bound([path, 0], [path, int_util.int32Max]); // count number of entries for this album final count = await index.count(range); @@ -441,7 +441,7 @@ Future _cacheAlbum( for (final e in entries) { _log.info("[_cacheAlbum] Caching ${e.path}[${e.index}]"); await store.put(e.toJson(), - AppDbAlbumEntry.toPrimaryKey(account, e.album.albumFile, e.index)); + AppDbAlbumEntry.toPrimaryKey(account, e.album.albumFile!, e.index)); } if (count > entries.length) { diff --git a/lib/entity/album/cover_provider.dart b/lib/entity/album/cover_provider.dart index c8ef6b5f..1c371380 100644 --- a/lib/entity/album/cover_provider.dart +++ b/lib/entity/album/cover_provider.dart @@ -40,7 +40,7 @@ abstract class AlbumCoverProvider with EquatableMixin { @override toString(); - File getCover(Album album); + File? getCover(Album album); Map _toContentJson(); @@ -68,7 +68,8 @@ class AlbumAutoCoverProvider extends AlbumCoverProvider { "}"; } - File getCover(Album album) { + @override + getCover(Album album) { if (coverFile == null) { try { // use the latest file as cover @@ -77,7 +78,8 @@ class AlbumAutoCoverProvider extends AlbumCoverProvider { .whereType() .map((e) => e.file) .where((element) => - file_util.isSupportedFormat(element) && element.hasPreview) + file_util.isSupportedFormat(element) && + (element.hasPreview ?? false)) .sorted(compareFileDateTimeDescending) .first; } catch (_) { @@ -96,11 +98,11 @@ class AlbumAutoCoverProvider extends AlbumCoverProvider { @override _toContentJson() { return { - if (coverFile != null) "coverFile": coverFile.toJson(), + if (coverFile != null) "coverFile": coverFile!.toJson(), }; } - final File coverFile; + final File? coverFile; static const _type = "auto"; } diff --git a/lib/entity/album/item.dart b/lib/entity/album/item.dart index 353940a1..b7dd4f70 100644 --- a/lib/entity/album/item.dart +++ b/lib/entity/album/item.dart @@ -1,7 +1,6 @@ import 'dart:math'; import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/list_extension.dart'; @@ -60,18 +59,16 @@ abstract class AlbumItem { class AlbumFileItem extends AlbumItem with EquatableMixin { AlbumFileItem({ - @required this.file, + required this.file, }); @override // ignore: hash_and_equals - bool operator ==(Object other) => equals(other, isDeep: true); + bool operator ==(Object? other) => equals(other, isDeep: true); - bool equals(Object other, {bool isDeep = false}) { + bool equals(Object? other, {bool isDeep = false}) { if (other is AlbumFileItem) { - return super == other && - (file == null) == (other.file == null) && - (file?.equals(other.file, isDeep: isDeep) ?? true); + return super == other && (file.equals(other.file, isDeep: isDeep)); } else { return false; } @@ -109,7 +106,7 @@ class AlbumFileItem extends AlbumItem with EquatableMixin { class AlbumLabelItem extends AlbumItem with EquatableMixin { AlbumLabelItem({ - @required this.text, + required this.text, }); factory AlbumLabelItem.fromJson(Map json) { diff --git a/lib/entity/album/provider.dart b/lib/entity/album/provider.dart index 450f439e..8b8bcf0a 100644 --- a/lib/entity/album/provider.dart +++ b/lib/entity/album/provider.dart @@ -50,7 +50,7 @@ abstract class AlbumProvider with EquatableMixin { toString({bool isDeep = false}); /// Return the date time associated with the latest item, or null - DateTime get latestItemTime; + DateTime? get latestItemTime; AlbumProvider copyWith(); @@ -59,7 +59,7 @@ abstract class AlbumProvider with EquatableMixin { class AlbumStaticProvider extends AlbumProvider { AlbumStaticProvider({ - @required List items, + required List items, }) : this.items = UnmodifiableListView(items); factory AlbumStaticProvider.fromJson(Map json) { @@ -90,7 +90,9 @@ class AlbumStaticProvider extends AlbumProvider { } @override - AlbumStaticProvider copyWith({List items}) { + AlbumStaticProvider copyWith({ + List? items, + }) { return AlbumStaticProvider( items: items ?? this.items, ); @@ -124,7 +126,7 @@ class AlbumStaticProvider extends AlbumProvider { abstract class AlbumDynamicProvider extends AlbumProvider { AlbumDynamicProvider({ - DateTime latestItemTime, + DateTime? latestItemTime, }) : _latestItemTime = latestItemTime; @override @@ -137,13 +139,13 @@ abstract class AlbumDynamicProvider extends AlbumProvider { @override toContentJson() { return { - "latestItemTime": _latestItemTime?.toUtc()?.toIso8601String(), + "latestItemTime": _latestItemTime?.toUtc().toIso8601String(), }; } @override AlbumDynamicProvider copyWith({ - DateTime latestItemTime, + DateTime? latestItemTime, }); @override @@ -154,13 +156,13 @@ abstract class AlbumDynamicProvider extends AlbumProvider { _latestItemTime, ]; - final DateTime _latestItemTime; + final DateTime? _latestItemTime; } class AlbumDirProvider extends AlbumDynamicProvider { AlbumDirProvider({ - @required this.dirs, - DateTime latestItemTime, + required this.dirs, + DateTime? latestItemTime, }) : super(latestItemTime: latestItemTime); factory AlbumDirProvider.fromJson(Map json) { @@ -192,8 +194,8 @@ class AlbumDirProvider extends AlbumDynamicProvider { @override AlbumDirProvider copyWith({ - List dirs, - DateTime latestItemTime, + List? dirs, + DateTime? latestItemTime, }) { return AlbumDirProvider( dirs: dirs ?? this.dirs, diff --git a/lib/entity/album/sort_provider.dart b/lib/entity/album/sort_provider.dart index 0fa9626c..258e5617 100644 --- a/lib/entity/album/sort_provider.dart +++ b/lib/entity/album/sort_provider.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/entity/album/item.dart'; import 'package:nc_photos/entity/file.dart'; @@ -80,7 +79,7 @@ class AlbumNullSortProvider extends AlbumSortProvider { abstract class AlbumReversibleSortProvider extends AlbumSortProvider { const AlbumReversibleSortProvider({ - @required this.isAscending, + required this.isAscending, }); @override @@ -108,7 +107,7 @@ abstract class AlbumReversibleSortProvider extends AlbumSortProvider { /// Sort based on the time of the files class AlbumTimeSortProvider extends AlbumReversibleSortProvider { const AlbumTimeSortProvider({ - bool isAscending, + required bool isAscending, }) : super(isAscending: isAscending); factory AlbumTimeSortProvider.fromJson(Map json) { @@ -126,7 +125,7 @@ class AlbumTimeSortProvider extends AlbumReversibleSortProvider { @override sort(List items) { - DateTime prevFileTime; + DateTime? prevFileTime; return items .map((e) { if (e is AlbumFileItem) { @@ -139,16 +138,16 @@ class AlbumTimeSortProvider extends AlbumReversibleSortProvider { .stableSorted((x, y) { if (x.item1 == null && y.item1 == null) { return 0; - } - else if (x.item1 == null) { + } else if (x.item1 == null) { return -1; } else if (y.item1 == null) { return 1; - } - if (isAscending) { - return x.item1.compareTo(y.item1); } else { - return y.item1.compareTo(x.item1); + if (isAscending) { + return x.item1!.compareTo(y.item1!); + } else { + return y.item1!.compareTo(x.item1!); + } } }) .map((e) => e.item2) diff --git a/lib/entity/album/upgrader.dart b/lib/entity/album/upgrader.dart index f73deb8f..d2a38668 100644 --- a/lib/entity/album/upgrader.dart +++ b/lib/entity/album/upgrader.dart @@ -1,7 +1,7 @@ import 'package:logging/logging.dart'; abstract class AlbumUpgrader { - Map call(Map json); + Map? call(Map json); } /// Upgrade v1 Album to v2 @@ -10,7 +10,8 @@ class AlbumUpgraderV1 implements AlbumUpgrader { this.logFilePath, }); - Map call(Map json) { + @override + call(Map json) { // v1 album items are corrupted in one of the updates, drop it _log.fine("[call] Upgrade v1 Album for file: $logFilePath"); final result = Map.from(json); @@ -19,7 +20,7 @@ class AlbumUpgraderV1 implements AlbumUpgrader { } /// File path for logging only - final String logFilePath; + final String? logFilePath; static final _log = Logger("entity.album.upgrader.AlbumUpgraderV1"); } @@ -30,7 +31,8 @@ class AlbumUpgraderV2 implements AlbumUpgrader { this.logFilePath, }); - Map call(Map json) { + @override + call(Map json) { // move v2 items to v3 provider _log.fine("[call] Upgrade v2 Album for file: $logFilePath"); final result = Map.from(json); @@ -51,7 +53,7 @@ class AlbumUpgraderV2 implements AlbumUpgrader { } /// File path for logging only - final String logFilePath; + final String? logFilePath; static final _log = Logger("entity.album.upgrader.AlbumUpgraderV2"); } @@ -62,7 +64,8 @@ class AlbumUpgraderV3 implements AlbumUpgrader { this.logFilePath, }); - Map call(Map json) { + @override + call(Map json) { // move v3 items to v4 provider _log.fine("[call] Upgrade v3 Album for file: $logFilePath"); final result = Map.from(json); @@ -77,7 +80,7 @@ class AlbumUpgraderV3 implements AlbumUpgrader { } /// File path for logging only - final String logFilePath; + final String? logFilePath; static final _log = Logger("entity.album.upgrader.AlbumUpgraderV3"); } diff --git a/lib/entity/exif.dart b/lib/entity/exif.dart index 635aeb77..3a36856a 100644 --- a/lib/entity/exif.dart +++ b/lib/entity/exif.dart @@ -9,14 +9,14 @@ class Exif with EquatableMixin { @override // ignore: hash_and_equals - bool operator ==(Object other) => equals(other, isDeep: true); + bool operator ==(Object? other) => equals(other, isDeep: true); /// Compare two Exif objects /// /// If [isDeep] is false, two Exif objects are considered identical if they /// contain the same number of fields. This hack is to save time comparing a /// large amount of data that are mostly immutable - bool equals(Object other, {bool isDeep = false}) { + bool equals(Object? other, {bool isDeep = false}) { if (isDeep) { return super == other; } else { @@ -87,33 +87,33 @@ class Exif with EquatableMixin { } /// 0x010f Make - String get make => data["Make"]; + String? get make => data["Make"]; /// 0x0110 Model - String get model => data["Model"]; + String? get model => data["Model"]; /// 0x9003 DateTimeOriginal - DateTime get dateTimeOriginal => data.containsKey("DateTimeOriginal") + DateTime? get dateTimeOriginal => data.containsKey("DateTimeOriginal") ? dateTimeFormat.parse(data["DateTimeOriginal"]).toUtc() : null; /// 0x829a ExposureTime - Rational get exposureTime => data["ExposureTime"]; + Rational? get exposureTime => data["ExposureTime"]; /// 0x829d FNumber - Rational get fNumber => data["FNumber"]; + Rational? get fNumber => data["FNumber"]; /// 0x8827 ISO/ISOSpeedRatings/PhotographicSensitivity - int get isoSpeedRatings => data["ISOSpeedRatings"]; + int? get isoSpeedRatings => data["ISOSpeedRatings"]; /// 0x920a FocalLength - Rational get focalLength => data["FocalLength"]; + Rational? get focalLength => data["FocalLength"]; /// 0x8825 GPS tags - String get gpsLatitudeRef => data["GPSLatitudeRef"]; - List get gpsLatitude => data["GPSLatitude"].cast(); - String get gpsLongitudeRef => data["GPSLongitudeRef"]; - List get gpsLongitude => data["GPSLongitude"].cast(); + String? get gpsLatitudeRef => data["GPSLatitudeRef"]; + List? get gpsLatitude => data["GPSLatitude"].cast(); + String? get gpsLongitudeRef => data["GPSLongitudeRef"]; + List? get gpsLongitude => data["GPSLongitude"].cast(); @override get props => [ diff --git a/lib/entity/file.dart b/lib/entity/file.dart index 6a0272ab..ba8beaab 100644 --- a/lib/entity/file.dart +++ b/lib/entity/file.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/entity/exif.dart'; @@ -21,7 +20,7 @@ int compareFileDateTimeDescending(File x, File y) { /// Immutable object that hold metadata of a [File] class Metadata with EquatableMixin { Metadata({ - DateTime lastUpdated, + DateTime? lastUpdated, this.fileEtag, this.imageWidth, this.imageHeight, @@ -30,9 +29,9 @@ class Metadata with EquatableMixin { @override // ignore: hash_and_equals - bool operator ==(Object other) => equals(other, isDeep: true); + bool operator ==(Object? other) => equals(other, isDeep: true); - bool equals(Object other, {bool isDeep = false}) { + bool equals(Object? other, {bool isDeep = false}) { if (other is Metadata) { return super == other && (exif == null) == (other.exif == null) && @@ -48,36 +47,37 @@ class Metadata with EquatableMixin { /// corresponding upgrader will be called one by one to upgrade the json, /// version by version until it reached the active version. If any upgrader /// in the chain is null, the upgrade process will fail - factory Metadata.fromJson( + static Metadata? fromJson( Map json, { - MetadataUpgraderV1 upgraderV1, - MetadataUpgraderV2 upgraderV2, + required MetadataUpgraderV1? upgraderV1, + required MetadataUpgraderV2? upgraderV2, }) { final jsonVersion = json["version"]; + Map? result = json; if (jsonVersion < 2) { - json = upgraderV1?.call(json); - if (json == null) { + result = upgraderV1?.call(result); + if (result == null) { _log.info("[fromJson] Version $jsonVersion not compatible"); return null; } } if (jsonVersion < 3) { - json = upgraderV2?.call(json); - if (json == null) { + result = upgraderV2?.call(result); + if (result == null) { _log.info("[fromJson] Version $jsonVersion not compatible"); return null; } } return Metadata( - lastUpdated: json["lastUpdated"] == null + lastUpdated: result["lastUpdated"] == null ? null - : DateTime.parse(json["lastUpdated"]), - fileEtag: json["fileEtag"], - imageWidth: json["imageWidth"], - imageHeight: json["imageHeight"], - exif: json["exif"] == null + : DateTime.parse(result["lastUpdated"]), + fileEtag: result["fileEtag"], + imageWidth: result["imageWidth"], + imageHeight: result["imageHeight"], + exif: result["exif"] == null ? null - : Exif.fromJson(json["exif"].cast()), + : Exif.fromJson(result["exif"].cast()), ); } @@ -88,7 +88,7 @@ class Metadata with EquatableMixin { if (fileEtag != null) "fileEtag": fileEtag, if (imageWidth != null) "imageWidth": imageWidth, if (imageHeight != null) "imageHeight": imageHeight, - if (exif != null) "exif": exif.toJson(), + if (exif != null) "exif": exif!.toJson(), }; } @@ -123,10 +123,10 @@ class Metadata with EquatableMixin { final DateTime lastUpdated; /// Etag of the parent file when the metadata is saved - final String fileEtag; - final int imageWidth; - final int imageHeight; - final Exif exif; + final String? fileEtag; + final int? imageWidth; + final int? imageHeight; + final Exif? exif; /// versioning of this class, use to upgrade old persisted metadata static const version = 3; @@ -135,17 +135,17 @@ class Metadata with EquatableMixin { } abstract class MetadataUpgrader { - Map call(Map json); + Map? call(Map json); } /// Upgrade v1 Metadata to v2 class MetadataUpgraderV1 implements MetadataUpgrader { MetadataUpgraderV1({ - @required this.fileContentType, + required this.fileContentType, this.logFilePath, }); - Map call(Map json) { + Map? call(Map json) { if (fileContentType == "image/webp") { // Version 1 metadata for webp is bugged, drop it _log.fine("[call] Upgrade v1 metadata for file: $logFilePath"); @@ -155,10 +155,10 @@ class MetadataUpgraderV1 implements MetadataUpgrader { } } - final String fileContentType; + final String? fileContentType; /// File path for logging only - final String logFilePath; + final String? logFilePath; static final _log = Logger("entity.file.MetadataUpgraderV1"); } @@ -166,11 +166,11 @@ class MetadataUpgraderV1 implements MetadataUpgrader { /// Upgrade v2 Metadata to v3 class MetadataUpgraderV2 implements MetadataUpgrader { MetadataUpgraderV2({ - @required this.fileContentType, + required this.fileContentType, this.logFilePath, }); - Map call(Map json) { + Map? call(Map json) { if (fileContentType == "image/jpeg") { // Version 2 metadata for jpeg doesn't consider orientation if (json["exif"] != null && json["exif"].containsKey("Orientation")) { @@ -187,17 +187,17 @@ class MetadataUpgraderV2 implements MetadataUpgrader { return json; } - final String fileContentType; + final String? fileContentType; /// File path for logging only - final String logFilePath; + final String? logFilePath; static final _log = Logger("entity.file.MetadataUpgraderV2"); } class File with EquatableMixin { File({ - @required String path, + required String path, this.contentLength, this.contentType, this.etag, @@ -214,9 +214,9 @@ class File with EquatableMixin { @override // ignore: hash_and_equals - bool operator ==(Object other) => equals(other, isDeep: true); + bool operator ==(Object? other) => equals(other, isDeep: true); - bool equals(Object other, {bool isDeep = false}) { + bool equals(Object? other, {bool isDeep = false}) { if (other is File) { return super == other && (metadata == null) == (other.metadata == null) && @@ -310,33 +310,33 @@ class File with EquatableMixin { if (contentType != null) "contentType": contentType, if (etag != null) "etag": etag, if (lastModified != null) - "lastModified": lastModified.toUtc().toIso8601String(), + "lastModified": lastModified!.toUtc().toIso8601String(), if (isCollection != null) "isCollection": isCollection, if (usedBytes != null) "usedBytes": usedBytes, if (hasPreview != null) "hasPreview": hasPreview, if (fileId != null) "fileId": fileId, if (ownerId != null) "ownerId": ownerId, - if (metadata != null) "metadata": metadata.toJson(), + if (metadata != null) "metadata": metadata!.toJson(), if (isArchived != null) "isArchived": isArchived, if (overrideDateTime != null) - "overrideDateTime": overrideDateTime.toUtc().toIso8601String(), + "overrideDateTime": overrideDateTime!.toUtc().toIso8601String(), }; } File copyWith({ - String path, - int contentLength, - String contentType, - String etag, - DateTime lastModified, - bool isCollection, - int usedBytes, - bool hasPreview, - int fileId, - String ownerId, - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + String? path, + int? contentLength, + String? contentType, + String? etag, + DateTime? lastModified, + bool? isCollection, + int? usedBytes, + bool? hasPreview, + int? fileId, + String? ownerId, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }) { return File( path: path ?? this.path, @@ -392,20 +392,20 @@ class File with EquatableMixin { ]; final String path; - final int contentLength; - final String contentType; - final String etag; - final DateTime lastModified; - final bool isCollection; - final int usedBytes; - final bool hasPreview; + final int? contentLength; + final String? contentType; + final String? etag; + final DateTime? lastModified; + final bool? isCollection; + final int? usedBytes; + final bool? hasPreview; // maybe null when loaded from old cache - final int fileId; - final String ownerId; + final int? fileId; + final String? ownerId; // metadata - final Metadata metadata; - final bool isArchived; - final DateTime overrideDateTime; + final Metadata? metadata; + final bool? isArchived; + final DateTime? overrideDateTime; } extension FileExtension on File { @@ -442,9 +442,9 @@ class FileRepo { Future updateProperty( Account account, File file, { - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }) => this.dataSrc.updateProperty( account, @@ -459,7 +459,7 @@ class FileRepo { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) => this.dataSrc.copy( account, @@ -473,7 +473,7 @@ class FileRepo { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) => this.dataSrc.move( account, @@ -506,9 +506,9 @@ abstract class FileDataSource { Future updateProperty( Account account, File f, { - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }); /// Copy [f] to [destination] @@ -519,7 +519,7 @@ abstract class FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }); /// Move [f] to [destination] @@ -530,7 +530,7 @@ abstract class FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }); /// Create a directory at [path] diff --git a/lib/entity/file/data_source.dart b/lib/entity/file/data_source.dart index 5f88643d..1e368828 100644 --- a/lib/entity/file/data_source.dart +++ b/lib/entity/file/data_source.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:idb_shim/idb_client.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; @@ -27,7 +26,7 @@ class FileWebdavDataSource implements FileDataSource { list( Account account, File f, { - int depth, + int? depth, }) async { _log.fine("[list] ${f.path}"); final response = await Api(account).files().propfind( @@ -61,7 +60,7 @@ class FileWebdavDataSource implements FileDataSource { final files = WebdavFileParser()(xml); // _log.fine("[list] Parsed files: [$files]"); return files.where((element) => _validateFile(element)).map((e) { - if (e.metadata == null || e.metadata.fileEtag == e.etag) { + if (e.metadata == null || e.metadata!.fileEtag == e.etag) { return e; } else { _log.info("[list] Ignore outdated metadata for ${e.path}"); @@ -112,27 +111,27 @@ class FileWebdavDataSource implements FileDataSource { updateProperty( Account account, File f, { - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }) async { _log.info("[updateProperty] ${f.path}"); - if (metadata?.obj != null && metadata.obj.fileEtag != f.etag) { + if (metadata?.obj != null && metadata!.obj!.fileEtag != f.etag) { _log.warning( - "[updateProperty] Metadata etag mismatch (metadata: ${metadata.obj.fileEtag}, file: ${f.etag})"); + "[updateProperty] Metadata etag mismatch (metadata: ${metadata.obj!.fileEtag}, file: ${f.etag})"); } final setProps = { if (metadata?.obj != null) - "app:metadata": jsonEncode(metadata.obj.toJson()), - if (isArchived?.obj != null) "app:is-archived": isArchived.obj, + "app:metadata": jsonEncode(metadata!.obj!.toJson()), + if (isArchived?.obj != null) "app:is-archived": isArchived!.obj, if (overrideDateTime?.obj != null) "app:override-date-time": - overrideDateTime.obj.toUtc().toIso8601String(), + overrideDateTime!.obj!.toUtc().toIso8601String(), }; final removeProps = [ - if (OrNull.isNull(metadata)) "app:metadata", - if (OrNull.isNull(isArchived)) "app:is-archived", - if (OrNull.isNull(overrideDateTime)) "app:override-date-time", + if (OrNull.isSetNull(metadata)) "app:metadata", + if (OrNull.isSetNull(isArchived)) "app:is-archived", + if (OrNull.isSetNull(overrideDateTime)) "app:override-date-time", ]; final response = await Api(account).files().proppatch( path: f.path, @@ -155,7 +154,7 @@ class FileWebdavDataSource implements FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) async { _log.info("[copy] ${f.path} to $destination"); final response = await Api(account).files().copy( @@ -176,7 +175,7 @@ class FileWebdavDataSource implements FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) async { _log.info("[move] ${f.path} to $destination"); final response = await Api(account).files().move( @@ -256,9 +255,9 @@ class FileAppDbDataSource implements FileDataSource { updateProperty( Account account, File f, { - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }) { _log.info("[updateProperty] ${f.path}"); return AppDb.use((db) async { @@ -300,7 +299,7 @@ class FileAppDbDataSource implements FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) async { // do nothing } @@ -310,7 +309,7 @@ class FileAppDbDataSource implements FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) async { // do nothing } @@ -325,7 +324,7 @@ class FileAppDbDataSource implements FileDataSource { final path = AppDbFileEntry.toPath(account, f); final range = KeyRange.bound([path, 0], [path, int_util.int32Max]); final List results = await index.getAll(range); - if (results?.isNotEmpty == true) { + if (results.isNotEmpty == true) { final entries = results .map((e) => AppDbFileEntry.fromJson(e.cast())); return entries @@ -358,7 +357,7 @@ class FileCachedDataSource implements FileDataSource { ); final cache = await cacheManager.list(account, f); if (cacheManager.isGood) { - return cache; + return cache!; } // no cache or outdated @@ -426,9 +425,9 @@ class FileCachedDataSource implements FileDataSource { updateProperty( Account account, File f, { - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }) async { await _remoteSrc .updateProperty( @@ -462,7 +461,7 @@ class FileCachedDataSource implements FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) async { await _remoteSrc.copy(account, f, destination, shouldOverwrite: shouldOverwrite); @@ -473,7 +472,7 @@ class FileCachedDataSource implements FileDataSource { Account account, File f, String destination, { - bool shouldOverwrite, + bool? shouldOverwrite, }) async { await _remoteSrc.move(account, f, destination, shouldOverwrite: shouldOverwrite); @@ -599,17 +598,17 @@ class FileCachedDataSource implements FileDataSource { class _CacheManager { _CacheManager({ - @required this.appDbSrc, - @required this.remoteSrc, + required this.appDbSrc, + required this.remoteSrc, this.shouldCheckCache = false, }); /// Return the cached results of listing a directory [f] /// /// Should check [isGood] before using the cache returning by this method - Future> list(Account account, File f) async { + Future?> list(Account account, File f) async { final trimmedRootPath = f.path.trimAny("/"); - List cache; + List? cache; try { cache = await appDbSrc.list(account, f); // compare the cached root @@ -645,7 +644,7 @@ class _CacheManager { } bool get isGood => _isGood; - String get remoteTouchToken => _remoteToken; + String? get remoteTouchToken => _remoteToken; Future _checkTouchToken( Account account, File f, List cache) async { @@ -653,7 +652,7 @@ class _CacheManager { "${remote_storage_util.getRemoteTouchDir(account)}/${f.strippedPath}"; final fileRepo = FileRepo(FileCachedDataSource()); final tokenManager = TouchTokenManager(); - String remoteToken; + String? remoteToken; try { remoteToken = await tokenManager.getRemoteToken(fileRepo, account, f); } catch (e, stacktrace) { @@ -664,7 +663,7 @@ class _CacheManager { } _remoteToken = remoteToken; - String localToken; + String? localToken; try { localToken = await tokenManager.getLocalToken(account, f); } catch (e, stacktrace) { @@ -687,7 +686,7 @@ class _CacheManager { final bool shouldCheckCache; var _isGood = false; - String _remoteToken; + String? _remoteToken; static final _log = Logger("entity.file.data_source._CacheManager"); } diff --git a/lib/entity/webdav_response_parser.dart b/lib/entity/webdav_response_parser.dart index 456a2499..11c326e4 100644 --- a/lib/entity/webdav_response_parser.dart +++ b/lib/entity/webdav_response_parser.dart @@ -31,7 +31,7 @@ class WebdavFileParser { return null; } }) - .where((element) => element != null) + .whereType() .toList(); } @@ -55,19 +55,19 @@ class WebdavFileParser { /// Map contents to File File _toFile(XmlElement element) { - String path; - int contentLength; - String contentType; - String etag; - DateTime lastModified; - bool isCollection; - int usedBytes; - bool hasPreview; - int fileId; - String ownerId; - Metadata metadata; - bool isArchived; - DateTime overrideDateTime; + String? path; + int? contentLength; + String? contentType; + String? etag; + DateTime? lastModified; + bool? isCollection; + int? usedBytes; + bool? hasPreview; + int? fileId; + String? ownerId; + Metadata? metadata; + bool? isArchived; + DateTime? overrideDateTime; for (final child in element.children.whereType()) { if (child.matchQualifiedName("href", @@ -105,7 +105,7 @@ class WebdavFileParser { } return File( - path: path, + path: path!, contentLength: contentLength, contentType: contentType, etag: etag, @@ -140,7 +140,10 @@ class WebdavFileParser { } class _PropParser { - _PropParser({this.namespaces = const {}, this.logFilePath}); + _PropParser({ + this.namespaces = const {}, + this.logFilePath, + }); /// Parse element contents void parse(XmlElement element) { @@ -201,43 +204,43 @@ class _PropParser { } } - DateTime get lastModified => _lastModified; - int get contentLength => _contentLength; - String get contentType => _contentType; - String get etag => _etag; - int get usedBytes => _usedBytes; - bool get isCollection => _isCollection; - bool get hasPreview => _hasPreview; - int get fileId => _fileId; - String get ownerId => _ownerId; - Metadata get metadata => _metadata; - bool get isArchived => _isArchived; - DateTime get overrideDateTime => _overrideDateTime; + DateTime? get lastModified => _lastModified; + int? get contentLength => _contentLength; + String? get contentType => _contentType; + String? get etag => _etag; + int? get usedBytes => _usedBytes; + bool? get isCollection => _isCollection; + bool? get hasPreview => _hasPreview; + int? get fileId => _fileId; + String? get ownerId => _ownerId; + Metadata? get metadata => _metadata; + bool? get isArchived => _isArchived; + DateTime? get overrideDateTime => _overrideDateTime; final Map namespaces; /// File path for logging only - final String logFilePath; + final String? logFilePath; - DateTime _lastModified; - int _contentLength; - String _contentType; - String _etag; - int _usedBytes; - bool _isCollection; - bool _hasPreview; - int _fileId; - String _ownerId; - Metadata _metadata; - bool _isArchived; - DateTime _overrideDateTime; + DateTime? _lastModified; + int? _contentLength; + String? _contentType; + String? _etag; + int? _usedBytes; + bool? _isCollection; + bool? _hasPreview; + int? _fileId; + String? _ownerId; + Metadata? _metadata; + bool? _isArchived; + DateTime? _overrideDateTime; } extension on XmlElement { bool matchQualifiedName( String local, { - String prefix, - Map namespaces, + required String prefix, + required Map namespaces, }) { final localNamespaces = {}; for (final a in attributes) { diff --git a/lib/event/event.dart b/lib/event/event.dart index 36b9dd3c..a36f7518 100644 --- a/lib/event/event.dart +++ b/lib/event/event.dart @@ -23,13 +23,13 @@ class AppEventListener { _log.warning("[endListenEvent] Already not listening"); return; } - _subscription.cancel(); + _subscription?.cancel(); _subscription = null; } final void Function(T) _listener; final _stream = KiwiContainer().resolve().on(); - StreamSubscription _subscription; + StreamSubscription? _subscription; final _log = Logger("event.event.AppEventListener<${T.runtimeType}>"); } diff --git a/lib/exception.dart b/lib/exception.dart index b6e9b113..742b95d1 100644 --- a/lib/exception.dart +++ b/lib/exception.dart @@ -16,7 +16,10 @@ class CacheNotFoundException implements Exception { } class ApiException implements Exception { - ApiException({this.response, this.message}); + ApiException({ + required this.response, + this.message, + }); @override toString() { diff --git a/lib/exception_util.dart b/lib/exception_util.dart index e074cc90..755eafe9 100644 --- a/lib/exception_util.dart +++ b/lib/exception_util.dart @@ -10,16 +10,16 @@ import 'package:nc_photos/exception.dart'; String toUserString(dynamic exception, BuildContext context) { if (exception is ApiException) { if (exception.response.statusCode == 401) { - return AppLocalizations.of(context).errorUnauthenticated; + return AppLocalizations.of(context)!.errorUnauthenticated; } else if (exception.response.statusCode == 423) { - return AppLocalizations.of(context).errorLocked; + return AppLocalizations.of(context)!.errorLocked; } else if (exception.response.statusCode == 500) { - return AppLocalizations.of(context).errorServerError; + return AppLocalizations.of(context)!.errorServerError; } } else if (exception is SocketException) { - return AppLocalizations.of(context).errorDisconnected; + return AppLocalizations.of(context)!.errorDisconnected; } else if (exception is InvalidBaseUrlException) { - return AppLocalizations.of(context).errorInvalidBaseUrl; + return AppLocalizations.of(context)!.errorInvalidBaseUrl; } return exception.toString(); } diff --git a/lib/iterable_extension.dart b/lib/iterable_extension.dart index f6b0c91e..3632dc16 100644 --- a/lib/iterable_extension.dart +++ b/lib/iterable_extension.dart @@ -3,10 +3,10 @@ import 'package:tuple/tuple.dart'; extension IterableExtension on Iterable { /// Return a new sorted list - List sorted([int compare(T a, T b)]) => this.toList()..sort(compare); + List sorted([int compare(T a, T b)?]) => this.toList()..sort(compare); /// Return a new stable sorted list - List stableSorted([int compare(T a, T b)]) { + List stableSorted([int compare(T a, T b)?]) { final tmp = this.toList(); mergeSort(tmp, compare: compare); return tmp; diff --git a/lib/language_util.dart b/lib/language_util.dart index 89a0730f..185d8e8e 100644 --- a/lib/language_util.dart +++ b/lib/language_util.dart @@ -6,12 +6,12 @@ class AppLanguage { final int langId; final String nativeName; - final Locale locale; + final Locale? locale; } String getSelectedLanguageName(BuildContext context) => _getSelectedLanguage(context).nativeName; -Locale getSelectedLocale(BuildContext context) => +Locale? getSelectedLocale(BuildContext context) => _getSelectedLanguage(context).locale; final supportedLanguages = { @@ -32,9 +32,9 @@ enum _AppLanguageEnum { AppLanguage _getSelectedLanguage(BuildContext context) { try { - final lang = Pref.inst().getLanguage(); - return supportedLanguages[lang]; + final lang = Pref.inst().getLanguageOr(0); + return supportedLanguages[lang]!; } catch (_) { - return supportedLanguages[_AppLanguageEnum.systemDefault.index]; + return supportedLanguages[_AppLanguageEnum.systemDefault.index]!; } } diff --git a/lib/main.dart b/lib/main.dart index f12a189b..14f832ed 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -72,8 +72,8 @@ void _initLog() { Future _initPref() async { await Pref.init(); - if (Pref.inst().getLastVersion(null) == null) { - if (Pref.inst().getSetupProgress(null) == null) { + if (Pref.inst().getLastVersion() == null) { + if (Pref.inst().getSetupProgress() == null) { // new install await Pref.inst().setLastVersion(k.version); } else { diff --git a/lib/metadata_task_manager.dart b/lib/metadata_task_manager.dart index 2068f065..09ae9771 100644 --- a/lib/metadata_task_manager.dart +++ b/lib/metadata_task_manager.dart @@ -25,7 +25,7 @@ class MetadataTask { final op = UpdateMissingMetadata(fileRepo); await for (final _ in op(account, File(path: "${api_util.getWebdavRootUrlRelative(account)}/$r"))) { - if (!Pref.inst().isEnableExif()) { + if (!Pref.inst().isEnableExifOr()) { _log.info("[call] EXIF disabled, task ending immaturely"); op.stop(); return; @@ -53,7 +53,7 @@ class MetadataTaskManager { void _handleStream() async { await for (final task in _streamController.stream) { - if (Pref.inst().isEnableExif()) { + if (Pref.inst().isEnableExifOr()) { _log.info("[_doTask] Executing task: $task"); await task(); } else { diff --git a/lib/mobile/android/media_store.dart b/lib/mobile/android/media_store.dart index 6730b112..891357ba 100644 --- a/lib/mobile/android/media_store.dart +++ b/lib/mobile/android/media_store.dart @@ -6,11 +6,13 @@ class MediaStore { static const exceptionCodePermissionError = "permissionError"; static Future saveFileToDownload( - String fileName, Uint8List fileContent) => - _channel.invokeMethod("saveFileToDownload", { - "fileName": fileName, - "content": fileContent, - }); + String fileName, Uint8List fileContent) async { + return (await _channel + .invokeMethod("saveFileToDownload", { + "fileName": fileName, + "content": fileContent, + }))!; + } static const _channel = const MethodChannel("com.nkming.nc_photos/media_store"); diff --git a/lib/mobile/android/notification.dart b/lib/mobile/android/notification.dart index a7c419b7..d369a6be 100644 --- a/lib/mobile/android/notification.dart +++ b/lib/mobile/android/notification.dart @@ -2,7 +2,7 @@ import 'package:flutter/services.dart'; class Notification { static Future notifyItemsDownloadSuccessful( - List fileUris, List mimeTypes) => + List fileUris, List mimeTypes) => _channel.invokeMethod("notifyItemsDownloadSuccessful", { "fileUris": fileUris, "mimeTypes": mimeTypes, diff --git a/lib/mobile/android/share.dart b/lib/mobile/android/share.dart index 013bab30..a9ba4711 100644 --- a/lib/mobile/android/share.dart +++ b/lib/mobile/android/share.dart @@ -2,7 +2,7 @@ import 'package:flutter/services.dart'; class Share { static Future shareItems( - List fileUris, List mimeTypes) => + List fileUris, List mimeTypes) => _channel.invokeMethod("shareItems", { "fileUris": fileUris, "mimeTypes": mimeTypes, diff --git a/lib/mobile/map_widget.dart b/lib/mobile/map_widget.dart index be935b06..7fc818d3 100644 --- a/lib/mobile/map_widget.dart +++ b/lib/mobile/map_widget.dart @@ -4,9 +4,9 @@ import 'package:tuple/tuple.dart'; class Map extends StatelessWidget { const Map({ - Key key, - this.center, - this.zoom, + Key? key, + required this.center, + required this.zoom, this.onTap, }) : super(key: key); @@ -44,5 +44,5 @@ class Map extends StatelessWidget { /// A pair of latitude and longitude coordinates, stored as degrees final Tuple2 center; final double zoom; - final void Function() onTap; + final VoidCallback? onTap; } diff --git a/lib/mobile/notification.dart b/lib/mobile/notification.dart index 9a1ec09e..1cbfd93c 100644 --- a/lib/mobile/notification.dart +++ b/lib/mobile/notification.dart @@ -11,5 +11,5 @@ class AndroidItemDownloadSuccessfulNotification } final List fileUris; - final List mimeTypes; + final List mimeTypes; } diff --git a/lib/mobile/self_signed_cert_manager.dart b/lib/mobile/self_signed_cert_manager.dart index d86d24ce..d4046f74 100644 --- a/lib/mobile/self_signed_cert_manager.dart +++ b/lib/mobile/self_signed_cert_manager.dart @@ -103,7 +103,7 @@ class SelfSignedCertManager { } } - _BadCertInfo _latestBadCert; + late _BadCertInfo _latestBadCert; var _whitelist = <_CertInfo>[]; static SelfSignedCertManager _inst = SelfSignedCertManager._(); @@ -168,7 +168,7 @@ class _BadCertInfo { class _CustomHttpOverrides extends HttpOverrides { @override - HttpClient createHttpClient(SecurityContext context) { + HttpClient createHttpClient(SecurityContext? context) { return super.createHttpClient(context) ..badCertificateCallback = (cert, host, port) { try { diff --git a/lib/mobile/share.dart b/lib/mobile/share.dart index 4c15bc01..60b9313b 100644 --- a/lib/mobile/share.dart +++ b/lib/mobile/share.dart @@ -10,5 +10,5 @@ class AndroidShare extends itf.Share { } final List fileUris; - final List mimeTypes; + final List mimeTypes; } diff --git a/lib/or_null.dart b/lib/or_null.dart index faef3c7d..2f1d4d4b 100644 --- a/lib/or_null.dart +++ b/lib/or_null.dart @@ -2,7 +2,9 @@ class OrNull { OrNull(this.obj); - static bool isNull(OrNull x) => x != null && x.obj == null; + /// Return iff the value of [x] is set to null, which means if [x] itself is + /// null, false will still be returned + static bool isSetNull(OrNull? x) => x != null && x.obj == null; - final T obj; + final T? obj; } diff --git a/lib/platform/universal_storage.dart b/lib/platform/universal_storage.dart index 0cb8dec1..b40b7922 100644 --- a/lib/platform/universal_storage.dart +++ b/lib/platform/universal_storage.dart @@ -9,13 +9,13 @@ abstract class UniversalStorage { /// Return the content associated with [name], or null if no such association /// exists - Future getBinary(String name); + Future getBinary(String name); Future putString(String name, String content); /// Return the string associated with [name], or null if no such association /// exists - Future getString(String name); + Future getString(String name); Future remove(String name); } diff --git a/lib/pref.dart b/lib/pref.dart index 30706ee3..72f8942b 100644 --- a/lib/pref.dart +++ b/lib/pref.dart @@ -12,67 +12,65 @@ class Pref { factory Pref.inst() => _inst; - List getAccounts([List def]) { + List? getAccounts() { final jsonObjs = _pref.getStringList("accounts"); - return jsonObjs?.map((e) => Account.fromJson(jsonDecode(e)))?.toList() ?? - def; + return jsonObjs?.map((e) => Account.fromJson(jsonDecode(e))).toList(); } + List getAccountsOr(List def) => getAccounts() ?? def; Future setAccounts(List value) { final jsons = value.map((e) => jsonEncode(e.toJson())).toList(); return _pref.setStringList("accounts", jsons); } - int getCurrentAccountIndex([int def]) => - _pref.getInt("currentAccountIndex") ?? def; - + int? getCurrentAccountIndex() => _pref.getInt("currentAccountIndex"); + int getCurrentAccountIndexOr(int def) => getCurrentAccountIndex() ?? def; Future setCurrentAccountIndex(int value) => _pref.setInt("currentAccountIndex", value); - int getHomePhotosZoomLevel([int def]) => - _pref.getInt("homePhotosZoomLevel") ?? def; - + int? getHomePhotosZoomLevel() => _pref.getInt("homePhotosZoomLevel"); + int getHomePhotosZoomLevelOr(int def) => getHomePhotosZoomLevel() ?? def; Future setHomePhotosZoomLevel(int value) => _pref.setInt("homePhotosZoomLevel", value); - int getAlbumViewerZoomLevel([int def]) => - _pref.getInt("albumViewerZoomLevel") ?? def; - + int? getAlbumViewerZoomLevel() => _pref.getInt("albumViewerZoomLevel"); + int getAlbumViewerZoomLevelOr(int def) => getAlbumViewerZoomLevel() ?? def; Future setAlbumViewerZoomLevel(int value) => _pref.setInt("albumViewerZoomLevel", value); - bool isEnableExif([bool def = true]) => _pref.getBool("isEnableExif") ?? def; - + bool? isEnableExif() => _pref.getBool("isEnableExif"); + bool isEnableExifOr([bool def = true]) => isEnableExif() ?? def; Future setEnableExif(bool value) => _pref.setBool("isEnableExif", value); - int getSetupProgress([int def = 0]) => _pref.getInt("setupProgress") ?? def; - + int? getSetupProgress() => _pref.getInt("setupProgress"); + int getSetupProgressOr([int def = 0]) => getSetupProgress() ?? def; Future setSetupProgress(int value) => _pref.setInt("setupProgress", value); /// Return the version number when the app last ran - int getLastVersion([int def = 0]) => _pref.getInt("lastVersion") ?? def; - + int? getLastVersion() => _pref.getInt("lastVersion"); + int getLastVersionOr(int def) => getLastVersion() ?? def; Future setLastVersion(int value) => _pref.setInt("lastVersion", value); - bool isDarkTheme([bool def = false]) => _pref.getBool("isDarkTheme") ?? def; - + bool? isDarkTheme() => _pref.getBool("isDarkTheme"); + bool isDarkThemeOr(bool def) => isDarkTheme() ?? def; Future setDarkTheme(bool value) => _pref.setBool("isDarkTheme", value); - int getLanguage([int def = 0]) => _pref.getInt("language") ?? def; + int? getLanguage() => _pref.getInt("language"); + int getLanguageOr(int def) => getLanguage() ?? def; Future setLanguage(int value) => _pref.setInt("language", value); Pref._(); static final _inst = Pref._(); - SharedPreferences _pref; + late SharedPreferences _pref; } extension PrefExtension on Pref { - Account getCurrentAccount() { + Account? getCurrentAccount() { try { - return Pref.inst().getAccounts()[Pref.inst().getCurrentAccountIndex()]; + return Pref.inst().getAccounts()![Pref.inst().getCurrentAccountIndex()!]; } catch (_) { return null; } diff --git a/lib/share_handler.dart b/lib/share_handler.dart index 6e3bad63..54fa1483 100644 --- a/lib/share_handler.dart +++ b/lib/share_handler.dart @@ -23,7 +23,7 @@ class ShareHandler { showDialog( context: context, builder: (context) => ProcessingDialog( - text: AppLocalizations.of(context).shareDownloadingDialogContent), + text: AppLocalizations.of(context)!.shareDownloadingDialogContent), ); final results = >[]; for (final f in files) { @@ -33,7 +33,7 @@ class ShareHandler { } on PermissionException catch (_) { _log.warning("[shareFiles] Permission not granted"); SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .downloadFailureNoPermissionNotification), duration: k.snackBarDurationNormal, )); diff --git a/lib/snack_bar_manager.dart b/lib/snack_bar_manager.dart index 8e7790b5..cd844b31 100644 --- a/lib/snack_bar_manager.dart +++ b/lib/snack_bar_manager.dart @@ -22,7 +22,7 @@ class SnackBarManager { /// Show a snack bar if possible /// /// If the snack bar can't be shown at this time, return null - ScaffoldFeatureController showSnackBar( + ScaffoldFeatureController? showSnackBar( SnackBar snackBar) { for (final h in _handlers.reversed) { final result = h.showSnackBar(snackBar); @@ -42,6 +42,6 @@ class SnackBarManager { } abstract class SnackBarHandler { - ScaffoldFeatureController showSnackBar( + ScaffoldFeatureController? showSnackBar( SnackBar snackBar); } diff --git a/lib/theme.dart b/lib/theme.dart index 2a0ecd96..d294a6c9 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class AppTheme extends StatelessWidget { - const AppTheme({@required this.child}); + const AppTheme({ + required this.child, + }); @override Widget build(BuildContext context) { @@ -46,24 +48,24 @@ class AppTheme extends StatelessWidget { static Color getSelectionOverlayColor(BuildContext context) { return Theme.of(context).brightness == Brightness.light - ? primarySwatchLight[100].withOpacity(0.7) - : primarySwatchDark[700].withOpacity(0.7); + ? primarySwatchLight[100]!.withOpacity(0.7) + : primarySwatchDark[700]!.withOpacity(0.7); } static Color getSelectionCheckColor(BuildContext context) { return Theme.of(context).brightness == Brightness.light - ? Colors.grey[800] - : Colors.grey[350]; + ? Colors.grey[800]! + : Colors.grey[350]!; } static Color getOverscrollIndicatorColor(BuildContext context) { return Theme.of(context).brightness == Brightness.light - ? Colors.grey[800] - : Colors.grey[200]; + ? Colors.grey[800]! + : Colors.grey[200]!; } static Color getRootPickerContentBoxColor(BuildContext context) { - return Colors.blue[200]; + return Colors.blue[200]!; } static Color getPrimaryTextColor(BuildContext context) { diff --git a/lib/touch_token_manager.dart b/lib/touch_token_manager.dart index e6400f35..c5915874 100644 --- a/lib/touch_token_manager.dart +++ b/lib/touch_token_manager.dart @@ -21,7 +21,7 @@ import 'package:nc_photos/use_case/remove.dart'; /// doing it on every query class TouchTokenManager { Future setRemoteToken( - FileRepo fileRepo, Account account, File file, String token) async { + FileRepo fileRepo, Account account, File file, String? token) async { _log.info( "[setRemoteToken] Set remote token for file '${file.path}': $token"); final path = _getRemotePath(account, file); @@ -36,7 +36,7 @@ class TouchTokenManager { /// Return the touch token for [file] from remote source, or null if no such /// file - Future getRemoteToken( + Future getRemoteToken( FileRepo fileRepo, Account account, File file) async { final path = _getRemotePath(account, file); try { @@ -51,7 +51,7 @@ class TouchTokenManager { } } - Future setLocalToken(Account account, File file, String token) { + Future setLocalToken(Account account, File file, String? token) { _log.info( "[setLocalToken] Set local token for file '${file.path}': $token"); final name = _getLocalStorageName(account, file); @@ -62,7 +62,7 @@ class TouchTokenManager { } } - Future getLocalToken(Account account, File file) async { + Future getLocalToken(Account account, File file) async { final name = _getLocalStorageName(account, file); return platform.UniversalStorage().getString(name); } diff --git a/lib/use_case/load_metadata.dart b/lib/use_case/load_metadata.dart index 02009bda..63744e2f 100644 --- a/lib/use_case/load_metadata.dart +++ b/lib/use_case/load_metadata.dart @@ -22,9 +22,9 @@ class LoadMetadata { } Future> _loadMetadata({ - @required File file, - exifdart.AbstractBlobReader Function() exifdartReaderBuilder, - AsyncImageInput Function() imageSizeGetterInputBuilder, + required File file, + required exifdart.AbstractBlobReader Function() exifdartReaderBuilder, + required AsyncImageInput Function() imageSizeGetterInputBuilder, }) async { var metadata = exifdart.Metadata(); if (file_util.isMetadataSupportedFormat(file)) { @@ -47,8 +47,8 @@ class LoadMetadata { await AsyncImageSizeGetter.getSize(imageSizeGetterInputBuilder()); // image size getter doesn't handle exif orientation if (metadata.exif?.containsKey("Orientation") == true && - metadata.exif["Orientation"] >= 5 && - metadata.exif["Orientation"] <= 8) { + metadata.exif!["Orientation"] >= 5 && + metadata.exif!["Orientation"] <= 8) { // 90 deg CW/CCW imageWidth = resolution.height; imageHeight = resolution.width; @@ -66,12 +66,12 @@ class LoadMetadata { } } else { if (metadata.rotateAngleCcw != null && - metadata.rotateAngleCcw % 180 != 0) { - imageWidth = metadata.imageHeight; - imageHeight = metadata.imageWidth; + metadata.rotateAngleCcw! % 180 != 0) { + imageWidth = metadata.imageHeight!; + imageHeight = metadata.imageWidth!; } else { - imageWidth = metadata.imageWidth; - imageHeight = metadata.imageHeight; + imageWidth = metadata.imageWidth!; + imageHeight = metadata.imageHeight!; } } diff --git a/lib/use_case/remove.dart b/lib/use_case/remove.dart index b9ad86e8..e94ab034 100644 --- a/lib/use_case/remove.dart +++ b/lib/use_case/remove.dart @@ -24,6 +24,7 @@ class Remove { } Future _cleanUpAlbums(Account account, File file) async { + final albumRepo = this.albumRepo!; final albums = (await ListAlbum(fileRepo, albumRepo)(account) .where((event) => event is Album) .toList()).cast(); @@ -58,7 +59,7 @@ class Remove { } final FileRepo fileRepo; - final AlbumRepo albumRepo; + final AlbumRepo? albumRepo; static final _log = Logger("use_case.remove.Remove"); } diff --git a/lib/use_case/resync_album.dart b/lib/use_case/resync_album.dart index fa97c570..8b830bbc 100644 --- a/lib/use_case/resync_album.dart +++ b/lib/use_case/resync_album.dart @@ -44,18 +44,22 @@ class ResyncAlbum { Future _syncOne(Account account, AlbumFileItem item, ObjectStore objStore, Index index) async { - Map dbItem; + Map? dbItem; if (item.file.fileId != null) { final List dbItems = await index .getAll(AppDbFileDbEntry.toNamespacedFileId(account, item.file)); // find the one owned by us - dbItem = dbItems.firstWhere((element) { - final e = AppDbFileDbEntry.fromJson(element.cast()); - return file_util.getUserDirName(e.file) == account.username; - }, orElse: () => null); + try { + dbItem = dbItems.firstWhere((element) { + final e = AppDbFileDbEntry.fromJson(element.cast()); + return file_util.getUserDirName(e.file) == account.username; + }); + } on StateError catch (_) { + // not found + } } else { dbItem = await objStore - .getObject(AppDbFileDbEntry.toPrimaryKey(account, item.file)); + .getObject(AppDbFileDbEntry.toPrimaryKey(account, item.file)) as Map; } if (dbItem == null) { _log.warning( diff --git a/lib/use_case/update_dynamic_album_cover.dart b/lib/use_case/update_dynamic_album_cover.dart index 3dfe89d0..07a51cec 100644 --- a/lib/use_case/update_dynamic_album_cover.dart +++ b/lib/use_case/update_dynamic_album_cover.dart @@ -40,8 +40,9 @@ class UpdateDynamicAlbumCover { } Album _updateWithSortedFiles(Album album, List sortedFiles) { - final coverFile = sortedFiles.firstWhere((element) => element.hasPreview); - if (coverFile != null) { + try { + final coverFile = + sortedFiles.firstWhere((element) => element.hasPreview ?? false); // cache the result for later use if (coverFile.path != (album.coverProvider as AlbumAutoCoverProvider).coverFile?.path) { @@ -51,6 +52,8 @@ class UpdateDynamicAlbumCover { ), ); } + } on StateError catch (_) { + // no files } return album; } diff --git a/lib/use_case/update_dynamic_album_time.dart b/lib/use_case/update_dynamic_album_time.dart index fc8371b5..da7c1ca2 100644 --- a/lib/use_case/update_dynamic_album_time.dart +++ b/lib/use_case/update_dynamic_album_time.dart @@ -37,7 +37,7 @@ class UpdateDynamicAlbumTime { } Album _updateWithSortedFiles(Album album, List sortedFiles) { - DateTime latestItemTime; + DateTime? latestItemTime; try { latestItemTime = sortedFiles.first.bestDateTime; } catch (_) { diff --git a/lib/use_case/update_missing_metadata.dart b/lib/use_case/update_missing_metadata.dart index 493e124a..d628b39d 100644 --- a/lib/use_case/update_missing_metadata.dart +++ b/lib/use_case/update_missing_metadata.dart @@ -40,8 +40,8 @@ class UpdateMissingMetadata { _log.fine("[call] Updating metadata for ${file.path}"); final binary = await GetFileBinary(fileRepo)(account, file); final metadata = await LoadMetadata()(account, file, binary); - int imageWidth, imageHeight; - Exif exif; + int? imageWidth, imageHeight; + Exif? exif; if (metadata.containsKey("resolution")) { imageWidth = metadata["resolution"]["width"]; imageHeight = metadata["resolution"]["height"]; diff --git a/lib/use_case/update_property.dart b/lib/use_case/update_property.dart index f86469ad..46555bfe 100644 --- a/lib/use_case/update_property.dart +++ b/lib/use_case/update_property.dart @@ -12,9 +12,9 @@ class UpdateProperty { Future call( Account account, File file, { - OrNull metadata, - OrNull isArchived, - OrNull overrideDateTime, + OrNull? metadata, + OrNull? isArchived, + OrNull? overrideDateTime, }) async { if (metadata == null && isArchived == null && overrideDateTime == null) { // ? @@ -22,9 +22,9 @@ class UpdateProperty { return; } - if (metadata?.obj != null && metadata.obj.fileEtag != file.etag) { + if (metadata?.obj != null && metadata!.obj!.fileEtag != file.etag) { _log.warning( - "[call] Metadata fileEtag mismatch with actual file's (metadata: ${metadata.obj.fileEtag}, file: ${file.etag})"); + "[call] Metadata fileEtag mismatch with actual file's (metadata: ${metadata.obj!.fileEtag}, file: ${file.etag})"); } await fileRepo.updateProperty( account, diff --git a/lib/web/db_util.dart b/lib/web/db_util.dart index 4f51c308..dcd90c07 100644 --- a/lib/web/db_util.dart +++ b/lib/web/db_util.dart @@ -1,4 +1,4 @@ import 'package:idb_shim/idb_browser.dart'; import 'package:idb_shim/idb_shim.dart'; -IdbFactory getDbFactory() => getIdbFactory(); +IdbFactory getDbFactory() => idbFactoryBrowser; diff --git a/lib/web/map_widget.dart b/lib/web/map_widget.dart index 3c1df57b..1ffb9324 100644 --- a/lib/web/map_widget.dart +++ b/lib/web/map_widget.dart @@ -8,9 +8,9 @@ import 'package:tuple/tuple.dart'; class Map extends StatefulWidget { const Map({ - Key key, - this.center, - this.zoom, + Key? key, + required this.center, + required this.zoom, this.onTap, }) : super(key: key); @@ -20,7 +20,7 @@ class Map extends StatefulWidget { /// A pair of latitude and longitude coordinates, stored as degrees final Tuple2 center; final double zoom; - final void Function() onTap; + final void Function()? onTap; } class _MapState extends State { diff --git a/lib/widget/account_picker_dialog.dart b/lib/widget/account_picker_dialog.dart index 0d41e58b..aac329df 100644 --- a/lib/widget/account_picker_dialog.dart +++ b/lib/widget/account_picker_dialog.dart @@ -15,8 +15,8 @@ import 'package:nc_photos/widget/sign_in.dart'; /// A dialog that allows the user to switch between accounts class AccountPickerDialog extends StatefulWidget { AccountPickerDialog({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); @override @@ -29,7 +29,7 @@ class _AccountPickerDialogState extends State { @override initState() { super.initState(); - _accounts = Pref.inst().getAccounts([]); + _accounts = Pref.inst().getAccountsOr([]); } @override @@ -48,7 +48,7 @@ class _AccountPickerDialogState extends State { Icons.close, color: AppTheme.getSecondaryTextColor(context), ), - tooltip: AppLocalizations.of(context).deleteTooltip, + tooltip: AppLocalizations.of(context)!.deleteTooltip, onPressed: () => _onRemoveItemPressed(a), ), ), @@ -63,7 +63,7 @@ class _AccountPickerDialogState extends State { ..pushNamed(SignIn.routeName); }, child: Tooltip( - message: AppLocalizations.of(context).addServerTooltip, + message: AppLocalizations.of(context)!.addServerTooltip, child: Center( child: Icon( Icons.add, @@ -89,7 +89,7 @@ class _AccountPickerDialogState extends State { Icons.edit, color: AppTheme.getSecondaryTextColor(context), ), - tooltip: AppLocalizations.of(context).editTooltip, + tooltip: AppLocalizations.of(context)!.editTooltip, onPressed: () => _onEditPressed(), ), ), @@ -106,20 +106,28 @@ class _AccountPickerDialogState extends State { } void _onRemoveItemPressed(Account account) { - _removeAccount(account); - setState(() { - _accounts = Pref.inst().getAccounts([]); - }); - SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) - .removeServerSuccessNotification(account.url)), - duration: k.snackBarDurationNormal, - )); + try { + _removeAccount(account); + setState(() { + _accounts = Pref.inst().getAccounts()!; + }); + SnackBarManager().showSnackBar(SnackBar( + content: Text(AppLocalizations.of(context)! + .removeServerSuccessNotification(account.url)), + duration: k.snackBarDurationNormal, + )); + } catch (e) { + SnackBarManager().showSnackBar(SnackBar( + content: Text(exception_util.toUserString(e, context)), + duration: k.snackBarDurationNormal, + )); + } } void _onEditPressed() async { try { - final result = await Navigator.of(context).pushNamed(RootPicker.routeName, + final result = await Navigator.of(context).pushNamed( + RootPicker.routeName, arguments: RootPickerArguments(widget.account)); if (result != null) { // we've got a good account @@ -129,19 +137,19 @@ class _AccountPickerDialogState extends State { Navigator.of(context).pop(); return; } - final accounts = Pref.inst().getAccounts([]); + final accounts = Pref.inst().getAccounts()!; if (accounts.contains(result)) { // conflict with another account. This normally won't happen because // the app passwords are unique to each entry, but just in case Navigator.of(context).pop(); SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .editAccountConflictFailureNotification), duration: k.snackBarDurationNormal, )); return; } - accounts[Pref.inst().getCurrentAccountIndex()] = result; + accounts[Pref.inst().getCurrentAccountIndex()!] = result; Pref.inst()..setAccounts(accounts); Navigator.pushNamedAndRemoveUntil( context, Home.routeName, (route) => false, @@ -158,9 +166,9 @@ class _AccountPickerDialogState extends State { } void _removeAccount(Account account) { - final currentAccounts = Pref.inst().getAccounts([]); + final currentAccounts = Pref.inst().getAccounts()!; final currentAccount = - currentAccounts[Pref.inst().getCurrentAccountIndex()]; + currentAccounts[Pref.inst().getCurrentAccountIndex()!]; final newAccounts = currentAccounts.where((element) => element != account).toList(); final newAccountIndex = newAccounts.indexOf(currentAccount); @@ -172,7 +180,7 @@ class _AccountPickerDialogState extends State { ..setCurrentAccountIndex(newAccountIndex); } - List _accounts; + late List _accounts; static final _log = Logger("widget.account_picker_dialog._AccountPickerDialogState"); diff --git a/lib/widget/album_dir_picker.dart b/lib/widget/album_dir_picker.dart index f278f77f..b10de3de 100644 --- a/lib/widget/album_dir_picker.dart +++ b/lib/widget/album_dir_picker.dart @@ -21,12 +21,17 @@ class AlbumDirPickerArguments { class AlbumDirPicker extends StatefulWidget { static const routeName = "/album-dir-picker"; + static Route buildRoute(AlbumDirPickerArguments args) => + MaterialPageRoute>( + builder: (context) => AlbumDirPicker.fromArgs(args), + ); + AlbumDirPicker({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - AlbumDirPicker.fromArgs(AlbumDirPickerArguments args, {Key key}) + AlbumDirPicker.fromArgs(AlbumDirPickerArguments args, {Key? key}) : this( key: key, account: args.account, @@ -81,7 +86,7 @@ class _AlbumDirPickerState extends State child: Column( children: [ Text( - AppLocalizations.of(context).albumDirPickerHeaderText, + AppLocalizations.of(context)!.albumDirPickerHeaderText, style: Theme.of(context).textTheme.headline5, textAlign: TextAlign.center, ), @@ -89,7 +94,7 @@ class _AlbumDirPickerState extends State Align( alignment: AlignmentDirectional.topStart, child: Text( - AppLocalizations.of(context).albumDirPickerSubHeaderText, + AppLocalizations.of(context)!.albumDirPickerSubHeaderText, ), ), ], @@ -112,7 +117,7 @@ class _AlbumDirPickerState extends State ), ElevatedButton( onPressed: () => _onConfirmPressed(context), - child: Text(AppLocalizations.of(context).confirmButtonLabel), + child: Text(AppLocalizations.of(context)!.confirmButtonLabel), ), ], ), @@ -127,7 +132,7 @@ class _AlbumDirPickerState extends State if (picked.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).albumDirPickerListEmptyNotification), + AppLocalizations.of(context)!.albumDirPickerListEmptyNotification), duration: k.snackBarDurationNormal, )); } else { diff --git a/lib/widget/album_grid_item.dart b/lib/widget/album_grid_item.dart index 4758e74e..95065808 100644 --- a/lib/widget/album_grid_item.dart +++ b/lib/widget/album_grid_item.dart @@ -5,9 +5,9 @@ import 'package:nc_photos/widget/selectable.dart'; class AlbumGridItem extends StatelessWidget { AlbumGridItem({ - Key key, - @required this.cover, - @required this.title, + Key? key, + required this.cover, + required this.title, this.subtitle, this.subtitle2, this.icon, @@ -50,8 +50,8 @@ class AlbumGridItem extends StatelessWidget { ), const SizedBox(height: 8), Text( - title ?? "", - style: Theme.of(context).textTheme.bodyText1.copyWith( + title, + style: Theme.of(context).textTheme.bodyText1!.copyWith( color: AppTheme.getPrimaryTextColor(context), ), textAlign: TextAlign.start, @@ -64,7 +64,7 @@ class AlbumGridItem extends StatelessWidget { Expanded( child: Text( subtitle ?? "", - style: Theme.of(context).textTheme.bodyText2.copyWith( + style: Theme.of(context).textTheme.bodyText2!.copyWith( fontSize: 10, color: AppTheme.getSecondaryTextColor(context), ), @@ -75,8 +75,8 @@ class AlbumGridItem extends StatelessWidget { ), if (subtitle2?.isNotEmpty == true) Text( - subtitle2, - style: Theme.of(context).textTheme.bodyText2.copyWith( + subtitle2!, + style: Theme.of(context).textTheme.bodyText2!.copyWith( fontSize: 10, color: AppTheme.getSecondaryTextColor(context), ), @@ -93,12 +93,12 @@ class AlbumGridItem extends StatelessWidget { final Widget cover; final String title; - final String subtitle; + final String? subtitle; /// Appears after [subtitle], aligned to the end side of parent - final String subtitle2; - final IconData icon; + final String? subtitle2; + final IconData? icon; final bool isSelected; - final VoidCallback onTap; - final VoidCallback onLongPress; + final VoidCallback? onTap; + final VoidCallback? onLongPress; } diff --git a/lib/widget/album_importer.dart b/lib/widget/album_importer.dart index 2eec790f..08e9d749 100644 --- a/lib/widget/album_importer.dart +++ b/lib/widget/album_importer.dart @@ -34,12 +34,16 @@ class AlbumImporterArguments { class AlbumImporter extends StatefulWidget { static const routeName = "/album-importer"; + static Route buildRoute(AlbumImporterArguments args) => MaterialPageRoute( + builder: (context) => AlbumImporter.fromArgs(args), + ); + AlbumImporter({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - AlbumImporter.fromArgs(AlbumImporterArguments args, {Key key}) + AlbumImporter.fromArgs(AlbumImporterArguments args, {Key? key}) : this( key: key, account: args.account, @@ -98,7 +102,7 @@ class _AlbumImporterState extends State { child: Column( children: [ Text( - AppLocalizations.of(context).albumImporterHeaderText, + AppLocalizations.of(context)!.albumImporterHeaderText, style: Theme.of(context).textTheme.headline5, textAlign: TextAlign.center, ), @@ -106,7 +110,7 @@ class _AlbumImporterState extends State { Align( alignment: AlignmentDirectional.topStart, child: Text( - AppLocalizations.of(context).albumImporterSubHeaderText, + AppLocalizations.of(context)!.albumImporterSubHeaderText, ), ), ], @@ -141,7 +145,7 @@ class _AlbumImporterState extends State { ), ElevatedButton( onPressed: () => _onImportPressed(context), - child: Text(AppLocalizations.of(context).importButtonLabel), + child: Text(AppLocalizations.of(context)!.importButtonLabel), ), ], ), @@ -214,7 +218,7 @@ class _AlbumImporterState extends State { barrierDismissible: false, context: context, builder: (context) => ProcessingDialog( - text: AppLocalizations.of(context).albumImporterProgressText), + text: AppLocalizations.of(context)!.albumImporterProgressText), ); try { await _createAllAlbums(context); @@ -269,7 +273,7 @@ class _AlbumImporterState extends State { .toList(); } - ListImportableAlbumBloc _bloc; + late ListImportableAlbumBloc _bloc; var _backingFiles = []; final _picks = []; diff --git a/lib/widget/album_picker_dialog.dart b/lib/widget/album_picker_dialog.dart index e30a5c6a..b8069050 100644 --- a/lib/widget/album_picker_dialog.dart +++ b/lib/widget/album_picker_dialog.dart @@ -18,8 +18,8 @@ import 'package:tuple/tuple.dart'; class AlbumPickerDialog extends StatefulWidget { AlbumPickerDialog({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); @override @@ -68,7 +68,7 @@ class _AlbumPickerDialogState extends State { _reqQuery(); } else { // process the current state - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { setState(() { _onStateChange(context, _bloc.state); }); @@ -81,7 +81,7 @@ class _AlbumPickerDialogState extends State { SimpleDialogOption( onPressed: () => _onNewAlbumPressed(context), child: Tooltip( - message: AppLocalizations.of(context).createAlbumTooltip, + message: AppLocalizations.of(context)!.createAlbumTooltip, child: Center( child: Icon( Icons.add, @@ -170,7 +170,7 @@ class _AlbumPickerDialogState extends State { _bloc.add(ListAlbumBlocQuery(widget.account)); } - ListAlbumBloc _bloc; + late ListAlbumBloc _bloc; final _items = []; diff --git a/lib/widget/album_search_delegate.dart b/lib/widget/album_search_delegate.dart index 7a5e418e..13fa7ae7 100644 --- a/lib/widget/album_search_delegate.dart +++ b/lib/widget/album_search_delegate.dart @@ -18,7 +18,7 @@ class AlbumSearchDelegate extends SearchDelegate { AlbumSearchDelegate(BuildContext context, this.account) : super( searchFieldLabel: - AppLocalizations.of(context).albumSearchTextFieldHint, + AppLocalizations.of(context)!.albumSearchTextFieldHint, ) { final fileRepo = FileRepo(FileCachedDataSource()); final albumRepo = AlbumRepo(AlbumCachedDataSource()); @@ -38,7 +38,7 @@ class AlbumSearchDelegate extends SearchDelegate { return [ IconButton( icon: Icon(Icons.clear), - tooltip: AppLocalizations.of(context).clearTooltip, + tooltip: AppLocalizations.of(context)!.clearTooltip, onPressed: () { query = ""; }, @@ -88,7 +88,7 @@ class AlbumSearchDelegate extends SearchDelegate { ), const SizedBox(height: 8), Text( - AppLocalizations.of(context).listNoResultsText, + AppLocalizations.of(context)!.listNoResultsText, style: const TextStyle(fontSize: 24), ), ], diff --git a/lib/widget/album_viewer.dart b/lib/widget/album_viewer.dart index 58228a4b..1060a057 100644 --- a/lib/widget/album_viewer.dart +++ b/lib/widget/album_viewer.dart @@ -42,13 +42,17 @@ class AlbumViewerArguments { class AlbumViewer extends StatefulWidget { static const routeName = "/album-viewer"; + static Route buildRoute(AlbumViewerArguments args) => MaterialPageRoute( + builder: (context) => AlbumViewer.fromArgs(args), + ); + AlbumViewer({ - Key key, - @required this.account, - @required this.album, + Key? key, + required this.account, + required this.album, }) : super(key: key); - AlbumViewer.fromArgs(AlbumViewerArguments args, {Key key}) + AlbumViewer.fromArgs(AlbumViewerArguments args, {Key? key}) : this( key: key, account: args.account, @@ -96,7 +100,7 @@ class _AlbumViewerState extends State @override enterEditMode() { super.enterEditMode(); - _editAlbum = _album.copyWith(); + _editAlbum = _album!.copyWith(); setState(() { _transformItems(); }); @@ -104,7 +108,7 @@ class _AlbumViewerState extends State if (!SessionStorage().hasShowDragRearrangeNotification) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).albumEditDragRearrangeNotification), + AppLocalizations.of(context)!.albumEditDragRearrangeNotification), duration: k.snackBarDurationNormal, )); SessionStorage().hasShowDragRearrangeNotification = true; @@ -112,15 +116,15 @@ class _AlbumViewerState extends State } @override - validateEditMode() => _editFormKey?.currentState?.validate() == true; + validateEditMode() => _editFormKey.currentState?.validate() == true; @override doneEditMode() { try { // persist the changes - _editFormKey.currentState.save(); - final newAlbum = makeEdited(_editAlbum); - if (newAlbum.copyWith(lastUpdated: OrNull(_album.lastUpdated)) != + _editFormKey.currentState!.save(); + final newAlbum = makeEdited(_editAlbum!); + if (newAlbum.copyWith(lastUpdated: OrNull(_album!.lastUpdated)) != _album) { _log.info("[doneEditMode] Album modified: $newAlbum"); final albumRepo = AlbumRepo(AlbumCachedDataSource()); @@ -217,7 +221,7 @@ class _AlbumViewerState extends State } else if (isSelectionMode) { return _buildSelectionAppBar(context); } else { - return buildNormalAppBar(context, widget.account, _album); + return buildNormalAppBar(context, widget.account, _album!); } } @@ -226,14 +230,14 @@ class _AlbumViewerState extends State if (platform_k.isAndroid) IconButton( icon: const Icon(Icons.share), - tooltip: AppLocalizations.of(context).shareSelectedTooltip, + tooltip: AppLocalizations.of(context)!.shareSelectedTooltip, onPressed: () { _onSelectionAppBarSharePressed(context); }, ), IconButton( icon: const Icon(Icons.remove), - tooltip: AppLocalizations.of(context).removeSelectedFromAlbumTooltip, + tooltip: AppLocalizations.of(context)!.removeSelectedFromAlbumTooltip, onPressed: () { _onSelectionAppBarRemovePressed(); }, @@ -245,12 +249,12 @@ class _AlbumViewerState extends State return buildEditAppBar(context, widget.account, widget.album, actions: [ IconButton( icon: Icon(Icons.text_fields), - tooltip: AppLocalizations.of(context).albumAddTextTooltip, + tooltip: AppLocalizations.of(context)!.albumAddTextTooltip, onPressed: _onEditAppBarAddTextPressed, ), IconButton( icon: Icon(Icons.sort_by_alpha), - tooltip: AppLocalizations.of(context).sortTooltip, + tooltip: AppLocalizations.of(context)!.sortTooltip, onPressed: _onEditAppBarSortPressed, ), ]); @@ -279,7 +283,7 @@ class _AlbumViewerState extends State if (selected.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: - Text(AppLocalizations.of(context).shareSelectedEmptyNotification), + Text(AppLocalizations.of(context)!.shareSelectedEmptyNotification), duration: k.snackBarDurationNormal, )); return; @@ -301,14 +305,14 @@ class _AlbumViewerState extends State .toList(); final albumRepo = AlbumRepo(AlbumCachedDataSource()); - final newAlbum = _album.copyWith( + final newAlbum = _album!.copyWith( provider: AlbumStaticProvider( items: newItems, ), ); UpdateAlbum(albumRepo)(widget.account, newAlbum).then((_) { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .removeSelectedFromAlbumSuccessNotification( selectedIndexes.length)), duration: k.snackBarDurationNormal, @@ -323,7 +327,7 @@ class _AlbumViewerState extends State stacktrace); SnackBarManager().showSnackBar(SnackBar( content: Text( - "${AppLocalizations.of(context).removeSelectedFromAlbumFailureNotification}: " + "${AppLocalizations.of(context)!.removeSelectedFromAlbumFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -334,14 +338,14 @@ class _AlbumViewerState extends State } void _onEditAppBarSortPressed() { - final sortProvider = _editAlbum.sortProvider; + final sortProvider = _editAlbum!.sortProvider; showDialog( context: context, builder: (context) => FancyOptionPicker( - title: AppLocalizations.of(context).sortOptionDialogTitle, + title: AppLocalizations.of(context)!.sortOptionDialogTitle, items: [ FancyOptionPickerItem( - label: AppLocalizations.of(context).sortOptionTimeAscendingLabel, + label: AppLocalizations.of(context)!.sortOptionTimeAscendingLabel, isSelected: sortProvider is AlbumTimeSortProvider && sortProvider.isAscending, onSelect: () { @@ -350,7 +354,7 @@ class _AlbumViewerState extends State }, ), FancyOptionPickerItem( - label: AppLocalizations.of(context).sortOptionTimeDescendingLabel, + label: AppLocalizations.of(context)!.sortOptionTimeDescendingLabel, isSelected: sortProvider is AlbumTimeSortProvider && !sortProvider.isAscending, onSelect: () { @@ -364,7 +368,7 @@ class _AlbumViewerState extends State } void _onSortOldestPressed() { - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( sortProvider: AlbumTimeSortProvider(isAscending: true), ); setState(() { @@ -373,7 +377,7 @@ class _AlbumViewerState extends State } void _onSortNewestPressed() { - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( sortProvider: AlbumTimeSortProvider(isAscending: false), ); setState(() { @@ -431,7 +435,7 @@ class _AlbumViewerState extends State final newIndex = toIndex + (isBefore ? 0 : 1) + (fromIndex < toIndex ? -1 : 0); _sortedItems.insert(newIndex, item); - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( sortProvider: AlbumNullSortProvider(), // save the current order provider: AlbumStaticProvider( @@ -444,14 +448,14 @@ class _AlbumViewerState extends State } void _onEditAppBarAddTextPressed() { - showDialog( + showDialog( context: context, builder: (context) => SimpleInputDialog(), ).then((value) { if (value == null) { return; } - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( provider: AlbumStaticProvider( items: [ AlbumLabelItem(text: value), @@ -466,7 +470,7 @@ class _AlbumViewerState extends State } void _onLabelItemEditPressed(AlbumLabelItem item, int index) { - showDialog( + showDialog( context: context, builder: (context) => SimpleInputDialog( initialText: item.text, @@ -476,7 +480,7 @@ class _AlbumViewerState extends State return; } _sortedItems[index] = AlbumLabelItem(text: value); - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( provider: AlbumStaticProvider( items: _sortedItems, ), @@ -490,9 +494,10 @@ class _AlbumViewerState extends State void _transformItems() { if (_editAlbum != null) { // edit mode - _sortedItems = _editAlbum.sortProvider.sort(_getAlbumItemsOf(_editAlbum)); + _sortedItems = + _editAlbum!.sortProvider.sort(_getAlbumItemsOf(_editAlbum!)); } else { - _sortedItems = _album.sortProvider.sort(_getAlbumItemsOf(_album)); + _sortedItems = _album!.sortProvider.sort(_getAlbumItemsOf(_album!)); } _backingFiles = _sortedItems .whereType() @@ -644,29 +649,29 @@ class _AlbumViewerState extends State static List _getAlbumItemsOf(Album a) => AlbumStaticProvider.of(a).items; - Album _album; + Album? _album; var _sortedItems = []; var _backingFiles = []; final _scrollController = ScrollController(); - double _itemListMaxExtent; + double? _itemListMaxExtent; bool _isDragging = false; // == null if not drag scrolling - bool _isDragScrollingDown; + bool? _isDragScrollingDown; final _editFormKey = GlobalKey(); - Album _editAlbum; + Album? _editAlbum; static final _log = Logger("widget.album_viewer._AlbumViewerState"); } abstract class _ListItem implements SelectableItem, DraggableItem { _ListItem({ - @required this.index, - VoidCallback onTap, - DragTargetAccept onDropBefore, - DragTargetAccept onDropAfter, - VoidCallback onDragStarted, - VoidCallback onDragEndedAny, + required this.index, + VoidCallback? onTap, + DragTargetAccept? onDropBefore, + DragTargetAccept? onDropAfter, + VoidCallback? onDragStarted, + VoidCallback? onDragEndedAny, }) : _onTap = onTap, _onDropBefore = onDropBefore, _onDropAfter = onDropAfter, @@ -709,22 +714,22 @@ abstract class _ListItem implements SelectableItem, DraggableItem { final int index; - final VoidCallback _onTap; - final DragTargetAccept _onDropBefore; - final DragTargetAccept _onDropAfter; - final VoidCallback _onDragStarted; - final VoidCallback _onDragEndedAny; + final VoidCallback? _onTap; + final DragTargetAccept? _onDropBefore; + final DragTargetAccept? _onDropAfter; + final VoidCallback? _onDragStarted; + final VoidCallback? _onDragEndedAny; } abstract class _FileListItem extends _ListItem { _FileListItem({ - @required int index, - @required this.file, - VoidCallback onTap, - DragTargetAccept onDropBefore, - DragTargetAccept onDropAfter, - VoidCallback onDragStarted, - VoidCallback onDragEndedAny, + required int index, + required this.file, + VoidCallback? onTap, + DragTargetAccept? onDropBefore, + DragTargetAccept? onDropAfter, + VoidCallback? onDragStarted, + VoidCallback? onDragEndedAny, }) : super( index: index, onTap: onTap, @@ -739,15 +744,15 @@ abstract class _FileListItem extends _ListItem { class _ImageListItem extends _FileListItem { _ImageListItem({ - @required int index, - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, - DragTargetAccept onDropBefore, - DragTargetAccept onDropAfter, - VoidCallback onDragStarted, - VoidCallback onDragEndedAny, + required int index, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, + DragTargetAccept? onDropBefore, + DragTargetAccept? onDropAfter, + VoidCallback? onDragStarted, + VoidCallback? onDragEndedAny, }) : super( index: index, file: file, @@ -773,15 +778,15 @@ class _ImageListItem extends _FileListItem { class _VideoListItem extends _FileListItem { _VideoListItem({ - @required int index, - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, - DragTargetAccept onDropBefore, - DragTargetAccept onDropAfter, - VoidCallback onDragStarted, - VoidCallback onDragEndedAny, + required int index, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, + DragTargetAccept? onDropBefore, + DragTargetAccept? onDropAfter, + VoidCallback? onDragStarted, + VoidCallback? onDragEndedAny, }) : super( index: index, file: file, @@ -806,12 +811,12 @@ class _VideoListItem extends _FileListItem { class _LabelListItem extends _ListItem { _LabelListItem({ - @required int index, - @required this.text, - DragTargetAccept onDropBefore, - DragTargetAccept onDropAfter, - VoidCallback onDragStarted, - VoidCallback onDragEndedAny, + required int index, + required this.text, + DragTargetAccept? onDropBefore, + DragTargetAccept? onDropAfter, + VoidCallback? onDragStarted, + VoidCallback? onDragEndedAny, }) : super( index: index, onDropBefore: onDropBefore, @@ -842,13 +847,13 @@ class _LabelListItem extends _ListItem { class _EditLabelListItem extends _LabelListItem { _EditLabelListItem({ - @required int index, - @required String text, - @required this.onEditPressed, - DragTargetAccept onDropBefore, - DragTargetAccept onDropAfter, - VoidCallback onDragStarted, - VoidCallback onDragEndedAny, + required int index, + required String text, + required this.onEditPressed, + DragTargetAccept? onDropBefore, + DragTargetAccept? onDropAfter, + VoidCallback? onDragStarted, + VoidCallback? onDragEndedAny, }) : super( index: index, text: text, @@ -873,7 +878,7 @@ class _EditLabelListItem extends _LabelListItem { end: 0, child: IconButton( icon: Icon(Icons.edit), - tooltip: AppLocalizations.of(context).editTooltip, + tooltip: AppLocalizations.of(context)!.editTooltip, onPressed: onEditPressed, ), ), @@ -886,5 +891,5 @@ class _EditLabelListItem extends _LabelListItem { return super.buildWidget(context); } - final VoidCallback onEditPressed; + final VoidCallback? onEditPressed; } diff --git a/lib/widget/album_viewer_mixin.dart b/lib/widget/album_viewer_mixin.dart index 8810c624..084965ec 100644 --- a/lib/widget/album_viewer_mixin.dart +++ b/lib/widget/album_viewer_mixin.dart @@ -18,20 +18,17 @@ mixin AlbumViewerMixin @override initState() { super.initState(); - _thumbZoomLevel = Pref.inst().getAlbumViewerZoomLevel(0); + _thumbZoomLevel = Pref.inst().getAlbumViewerZoomLevelOr(0); } @protected - File initCover(Account account, List backingFiles) { + void initCover(Account account, List backingFiles) { try { final coverFile = - backingFiles.firstWhere((element) => element.hasPreview); + backingFiles.firstWhere((element) => element.hasPreview ?? false); _coverPreviewUrl = api_util.getFilePreviewUrl(account, coverFile, width: 1024, height: 600); - return coverFile; - } catch (_) { - return null; - } + } catch (_) {} } @protected @@ -39,9 +36,9 @@ mixin AlbumViewerMixin BuildContext context, Account account, Album album, { - List actions, - List> Function(BuildContext) menuItemBuilder, - void Function(int) onSelectedMenuItem, + List? actions, + List> Function(BuildContext)? menuItemBuilder, + void Function(int)? onSelectedMenuItem, }) { return SliverAppBar( floating: true, @@ -58,7 +55,7 @@ mixin AlbumViewerMixin actions: [ PopupMenuButton( icon: const Icon(Icons.photo_size_select_large), - tooltip: AppLocalizations.of(context).zoomTooltip, + tooltip: AppLocalizations.of(context)!.zoomTooltip, itemBuilder: (context) => [ PopupMenuZoom( initialValue: _thumbZoomLevel, @@ -74,12 +71,12 @@ mixin AlbumViewerMixin ], ), ...(actions ?? []), - PopupMenuButton( + PopupMenuButton( tooltip: MaterialLocalizations.of(context).moreButtonTooltip, itemBuilder: (context) => [ PopupMenuItem( value: -1, - child: Text(AppLocalizations.of(context).editAlbumMenuLabel), + child: Text(AppLocalizations.of(context)!.editAlbumMenuLabel), ), ...(menuItemBuilder?.call(context) ?? []), ], @@ -120,7 +117,7 @@ mixin AlbumViewerMixin }); }, ), - title: Text(AppLocalizations.of(context) + title: Text(AppLocalizations.of(context)! .selectionAppBarTitle(selectedListItems.length)), actions: actions, ), @@ -132,7 +129,7 @@ mixin AlbumViewerMixin BuildContext context, Account account, Album album, { - List actions, + List? actions, }) { return SliverAppBar( floating: true, @@ -141,16 +138,17 @@ mixin AlbumViewerMixin background: _getAppBarCover(context, account), title: TextFormField( decoration: InputDecoration( - hintText: AppLocalizations.of(context).nameInputHint, + hintText: AppLocalizations.of(context)!.nameInputHint, ), validator: (value) { - if (value.isEmpty) { - return AppLocalizations.of(context).albumNameInputInvalidEmpty; + if (value?.isNotEmpty == true) { + return null; + } else { + return AppLocalizations.of(context)!.albumNameInputInvalidEmpty; } - return null; }, onSaved: (value) { - _editFormValue.name = value; + _editFormValue.name = value!; }, onChanged: (value) { // need to save the value otherwise it'll return to the initial @@ -166,7 +164,7 @@ mixin AlbumViewerMixin leading: IconButton( icon: const Icon(Icons.check), color: Theme.of(context).colorScheme.primary, - tooltip: AppLocalizations.of(context).doneButtonTooltip, + tooltip: AppLocalizations.of(context)!.doneButtonTooltip, onPressed: () { if (validateEditMode()) { setState(() { @@ -226,7 +224,7 @@ mixin AlbumViewerMixin }); } - Widget _getAppBarCover(BuildContext context, Account account) { + Widget? _getAppBarCover(BuildContext context, Account account) { try { if (_coverPreviewUrl != null) { return Opacity( @@ -236,7 +234,7 @@ mixin AlbumViewerMixin clipBehavior: Clip.hardEdge, fit: BoxFit.cover, child: CachedNetworkImage( - imageUrl: _coverPreviewUrl, + imageUrl: _coverPreviewUrl!, httpHeaders: { "Authorization": Api.getAuthorizationHeaderValue(account), }, @@ -254,11 +252,11 @@ mixin AlbumViewerMixin return null; } - String _coverPreviewUrl; + String? _coverPreviewUrl; var _thumbZoomLevel = 0; var _isEditMode = false; - String _editNameValue; + String? _editNameValue; var _editFormValue = _EditFormValue(); static final _log = Logger("widget.album_viewer_mixin.AlbumViewerMixin"); @@ -266,5 +264,5 @@ mixin AlbumViewerMixin } class _EditFormValue { - String name; + late String name; } diff --git a/lib/widget/animated_visibility.dart b/lib/widget/animated_visibility.dart index 44d3e777..2aab2152 100644 --- a/lib/widget/animated_visibility.dart +++ b/lib/widget/animated_visibility.dart @@ -5,14 +5,14 @@ import 'package:flutter/widgets.dart'; /// The point is to disable non-visible buttons class AnimatedVisibility extends StatefulWidget { const AnimatedVisibility({ - Key key, - this.child, - @required this.opacity, + Key? key, + required this.child, + required this.opacity, this.curve = Curves.linear, - @required this.duration, + required this.duration, this.onEnd, this.alwaysIncludeSemantics = false, - }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0), + }) : assert(opacity >= 0.0 && opacity <= 1.0), super(key: key); @override @@ -22,7 +22,7 @@ class AnimatedVisibility extends StatefulWidget { final double opacity; final Curve curve; final Duration duration; - final VoidCallback onEnd; + final VoidCallback? onEnd; final bool alwaysIncludeSemantics; } diff --git a/lib/widget/archive_viewer.dart b/lib/widget/archive_viewer.dart index 0bc37b5b..beadf311 100644 --- a/lib/widget/archive_viewer.dart +++ b/lib/widget/archive_viewer.dart @@ -32,12 +32,16 @@ class ArchiveViewerArguments { class ArchiveViewer extends StatefulWidget { static const routeName = "/archive-viewer"; + static Route buildRoute(ArchiveViewerArguments args) => MaterialPageRoute( + builder: (context) => ArchiveViewer.fromArgs(args), + ); + ArchiveViewer({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - ArchiveViewer.fromArgs(ArchiveViewerArguments args, {Key key}) + ArchiveViewer.fromArgs(ArchiveViewerArguments args, {Key? key}) : this( key: key, account: args.account, @@ -55,7 +59,7 @@ class _ArchiveViewerState extends State initState() { super.initState(); _initBloc(); - _thumbZoomLevel = Pref.inst().getAlbumViewerZoomLevel(0); + _thumbZoomLevel = Pref.inst().getAlbumViewerZoomLevelOr(0); } @override @@ -81,7 +85,7 @@ class _ArchiveViewerState extends State _reqQuery(); } else { // process the current state - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { setState(() { _onStateChange(context, _bloc.state); }); @@ -144,12 +148,12 @@ class _ArchiveViewerState extends State }); }, ), - title: Text(AppLocalizations.of(context) + title: Text(AppLocalizations.of(context)! .selectionAppBarTitle(selectedListItems.length)), actions: [ IconButton( icon: const Icon(Icons.unarchive), - tooltip: AppLocalizations.of(context).unarchiveSelectedTooltip, + tooltip: AppLocalizations.of(context)!.unarchiveSelectedTooltip, onPressed: () { _onSelectionAppBarUnarchivePressed(); }, @@ -161,12 +165,12 @@ class _ArchiveViewerState extends State Widget _buildNormalAppBar(BuildContext context) { return SliverAppBar( - title: Text(AppLocalizations.of(context).albumArchiveLabel), + title: Text(AppLocalizations.of(context)!.albumArchiveLabel), floating: true, actions: [ PopupMenuButton( icon: const Icon(Icons.photo_size_select_large), - tooltip: AppLocalizations.of(context).zoomTooltip, + tooltip: AppLocalizations.of(context)!.zoomTooltip, itemBuilder: (context) => [ PopupMenuZoom( initialValue: _thumbZoomLevel, @@ -208,7 +212,7 @@ class _ArchiveViewerState extends State Future _onSelectionAppBarUnarchivePressed() async { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .unarchiveSelectedProcessingNotification(selectedListItems.length)), duration: k.snackBarDurationShort, )); @@ -237,12 +241,12 @@ class _ArchiveViewerState extends State if (failures.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).unarchiveSelectedSuccessNotification), + AppLocalizations.of(context)!.unarchiveSelectedSuccessNotification), duration: k.snackBarDurationNormal, )); } else { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .unarchiveSelectedFailureNotification(failures.length)), duration: k.snackBarDurationNormal, )); @@ -308,7 +312,7 @@ class _ArchiveViewerState extends State } } - ScanDirBloc _bloc; + late ScanDirBloc _bloc; var _backingFiles = []; @@ -319,7 +323,7 @@ class _ArchiveViewerState extends State abstract class _ListItem implements SelectableItem { _ListItem({ - VoidCallback onTap, + VoidCallback? onTap, }) : _onTap = onTap; @override @@ -331,13 +335,13 @@ abstract class _ListItem implements SelectableItem { @override get staggeredTile => const StaggeredTile.count(1, 1); - final VoidCallback _onTap; + final VoidCallback? _onTap; } abstract class _FileListItem extends _ListItem { _FileListItem({ - @required this.file, - VoidCallback onTap, + required this.file, + VoidCallback? onTap, }) : super(onTap: onTap); @override @@ -353,10 +357,10 @@ abstract class _FileListItem extends _ListItem { class _ImageListItem extends _FileListItem { _ImageListItem({ - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, }) : super(file: file, onTap: onTap); @override @@ -374,10 +378,10 @@ class _ImageListItem extends _FileListItem { class _VideoListItem extends _FileListItem { _VideoListItem({ - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, }) : super(file: file, onTap: onTap); @override diff --git a/lib/widget/builder/album_grid_item_builder.dart b/lib/widget/builder/album_grid_item_builder.dart index 8514713a..46df84fc 100644 --- a/lib/widget/builder/album_grid_item_builder.dart +++ b/lib/widget/builder/album_grid_item_builder.dart @@ -13,8 +13,8 @@ import 'package:nc_photos/widget/album_grid_item.dart'; /// Build a standard [AlbumGridItem] for an [Album] class AlbumGridItemBuilder { AlbumGridItemBuilder({ - @required this.account, - @required this.album, + required this.account, + required this.album, this.isSelected = false, this.onTap, this.onLongPress, @@ -22,9 +22,9 @@ class AlbumGridItemBuilder { AlbumGridItem build(BuildContext context) { var subtitle = ""; - String subtitle2; + String? subtitle2; if (album.provider is AlbumStaticProvider) { - subtitle = AppLocalizations.of(context) + subtitle = AppLocalizations.of(context)! .albumSize(AlbumStaticProvider.of(album).items.length); } else if (album.provider is AlbumDirProvider) { final provider = album.provider as AlbumDirProvider; @@ -49,7 +49,7 @@ class AlbumGridItemBuilder { Widget cover; try { final coverFile = album.coverProvider.getCover(album); - final previewUrl = api_util.getFilePreviewUrl(account, coverFile, + final previewUrl = api_util.getFilePreviewUrl(account, coverFile!, width: 512, height: 512); cover = FittedBox( clipBehavior: Clip.hardEdge, @@ -89,6 +89,6 @@ class AlbumGridItemBuilder { final Account account; final Album album; final bool isSelected; - final VoidCallback onTap; - final VoidCallback onLongPress; + final VoidCallback? onTap; + final VoidCallback? onLongPress; } diff --git a/lib/widget/cached_network_image_mod.dart b/lib/widget/cached_network_image_mod.dart index 098f95b8..14437911 100644 --- a/lib/widget/cached_network_image_mod.dart +++ b/lib/widget/cached_network_image_mod.dart @@ -21,8 +21,8 @@ class CachedNetworkImage extends StatelessWidget { /// to clear the image from the [ImageCache]. static Future evictFromCache( String url, { - String cacheKey, - BaseCacheManager cacheManager, + String? cacheKey, + BaseCacheManager? cacheManager, double scale = 1.0, }) async { cacheManager = cacheManager ?? DefaultCacheManager(); @@ -33,31 +33,31 @@ class CachedNetworkImage extends StatelessWidget { final CachedNetworkImageProvider _image; /// Option to use cachemanager with other settings - final BaseCacheManager cacheManager; + final BaseCacheManager? cacheManager; /// The target image that is displayed. final String imageUrl; /// The target image's cache key. - final String cacheKey; + final String? cacheKey; /// Optional builder to further customize the display of the image. - final ImageWidgetBuilder imageBuilder; + final ImageWidgetBuilder? imageBuilder; /// Widget displayed while the target [imageUrl] is loading. - final PlaceholderWidgetBuilder placeholder; + final PlaceholderWidgetBuilder? placeholder; /// Widget displayed while the target [imageUrl] is loading. - final ProgressIndicatorBuilder progressIndicatorBuilder; + final ProgressIndicatorBuilder? progressIndicatorBuilder; /// Widget displayed while the target [imageUrl] failed loading. - final LoadingErrorWidgetBuilder errorWidget; + final LoadingErrorWidgetBuilder? errorWidget; /// The duration of the fade-in animation for the [placeholder]. - final Duration placeholderFadeInDuration; + final Duration? placeholderFadeInDuration; /// The duration of the fade-out animation for the [placeholder]. - final Duration fadeOutDuration; + final Duration? fadeOutDuration; /// The curve of the fade-out animation for the [placeholder]. final Curve fadeOutCurve; @@ -74,7 +74,7 @@ class CachedNetworkImage extends StatelessWidget { /// aspect ratio. This may result in a sudden change if the size of the /// placeholder widget does not match that of the target image. The size is /// also affected by the scale factor. - final double width; + final double? width; /// If non-null, require the image to have this height. /// @@ -82,13 +82,13 @@ class CachedNetworkImage extends StatelessWidget { /// aspect ratio. This may result in a sudden change if the size of the /// placeholder widget does not match that of the target image. The size is /// also affected by the scale factor. - final double height; + final double? height; /// How to inscribe the image into the space allocated during layout. /// /// The default varies based on the other fields. See the discussion at /// [paintImage]. - final BoxFit fit; + final BoxFit? fit; /// How to align the image within its bounds. /// @@ -112,7 +112,7 @@ class CachedNetworkImage extends StatelessWidget { /// specify an [AlignmentGeometry]. /// * [AlignmentDirectional], like [Alignment] for specifying alignments /// relative to text direction. - final AlignmentGeometry alignment; + final Alignment alignment; /// How to paint any portions of the layout bounds not covered by the image. final ImageRepeat repeat; @@ -135,14 +135,14 @@ class CachedNetworkImage extends StatelessWidget { final bool matchTextDirection; /// Optional headers for the http request of the image url - final Map httpHeaders; + final Map? httpHeaders; /// When set to true it will animate from the old image to the new image /// if the url changes. final bool useOldImageOnUrlChange; /// If non-null, this color is blended with each image pixel using [colorBlendMode]. - final Color color; + final Color? color; /// Used to combine [color] with this image. /// @@ -152,7 +152,7 @@ class CachedNetworkImage extends StatelessWidget { /// See also: /// /// * [BlendMode], which includes an illustration of the effect of each blend mode. - final BlendMode colorBlendMode; + final BlendMode? colorBlendMode; /// Target the interpolation quality for image scaling. /// @@ -160,24 +160,24 @@ class CachedNetworkImage extends StatelessWidget { final FilterQuality filterQuality; /// Will resize the image in memory to have a certain width using [ResizeImage] - final int memCacheWidth; + final int? memCacheWidth; /// Will resize the image in memory to have a certain height using [ResizeImage] - final int memCacheHeight; + final int? memCacheHeight; /// Will resize the image and store the resized image in the disk cache. - final int maxWidthDiskCache; + final int? maxWidthDiskCache; /// Will resize the image and store the resized image in the disk cache. - final int maxHeightDiskCache; + final int? maxHeightDiskCache; /// CachedNetworkImage shows a network image using a caching mechanism. It also /// provides support for a placeholder, showing an error and fading into the /// loaded image. Next to that it supports most features of a default Image /// widget. CachedNetworkImage({ - Key key, - @required this.imageUrl, + Key? key, + required this.imageUrl, this.httpHeaders, this.imageBuilder, this.placeholder, @@ -204,17 +204,8 @@ class CachedNetworkImage extends StatelessWidget { this.cacheKey, this.maxWidthDiskCache, this.maxHeightDiskCache, - ImageRenderMethodForWeb imageRenderMethodForWeb, - }) : assert(imageUrl != null), - assert(fadeOutDuration != null), - assert(fadeOutCurve != null), - assert(fadeInDuration != null), - assert(fadeInCurve != null), - assert(alignment != null), - assert(filterQuality != null), - assert(repeat != null), - assert(matchTextDirection != null), - _image = CachedNetworkImageProvider( + ImageRenderMethodForWeb? imageRenderMethodForWeb, + }) : _image = CachedNetworkImageProvider( imageUrl, headers: httpHeaders, cacheManager: cacheManager, @@ -267,32 +258,32 @@ class CachedNetworkImage extends StatelessWidget { } Widget _octoImageBuilder(BuildContext context, Widget child) { - return imageBuilder(context, child, _image); + return imageBuilder!(context, child, _image); } Widget _octoPlaceholderBuilder(BuildContext context) { - return placeholder(context, imageUrl); + return placeholder!(context, imageUrl); } Widget _octoProgressIndicatorBuilder( BuildContext context, - ImageChunkEvent progress, + ImageChunkEvent? progress, ) { - int totalSize; + int? totalSize; var downloaded = 0; if (progress != null) { totalSize = progress.expectedTotalBytes; downloaded = progress.cumulativeBytesLoaded; } - return progressIndicatorBuilder( + return progressIndicatorBuilder!( context, imageUrl, DownloadProgress(imageUrl, totalSize, downloaded)); } Widget _octoErrorBuilder( BuildContext context, Object error, - StackTrace stackTrace, + StackTrace? stackTrace, ) { - return errorWidget(context, imageUrl, error); + return errorWidget!(context, imageUrl, error); } } diff --git a/lib/widget/connect.dart b/lib/widget/connect.dart index abf68439..db78a13c 100644 --- a/lib/widget/connect.dart +++ b/lib/widget/connect.dart @@ -25,12 +25,16 @@ class ConnectArguments { class Connect extends StatefulWidget { static const routeName = "/connect"; + static Route buildRoute(ConnectArguments args) => MaterialPageRoute( + builder: (context) => Connect.fromArgs(args), + ); + Connect({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - Connect.fromArgs(ConnectArguments args, {Key key}) + Connect.fromArgs(ConnectArguments args, {Key? key}) : this( key: key, account: args.account, @@ -82,7 +86,7 @@ class _ConnectState extends State { color: Theme.of(context).colorScheme.primary, ), Text( - AppLocalizations.of(context) + AppLocalizations.of(context)! .connectingToServer(widget.account.url), textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline6, @@ -106,7 +110,7 @@ class _ConnectState extends State { } else if (state.exception is ApiException && (state.exception as ApiException).response.statusCode == 401) { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).errorWrongPassword), + content: Text(AppLocalizations.of(context)!.errorWrongPassword), duration: k.snackBarDurationNormal, )); Navigator.of(context).pop(null); @@ -124,9 +128,9 @@ class _ConnectState extends State { showDialog( context: context, builder: (context) => AlertDialog( - title: Text(AppLocalizations.of(context).serverCertErrorDialogTitle), + title: Text(AppLocalizations.of(context)!.serverCertErrorDialogTitle), content: - Text(AppLocalizations.of(context).serverCertErrorDialogContent), + Text(AppLocalizations.of(context)!.serverCertErrorDialogContent), actions: [ TextButton( onPressed: () { @@ -138,7 +142,7 @@ class _ConnectState extends State { onPressed: () { Navigator.of(context).pop(true); }, - child: Text(AppLocalizations.of(context).advancedButtonLabel), + child: Text(AppLocalizations.of(context)!.advancedButtonLabel), ), ], ), @@ -150,10 +154,11 @@ class _ConnectState extends State { showDialog( context: context, builder: (context) => AlertDialog( - title: Text(AppLocalizations.of(context).whitelistCertDialogTitle), - content: Text(AppLocalizations.of(context).whitelistCertDialogContent( - SelfSignedCertManager().getLastBadCertHost(), - SelfSignedCertManager().getLastBadCertFingerprint())), + title: Text(AppLocalizations.of(context)!.whitelistCertDialogTitle), + content: Text(AppLocalizations.of(context)! + .whitelistCertDialogContent( + SelfSignedCertManager().getLastBadCertHost(), + SelfSignedCertManager().getLastBadCertFingerprint())), actions: [ TextButton( onPressed: () { @@ -166,7 +171,7 @@ class _ConnectState extends State { Navigator.of(context).pop(true); }, child: - Text(AppLocalizations.of(context).whitelistCertButtonLabel), + Text(AppLocalizations.of(context)!.whitelistCertButtonLabel), ), ], ), diff --git a/lib/widget/dir_picker_mixin.dart b/lib/widget/dir_picker_mixin.dart index 31ac2185..abd5f821 100644 --- a/lib/widget/dir_picker_mixin.dart +++ b/lib/widget/dir_picker_mixin.dart @@ -100,7 +100,7 @@ mixin DirPickerMixin on State { dense: true, leading: const SizedBox(width: 24), title: Text( - AppLocalizations.of(context).rootPickerNavigateUpItemText), + AppLocalizations.of(context)!.rootPickerNavigateUpItemText), onTap: () { try { _navigateInto(File(path: path.dirname(_currentPath))); @@ -128,7 +128,7 @@ mixin DirPickerMixin on State { final canPick = canPickDir(item.file); final pickState = _isItemPicked(item); - IconData iconData; + IconData? iconData; if (canPick) { switch (pickState) { case _PickState.picked: @@ -170,9 +170,10 @@ mixin DirPickerMixin on State { onPressed: null, ), title: Text(path.basename(item.file.path)), - trailing: - item.children.isNotEmpty ? const Icon(Icons.arrow_forward_ios) : null, - onTap: item.children.isNotEmpty + trailing: item.children?.isNotEmpty == true + ? const Icon(Icons.arrow_forward_ios) + : null, + onTap: item.children?.isNotEmpty == true ? () { try { _navigateInto(item.file); @@ -238,12 +239,12 @@ mixin DirPickerMixin on State { // this dir is explicitly picked, nothing more to do return [item.file]; } - if (item.children == null || item.children.isEmpty) { + if (item.children == null || item.children!.isEmpty) { return []; } final products = []; - for (final i in item.children) { + for (final i in item.children!) { products.addAll(_optimizePicks(i)); } // // see if all children are being picked @@ -282,7 +283,7 @@ mixin DirPickerMixin on State { _picks.removeWhere((element) => identical(element, parent)); } catch (_) { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .rootPickerUnpickFailureNotification))); } } @@ -296,12 +297,13 @@ mixin DirPickerMixin on State { /// Either [path] or [item] must be set, If both are set, [item] takes /// priority List _pickedAllExclude({ - String path, - LsDirBlocItem item, - @required LsDirBlocItem exclude, + String? path, + LsDirBlocItem? item, + required LsDirBlocItem exclude, }) { + assert(path != null || item != null); if (item == null) { - final item = _findChildItemByPath(_root, path); + final item = _findChildItemByPath(_root, path!); return _pickedAllExclude(item: item, exclude: exclude); } @@ -311,7 +313,7 @@ mixin DirPickerMixin on State { _log.fine( "[_pickedAllExclude] Unpicking '${item.file.path}' and picking children"); final products = []; - for (final i in item.children) { + for (final i in item.children ?? []) { if (exclude.file.path.startsWith(i.file.path)) { // [i] is a parent of exclude products.addAll(_pickedAllExclude(item: i, exclude: exclude)); @@ -327,7 +329,7 @@ mixin DirPickerMixin on State { if (path == parent.file.path) { return parent; } - for (final c in parent.children) { + for (final c in parent.children ?? []) { if (path == c.file.path || path.startsWith("${c.file.path}/")) { return _findChildItemByPath(c, path); } @@ -364,11 +366,11 @@ mixin DirPickerMixin on State { _bloc.add(LsDirBlocQuery(getAccount(), file, depth: 2)); } - LsDirBloc _bloc; - LsDirBlocItem _root; + late LsDirBloc _bloc; + late LsDirBlocItem _root; /// Track where the user is navigating in [_backingFiles] - String _currentPath; + late String _currentPath; var _picks = []; static final _log = Logger("widget.dir_picker_mixin.DirPickerMixin"); diff --git a/lib/widget/draggable.dart b/lib/widget/draggable.dart index 95aab4c7..932ed49b 100644 --- a/lib/widget/draggable.dart +++ b/lib/widget/draggable.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; -class Draggable extends StatelessWidget { +class Draggable extends StatelessWidget { Draggable({ - Key key, - @required this.data, - @required this.child, + Key? key, + required this.data, + required this.child, this.feedback, this.onDropBefore, this.onDropAfter, @@ -79,7 +79,7 @@ class Draggable extends StatelessWidget { }, onAccept: (item) { _log.fine("[build] Dropping $item before $data"); - onDropBefore(item); + onDropBefore!(item); }, ), ), @@ -92,7 +92,7 @@ class Draggable extends StatelessWidget { }, onAccept: (item) { _log.fine("[build] Dropping $item after $data"); - onDropAfter(item); + onDropAfter!(item); }, ), ), @@ -105,26 +105,26 @@ class Draggable extends StatelessWidget { final T data; final Widget child; - final Widget feedback; + final Widget? feedback; /// Called when some item dropped before this item - final DragTargetAccept onDropBefore; + final DragTargetAccept? onDropBefore; /// Called when some item dropped after this item - final DragTargetAccept onDropAfter; + final DragTargetAccept? onDropAfter; - final VoidCallback onDragStarted; + final VoidCallback? onDragStarted; /// Called when either one of onDragEnd, onDragCompleted or /// onDraggableCanceled is called. /// /// The callback might be called multiple times per each drag event - final VoidCallback onDragEndedAny; + final VoidCallback? onDragEndedAny; /// Size of the feedback widget that appears under the pointer. /// /// Right now a translucent version of [child] is being shown - final Size feedbackSize; + final Size? feedbackSize; static final _log = Logger("widget.draggable.Draggable"); } diff --git a/lib/widget/draggable_item_list_mixin.dart b/lib/widget/draggable_item_list_mixin.dart index b77a4c56..5139e23a 100644 --- a/lib/widget/draggable_item_list_mixin.dart +++ b/lib/widget/draggable_item_list_mixin.dart @@ -9,21 +9,21 @@ abstract class DraggableItem { /// The widget to show under the pointer when a drag is under way. /// /// Return null if you wish to just use the same widget as display - Widget buildDragFeedbackWidget(BuildContext context) => null; + Widget? buildDragFeedbackWidget(BuildContext context) => null; bool get isDraggable => false; - DragTargetAccept get onDropBefore => null; - DragTargetAccept get onDropAfter => null; - VoidCallback get onDragStarted => null; - VoidCallback get onDragEndedAny => null; + DragTargetAccept? get onDropBefore => null; + DragTargetAccept? get onDropAfter => null; + VoidCallback? get onDragStarted => null; + VoidCallback? get onDragEndedAny => null; StaggeredTile get staggeredTile => const StaggeredTile.count(1, 1); } mixin DraggableItemListMixin on State { @protected Widget buildDraggableItemList({ - @required double maxCrossAxisExtent, - ValueChanged onMaxExtentChanged, + required double maxCrossAxisExtent, + ValueChanged? onMaxExtentChanged, }) { _maxCrossAxisExtent = maxCrossAxisExtent; return MeasurableItemList( @@ -50,12 +50,10 @@ mixin DraggableItemListMixin on State { onDropAfter: item.onDropAfter, onDragStarted: item.onDragStarted, onDragEndedAny: item.onDragEndedAny, - feedbackSize: _maxCrossAxisExtent != null - ? Size(_maxCrossAxisExtent * .65, _maxCrossAxisExtent * .65) - : null, + feedbackSize: Size(_maxCrossAxisExtent * .65, _maxCrossAxisExtent * .65), ); } var _items = []; - double _maxCrossAxisExtent; + late double _maxCrossAxisExtent; } diff --git a/lib/widget/dynamic_album_viewer.dart b/lib/widget/dynamic_album_viewer.dart index b843076b..69cc6ffe 100644 --- a/lib/widget/dynamic_album_viewer.dart +++ b/lib/widget/dynamic_album_viewer.dart @@ -43,13 +43,18 @@ class DynamicAlbumViewerArguments { class DynamicAlbumViewer extends StatefulWidget { static const routeName = "/dynamic-album-viewer"; + static Route buildRoute(DynamicAlbumViewerArguments args) => + MaterialPageRoute( + builder: (context) => DynamicAlbumViewer.fromArgs(args), + ); + DynamicAlbumViewer({ - Key key, - @required this.account, - @required this.album, + Key? key, + required this.account, + required this.album, }) : super(key: key); - DynamicAlbumViewer.fromArgs(DynamicAlbumViewerArguments args, {Key key}) + DynamicAlbumViewer.fromArgs(DynamicAlbumViewerArguments args, {Key? key}) : this( key: key, account: args.account, @@ -96,19 +101,19 @@ class _DynamicAlbumViewerState extends State @override enterEditMode() { super.enterEditMode(); - _editAlbum = _album.copyWith(); + _editAlbum = _album!.copyWith(); } @override - validateEditMode() => _editFormKey?.currentState?.validate() == true; + validateEditMode() => _editFormKey.currentState?.validate() == true; @override doneEditMode() { try { // persist the changes - _editFormKey.currentState.save(); - final newAlbum = makeEdited(_editAlbum); - if (newAlbum.copyWith(lastUpdated: OrNull(_album.lastUpdated)) != + _editFormKey.currentState!.save(); + final newAlbum = makeEdited(_editAlbum!); + if (newAlbum.copyWith(lastUpdated: OrNull(_album!.lastUpdated)) != _album) { _log.info("[doneEditMode] Album modified: $newAlbum"); final albumRepo = AlbumRepo(AlbumCachedDataSource()); @@ -142,14 +147,14 @@ class _DynamicAlbumViewerState extends State setState(() { _album = widget.album; _transformItems(items); - final coverFile = initCover(widget.account, _backingFiles); - _updateAlbumPostPopulate(coverFile, items); + initCover(widget.account, _backingFiles); + _updateAlbumPostPopulate(items); }); } }); } - void _updateAlbumPostPopulate(File coverFile, List items) { + void _updateAlbumPostPopulate(List items) { List timeDescSortedFiles; if (widget.album.sortProvider is AlbumTimeSortProvider) { if ((widget.album.sortProvider as AlbumTimeSortProvider).isAscending) { @@ -168,7 +173,7 @@ class _DynamicAlbumViewerState extends State bool shouldUpdate = false; final albumUpdatedCover = UpdateDynamicAlbumCover() - .updateWithSortedFiles(_album, timeDescSortedFiles); + .updateWithSortedFiles(_album!, timeDescSortedFiles); if (!identical(albumUpdatedCover, _album)) { _log.info("[_updateAlbumPostPopulate] Update album cover"); shouldUpdate = true; @@ -176,7 +181,7 @@ class _DynamicAlbumViewerState extends State _album = albumUpdatedCover; final albumUpdatedTime = UpdateDynamicAlbumTime() - .updateWithSortedFiles(_album, timeDescSortedFiles); + .updateWithSortedFiles(_album!, timeDescSortedFiles); if (!identical(albumUpdatedTime, _album)) { _log.info( "[_updateAlbumPostPopulate] Update album time: ${albumUpdatedTime.provider.latestItemTime}"); @@ -185,7 +190,7 @@ class _DynamicAlbumViewerState extends State _album = albumUpdatedTime; if (shouldUpdate) { - UpdateAlbum(AlbumRepo(AlbumCachedDataSource()))(widget.account, _album); + UpdateAlbum(AlbumRepo(AlbumCachedDataSource()))(widget.account, _album!); } } @@ -241,11 +246,11 @@ class _DynamicAlbumViewerState extends State return buildNormalAppBar( context, widget.account, - _album, + _album!, menuItemBuilder: (context) => [ PopupMenuItem( value: _menuValueConvertBasic, - child: Text(AppLocalizations.of(context).convertBasicAlbumMenuLabel), + child: Text(AppLocalizations.of(context)!.convertBasicAlbumMenuLabel), ), ], onSelectedMenuItem: (option) { @@ -267,7 +272,7 @@ class _DynamicAlbumViewerState extends State if (platform_k.isAndroid) IconButton( icon: const Icon(Icons.share), - tooltip: AppLocalizations.of(context).shareSelectedTooltip, + tooltip: AppLocalizations.of(context)!.shareSelectedTooltip, onPressed: () { _onSelectionAppBarSharePressed(context); }, @@ -277,7 +282,7 @@ class _DynamicAlbumViewerState extends State itemBuilder: (context) => [ PopupMenuItem( value: _SelectionAppBarOption.delete, - child: Text(AppLocalizations.of(context).deleteSelectedTooltip), + child: Text(AppLocalizations.of(context)!.deleteSelectedTooltip), ), ], onSelected: (option) { @@ -293,7 +298,7 @@ class _DynamicAlbumViewerState extends State return buildEditAppBar(context, widget.account, widget.album, actions: [ IconButton( icon: Icon(Icons.sort_by_alpha), - tooltip: AppLocalizations.of(context).sortTooltip, + tooltip: AppLocalizations.of(context)!.sortTooltip, onPressed: _onEditAppBarSortPressed, ), ]); @@ -317,9 +322,9 @@ class _DynamicAlbumViewerState extends State showDialog( context: context, builder: (context) => AlertDialog( - title: Text(AppLocalizations.of(context) + title: Text(AppLocalizations.of(context)! .convertBasicAlbumConfirmationDialogTitle), - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .convertBasicAlbumConfirmationDialogContent), actions: [ TextButton( @@ -341,17 +346,17 @@ class _DynamicAlbumViewerState extends State return; } _log.info( - "[_onAppBarConvertBasicPressed] Converting album '${_album.name}' to static"); + "[_onAppBarConvertBasicPressed] Converting album '${_album!.name}' to static"); final albumRepo = AlbumRepo(AlbumCachedDataSource()); UpdateAlbum(albumRepo)( widget.account, - _album.copyWith( + _album!.copyWith( provider: AlbumStaticProvider(items: _sortedItems), coverProvider: AlbumAutoCoverProvider(), ), ).then((value) { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .convertBasicAlbumSuccessNotification), duration: k.snackBarDurationNormal, )); @@ -386,7 +391,7 @@ class _DynamicAlbumViewerState extends State void _onSelectionAppBarDeletePressed() async { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .deleteSelectedProcessingNotification(selectedListItems.length)), duration: k.snackBarDurationShort, )); @@ -417,12 +422,12 @@ class _DynamicAlbumViewerState extends State if (failures.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).deleteSelectedSuccessNotification), + AppLocalizations.of(context)!.deleteSelectedSuccessNotification), duration: k.snackBarDurationNormal, )); } else { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .deleteSelectedFailureNotification(failures.length)), duration: k.snackBarDurationNormal, )); @@ -439,14 +444,14 @@ class _DynamicAlbumViewerState extends State } void _onEditAppBarSortPressed() { - final sortProvider = _editAlbum.sortProvider; + final sortProvider = _editAlbum!.sortProvider; showDialog( context: context, builder: (context) => FancyOptionPicker( - title: AppLocalizations.of(context).sortOptionDialogTitle, + title: AppLocalizations.of(context)!.sortOptionDialogTitle, items: [ FancyOptionPickerItem( - label: AppLocalizations.of(context).sortOptionTimeAscendingLabel, + label: AppLocalizations.of(context)!.sortOptionTimeAscendingLabel, isSelected: sortProvider is AlbumTimeSortProvider && sortProvider.isAscending, onSelect: () { @@ -455,7 +460,7 @@ class _DynamicAlbumViewerState extends State }, ), FancyOptionPickerItem( - label: AppLocalizations.of(context).sortOptionTimeDescendingLabel, + label: AppLocalizations.of(context)!.sortOptionTimeDescendingLabel, isSelected: sortProvider is AlbumTimeSortProvider && !sortProvider.isAscending, onSelect: () { @@ -469,7 +474,7 @@ class _DynamicAlbumViewerState extends State } void _onSortOldestPressed() { - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( sortProvider: AlbumTimeSortProvider(isAscending: true), ); setState(() { @@ -478,7 +483,7 @@ class _DynamicAlbumViewerState extends State } void _onSortNewestPressed() { - _editAlbum = _editAlbum.copyWith( + _editAlbum = _editAlbum!.copyWith( sortProvider: AlbumTimeSortProvider(isAscending: false), ); setState(() { @@ -489,9 +494,9 @@ class _DynamicAlbumViewerState extends State void _transformItems(List items) { if (_editAlbum != null) { // edit mode - _sortedItems = _editAlbum.sortProvider.sort(items); + _sortedItems = _editAlbum!.sortProvider.sort(items); } else { - _sortedItems = _album.sortProvider.sort(items); + _sortedItems = _album!.sortProvider.sort(items); } _onSortedItemsUpdated(); } @@ -535,12 +540,12 @@ class _DynamicAlbumViewerState extends State .toList(); } - Album _album; + Album? _album; var _sortedItems = []; var _backingFiles = []; final _editFormKey = GlobalKey(); - Album _editAlbum; + Album? _editAlbum; static final _log = Logger("widget.dynamic_album_viewer._DynamicAlbumViewerState"); @@ -549,8 +554,8 @@ class _DynamicAlbumViewerState extends State abstract class _ListItem implements SelectableItem { _ListItem({ - @required this.index, - VoidCallback onTap, + required this.index, + VoidCallback? onTap, }) : _onTap = onTap; @override @@ -571,14 +576,14 @@ abstract class _ListItem implements SelectableItem { final int index; - final VoidCallback _onTap; + final VoidCallback? _onTap; } abstract class _FileListItem extends _ListItem { _FileListItem({ - @required int index, - @required this.file, - VoidCallback onTap, + required int index, + required this.file, + VoidCallback? onTap, }) : super( index: index, onTap: onTap, @@ -589,11 +594,11 @@ abstract class _FileListItem extends _ListItem { class _ImageListItem extends _FileListItem { _ImageListItem({ - @required int index, - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, + required int index, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, }) : super( index: index, file: file, @@ -615,11 +620,11 @@ class _ImageListItem extends _FileListItem { class _VideoListItem extends _FileListItem { _VideoListItem({ - @required int index, - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, + required int index, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, }) : super( index: index, file: file, diff --git a/lib/widget/fancy_option_picker.dart b/lib/widget/fancy_option_picker.dart index cc6336f7..7f22dbf2 100644 --- a/lib/widget/fancy_option_picker.dart +++ b/lib/widget/fancy_option_picker.dart @@ -3,28 +3,28 @@ import 'package:flutter/widgets.dart'; class FancyOptionPickerItem { FancyOptionPickerItem({ - @required this.label, + required this.label, this.isSelected = false, this.onSelect, }); String label; bool isSelected; - VoidCallback onSelect; + VoidCallback? onSelect; } /// A fancy looking dialog to pick an option class FancyOptionPicker extends StatelessWidget { FancyOptionPicker({ - Key key, + Key? key, this.title, - @required this.items, + required this.items, }) : super(key: key); @override build(BuildContext context) { return SimpleDialog( - title: title != null ? Text(title) : null, + title: title != null ? Text(title!) : null, children: items .map((e) => SimpleDialogOption( child: ListTile( @@ -47,6 +47,6 @@ class FancyOptionPicker extends StatelessWidget { ); } - final String title; + final String? title; final List items; } diff --git a/lib/widget/home.dart b/lib/widget/home.dart index 8e32724d..084d7698 100644 --- a/lib/widget/home.dart +++ b/lib/widget/home.dart @@ -16,12 +16,16 @@ class HomeArguments { class Home extends StatefulWidget { static const routeName = "/home"; + static Route buildRoute(HomeArguments args) => MaterialPageRoute( + builder: (context) => Home.fromArgs(args), + ); + Home({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - Home.fromArgs(HomeArguments args, {Key key}) + Home.fromArgs(HomeArguments args, {Key? key}) : this( account: args.account, ); @@ -33,12 +37,6 @@ class Home extends StatefulWidget { } class _HomeState extends State { - @override - initState() { - super.initState(); - _pageController = PageController(initialPage: 0, keepPage: false); - } - @override build(BuildContext context) { return AppTheme( @@ -54,11 +52,11 @@ class _HomeState extends State { items: [ BottomNavigationBarItem( icon: const Icon(Icons.photo_outlined), - label: AppLocalizations.of(context).photosTabLabel, + label: AppLocalizations.of(context)!.photosTabLabel, ), BottomNavigationBarItem( icon: const Icon(Icons.photo_album_outlined), - label: AppLocalizations.of(context).albumsTabLabel, + label: AppLocalizations.of(context)!.albumsTabLabel, ), ], currentIndex: _nextPage, @@ -108,6 +106,6 @@ class _HomeState extends State { }); } - PageController _pageController; + final _pageController = PageController(initialPage: 0, keepPage: false); int _nextPage = 0; } diff --git a/lib/widget/home_albums.dart b/lib/widget/home_albums.dart index 6b324f3d..44107a77 100644 --- a/lib/widget/home_albums.dart +++ b/lib/widget/home_albums.dart @@ -32,8 +32,8 @@ import 'package:tuple/tuple.dart'; class HomeAlbums extends StatefulWidget { HomeAlbums({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); @override @@ -83,7 +83,7 @@ class _HomeAlbumsState extends State _reqQuery(); } else { // process the current state - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { setState(() { _onStateChange(context, _bloc.state); }); @@ -149,12 +149,12 @@ class _HomeAlbumsState extends State }); }, ), - title: Text(AppLocalizations.of(context) + title: Text(AppLocalizations.of(context)! .selectionAppBarTitle(_selectedItems.length)), actions: [ IconButton( icon: const Icon(Icons.delete), - tooltip: AppLocalizations.of(context).deleteSelectedTooltip, + tooltip: AppLocalizations.of(context)!.deleteSelectedTooltip, onPressed: () { _onSelectionAppBarDeletePressed(); }, @@ -171,13 +171,13 @@ class _HomeAlbumsState extends State IconButton( onPressed: () => _onSearchPressed(context), icon: const Icon(Icons.search), - tooltip: AppLocalizations.of(context).searchTooltip, + tooltip: AppLocalizations.of(context)!.searchTooltip, ), ], menuActions: [ PopupMenuItem( value: _menuValueImport, - child: Text(AppLocalizations.of(context).importFoldersTooltip), + child: Text(AppLocalizations.of(context)!.importFoldersTooltip), ), ], onSelectedMenuActions: (option) { @@ -225,7 +225,7 @@ class _HomeAlbumsState extends State ), ), ), - title: AppLocalizations.of(context).albumArchiveLabel, + title: AppLocalizations.of(context)!.albumArchiveLabel, onTap: () { Navigator.of(context).pushNamed(ArchiveViewer.routeName, arguments: ArchiveViewerArguments(widget.account)); @@ -247,7 +247,7 @@ class _HomeAlbumsState extends State ), ), ), - title: AppLocalizations.of(context).createAlbumTooltip, + title: AppLocalizations.of(context)!.createAlbumTooltip, onTap: () => _onNewAlbumItemTap(context), ); } @@ -324,7 +324,7 @@ class _HomeAlbumsState extends State "[_onNewAlbumItemTap] Failed while showDialog", e, stacktrace); SnackBarManager().showSnackBar(SnackBar( content: - Text(AppLocalizations.of(context).createAlbumFailureNotification), + Text(AppLocalizations.of(context)!.createAlbumFailureNotification), duration: k.snackBarDurationNormal, )); }); @@ -337,11 +337,12 @@ class _HomeAlbumsState extends State Future _onSelectionAppBarDeletePressed() async { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .deleteSelectedProcessingNotification(_selectedItems.length)), duration: k.snackBarDurationShort, )); - final selectedFiles = _selectedItems.map((e) => e.album.albumFile).toList(); + final selectedFiles = + _selectedItems.map((e) => e.album.albumFile!).toList(); setState(() { _selectedItems.clear(); }); @@ -363,12 +364,12 @@ class _HomeAlbumsState extends State if (failures.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).deleteSelectedSuccessNotification), + AppLocalizations.of(context)!.deleteSelectedSuccessNotification), duration: k.snackBarDurationNormal, )); } else { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .deleteSelectedFailureNotification(failures.length)), duration: k.snackBarDurationNormal, )); @@ -411,12 +412,12 @@ class _HomeAlbumsState extends State .map((from) { try { return _items.whereType<_GridItem>().firstWhere( - (to) => from.album.albumFile.path == to.album.albumFile.path); + (to) => from.album.albumFile!.path == to.album.albumFile!.path); } catch (_) { return null; } }) - .where((element) => element != null) + .whereType<_GridItem>() .toList(); _selectedItems ..clear() @@ -439,7 +440,7 @@ class _HomeAlbumsState extends State bool get _isSelectionMode => _selectedItems.isNotEmpty; - ListAlbumBloc _bloc; + late ListAlbumBloc _bloc; final _items = <_GridItem>[]; final _selectedItems = <_GridItem>[]; diff --git a/lib/widget/home_app_bar.dart b/lib/widget/home_app_bar.dart index 7e7f6aad..8b795433 100644 --- a/lib/widget/home_app_bar.dart +++ b/lib/widget/home_app_bar.dart @@ -13,8 +13,8 @@ import 'package:nc_photos/widget/settings.dart'; /// AppBar for home screens class HomeSliverAppBar extends StatelessWidget { HomeSliverAppBar({ - Key key, - @required this.account, + Key? key, + required this.account, this.actions, this.menuActions, this.onSelectedMenuActions, @@ -90,7 +90,7 @@ class HomeSliverAppBar extends StatelessWidget { inactiveThumbImage: const AssetImage("assets/ic_dark_mode_switch_24dp.png"), ), - PopupMenuButton( + PopupMenuButton( tooltip: MaterialLocalizations.of(context).moreButtonTooltip, itemBuilder: (context) => (menuActions ?? []) + @@ -98,7 +98,7 @@ class HomeSliverAppBar extends StatelessWidget { PopupMenuItem( value: _menuValueAbout, child: - Text(AppLocalizations.of(context).settingsMenuLabel), + Text(AppLocalizations.of(context)!.settingsMenuLabel), ), ], onSelected: (option) { @@ -125,12 +125,12 @@ class HomeSliverAppBar extends StatelessWidget { final Account account; /// Screen specific action buttons - final List actions; + final List? actions; /// Screen specific actions under the overflow menu. The value of each item /// much >= 0 - final List> menuActions; - final void Function(int) onSelectedMenuActions; + final List>? menuActions; + final void Function(int)? onSelectedMenuActions; static const _menuValueAbout = -1; } diff --git a/lib/widget/home_photos.dart b/lib/widget/home_photos.dart index d63e272e..1253a033 100644 --- a/lib/widget/home_photos.dart +++ b/lib/widget/home_photos.dart @@ -41,8 +41,8 @@ import 'package:nc_photos/widget/viewer.dart'; class HomePhotos extends StatefulWidget { HomePhotos({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); @override @@ -59,7 +59,7 @@ class _HomePhotosState extends State @override initState() { super.initState(); - _thumbZoomLevel = Pref.inst().getHomePhotosZoomLevel(0); + _thumbZoomLevel = Pref.inst().getHomePhotosZoomLevelOr(0); _initBloc(); } @@ -82,7 +82,7 @@ class _HomePhotosState extends State _reqQuery(); } else { // process the current state - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { setState(() { _onStateChange(context, _bloc.state); }); @@ -162,35 +162,36 @@ class _HomePhotosState extends State }); }, ), - title: Text(AppLocalizations.of(context) + title: Text(AppLocalizations.of(context)! .selectionAppBarTitle(selectedListItems.length)), actions: [ if (platform_k.isAndroid) IconButton( icon: const Icon(Icons.share), - tooltip: AppLocalizations.of(context).shareSelectedTooltip, + tooltip: AppLocalizations.of(context)!.shareSelectedTooltip, onPressed: () { _onSelectionAppBarSharePressed(context); }, ), IconButton( icon: const Icon(Icons.playlist_add), - tooltip: AppLocalizations.of(context).addSelectedToAlbumTooltip, + tooltip: AppLocalizations.of(context)!.addSelectedToAlbumTooltip, onPressed: () { _onSelectionAppBarAddToAlbumPressed(context); }, ), - PopupMenuButton( + PopupMenuButton<_SelectionAppBarMenuOption>( tooltip: MaterialLocalizations.of(context).moreButtonTooltip, itemBuilder: (context) => [ PopupMenuItem( value: _SelectionAppBarMenuOption.archive, - child: - Text(AppLocalizations.of(context).archiveSelectedMenuLabel), + child: Text( + AppLocalizations.of(context)!.archiveSelectedMenuLabel), ), PopupMenuItem( value: _SelectionAppBarMenuOption.delete, - child: Text(AppLocalizations.of(context).deleteSelectedTooltip), + child: + Text(AppLocalizations.of(context)!.deleteSelectedTooltip), ), ], onSelected: (option) { @@ -212,7 +213,7 @@ class _HomePhotosState extends State actions: [ PopupMenuButton( icon: const Icon(Icons.photo_size_select_large), - tooltip: AppLocalizations.of(context).zoomTooltip, + tooltip: AppLocalizations.of(context)!.zoomTooltip, itemBuilder: (context) => [ PopupMenuZoom( initialValue: _thumbZoomLevel, @@ -231,7 +232,7 @@ class _HomePhotosState extends State menuActions: [ PopupMenuItem( value: _menuValueRefresh, - child: Text(AppLocalizations.of(context).refreshMenuLabel), + child: Text(AppLocalizations.of(context)!.refreshMenuLabel), ), ], onSelectedMenuActions: (option) { @@ -251,7 +252,7 @@ class _HomePhotosState extends State } else if (state is ScanDirBlocSuccess || state is ScanDirBlocLoading) { _transformItems(state.files); if (state is ScanDirBlocSuccess) { - if (Pref.inst().isEnableExif() && !_hasFiredMetadataTask.value) { + if (Pref.inst().isEnableExifOr() && !_hasFiredMetadataTask.value) { KiwiContainer() .resolve() .addTask(MetadataTask(widget.account)); @@ -306,14 +307,14 @@ class _HomePhotosState extends State clearSelectedItems(); }); SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .addSelectedToAlbumSuccessNotification(value.name)), duration: k.snackBarDurationNormal, )); }).catchError((_) {}); } else { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .addSelectedToAlbumFailureNotification), duration: k.snackBarDurationNormal, )); @@ -325,7 +326,7 @@ class _HomePhotosState extends State stacktrace); SnackBarManager().showSnackBar(SnackBar( content: Text( - "${AppLocalizations.of(context).addSelectedToAlbumFailureNotification}: " + "${AppLocalizations.of(context)!.addSelectedToAlbumFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -355,7 +356,7 @@ class _HomePhotosState extends State "[_addSelectedToAlbum] Failed while updating album", e, stacktrace); SnackBarManager().showSnackBar(SnackBar( content: Text( - "${AppLocalizations.of(context).addSelectedToAlbumFailureNotification}: " + "${AppLocalizations.of(context)!.addSelectedToAlbumFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -365,7 +366,7 @@ class _HomePhotosState extends State Future _onSelectionAppBarDeletePressed(BuildContext context) async { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .deleteSelectedProcessingNotification(selectedListItems.length)), duration: k.snackBarDurationShort, )); @@ -394,12 +395,12 @@ class _HomePhotosState extends State if (failures.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).deleteSelectedSuccessNotification), + AppLocalizations.of(context)!.deleteSelectedSuccessNotification), duration: k.snackBarDurationNormal, )); } else { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .deleteSelectedFailureNotification(failures.length)), duration: k.snackBarDurationNormal, )); @@ -425,7 +426,7 @@ class _HomePhotosState extends State Future _onSelectionAppBarArchivePressed(BuildContext context) async { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .archiveSelectedProcessingNotification(selectedListItems.length)), duration: k.snackBarDurationShort, )); @@ -454,12 +455,12 @@ class _HomePhotosState extends State if (failures.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).archiveSelectedSuccessNotification), + AppLocalizations.of(context)!.archiveSelectedSuccessNotification), duration: k.snackBarDurationNormal, )); } else { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .archiveSelectedFailureNotification(failures.length)), duration: k.snackBarDurationNormal, )); @@ -478,16 +479,16 @@ class _HomePhotosState extends State file_util.isSupportedFormat(element) && element.isArchived != true) .sorted(compareFileDateTimeDescending); - DateTime currentDate; + DateTime? currentDate; final isMonthOnly = _thumbZoomLevel < 0; itemStreamListItems = () sync* { for (int i = 0; i < _backingFiles.length; ++i) { final f = _backingFiles[i]; - final newDate = f.bestDateTime?.toLocal(); - if (newDate?.year != currentDate?.year || - newDate?.month != currentDate?.month || - (!isMonthOnly && newDate?.day != currentDate?.day)) { + final newDate = f.bestDateTime.toLocal(); + if (newDate.year != currentDate?.year || + newDate.month != currentDate?.month || + (!isMonthOnly && newDate.day != currentDate?.day)) { yield _DateListItem(date: newDate, isMonthOnly: isMonthOnly); currentDate = newDate; } @@ -546,13 +547,13 @@ class _HomePhotosState extends State } /// Return the estimated scroll extent of the custom scroll view, or null - double _getScrollViewExtent(BoxConstraints constraints) { + double? _getScrollViewExtent(BoxConstraints constraints) { if (_itemListMaxExtent != null && constraints.hasBoundedHeight && _appBarExtent != null) { // scroll extent = list height - widget viewport height + sliver app bar height + list padding final scrollExtent = - _itemListMaxExtent - constraints.maxHeight + _appBarExtent + 16; + _itemListMaxExtent! - constraints.maxHeight + _appBarExtent! + 16; _log.info( "[_getScrollViewExtent] $_itemListMaxExtent - ${constraints.maxHeight} + $_appBarExtent + 16 = $scrollExtent"); return scrollExtent; @@ -595,7 +596,7 @@ class _HomePhotosState extends State } } - ScanDirBloc _bloc; + late ScanDirBloc _bloc; var _backingFiles = []; @@ -603,8 +604,8 @@ class _HomePhotosState extends State final ScrollController _scrollController = ScrollController(); - double _appBarExtent; - double _itemListMaxExtent; + double? _appBarExtent; + double? _itemListMaxExtent; static final _log = Logger("widget.home_photos._HomePhotosState"); static const _menuValueRefresh = 0; @@ -612,7 +613,7 @@ class _HomePhotosState extends State abstract class _ListItem implements SelectableItem { _ListItem({ - VoidCallback onTap, + VoidCallback? onTap, }) : _onTap = onTap; @override @@ -624,12 +625,12 @@ abstract class _ListItem implements SelectableItem { @override get staggeredTile => const StaggeredTile.count(1, 1); - final VoidCallback _onTap; + final VoidCallback? _onTap; } class _DateListItem extends _ListItem { _DateListItem({ - @required this.date, + required this.date, this.isMonthOnly = false, }); @@ -647,7 +648,7 @@ class _DateListItem extends _ListItem { isMonthOnly ? DateFormat.YEAR_MONTH : DateFormat.YEAR_MONTH_DAY; subtitle = DateFormat(pattern, Localizations.localeOf(context).languageCode) - .format(date.toLocal()); + .format(date!.toLocal()); } return Align( alignment: AlignmentDirectional.centerStart, @@ -655,7 +656,7 @@ class _DateListItem extends _ListItem { padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( subtitle, - style: Theme.of(context).textTheme.caption.copyWith( + style: Theme.of(context).textTheme.caption!.copyWith( color: AppTheme.getPrimaryTextColor(context), fontWeight: FontWeight.bold, ), @@ -664,14 +665,14 @@ class _DateListItem extends _ListItem { ); } - final DateTime date; + final DateTime? date; final bool isMonthOnly; } abstract class _FileListItem extends _ListItem { _FileListItem({ - @required this.file, - VoidCallback onTap, + required this.file, + VoidCallback? onTap, }) : super(onTap: onTap); @override @@ -687,10 +688,10 @@ abstract class _FileListItem extends _ListItem { class _ImageListItem extends _FileListItem { _ImageListItem({ - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, }) : super(file: file, onTap: onTap); @override @@ -708,10 +709,10 @@ class _ImageListItem extends _FileListItem { class _VideoListItem extends _FileListItem { _VideoListItem({ - @required File file, - @required this.account, - @required this.previewUrl, - VoidCallback onTap, + required File file, + required this.account, + required this.previewUrl, + VoidCallback? onTap, }) : super(file: file, onTap: onTap); @override diff --git a/lib/widget/image_viewer.dart b/lib/widget/image_viewer.dart index 42fcd75c..4c176917 100644 --- a/lib/widget/image_viewer.dart +++ b/lib/widget/image_viewer.dart @@ -11,9 +11,9 @@ import 'package:nc_photos/widget/cached_network_image_mod.dart' as mod; class ImageViewer extends StatefulWidget { ImageViewer({ - @required this.account, - @required this.file, - this.canZoom, + required this.account, + required this.file, + required this.canZoom, this.onLoaded, this.onHeightChanged, this.onZoomStarted, @@ -35,10 +35,10 @@ class ImageViewer extends StatefulWidget { final Account account; final File file; final bool canZoom; - final VoidCallback onLoaded; - final void Function(double height) onHeightChanged; - final VoidCallback onZoomStarted; - final VoidCallback onZoomEnded; + final VoidCallback? onLoaded; + final ValueChanged? onHeightChanged; + final VoidCallback? onZoomStarted; + final VoidCallback? onZoomEnded; } class _ImageViewerState extends State @@ -58,9 +58,9 @@ class _ImageViewerState extends State alignment: Alignment.center, child: NotificationListener( onNotification: (_) { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { if (_key.currentContext != null) { - widget.onHeightChanged?.call(_key.currentContext.size.height); + widget.onHeightChanged?.call(_key.currentContext!.size!.height); } }); return false; @@ -78,7 +78,7 @@ class _ImageViewerState extends State filterQuality: FilterQuality.high, imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, imageBuilder: (context, child, imageProvider) { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { _onItemLoaded(); }); SizeChangedLayoutNotification().dispatch(context); diff --git a/lib/widget/measurable_item_list.dart b/lib/widget/measurable_item_list.dart index bb377cf1..69879d28 100644 --- a/lib/widget/measurable_item_list.dart +++ b/lib/widget/measurable_item_list.dart @@ -10,11 +10,11 @@ abstract class MeasurableItemListState { class MeasurableItemList extends StatefulWidget { MeasurableItemList({ - Key key, - @required this.maxCrossAxisExtent, - @required this.itemCount, - @required this.itemBuilder, - @required this.staggeredTileBuilder, + Key? key, + required this.maxCrossAxisExtent, + required this.itemCount, + required this.itemBuilder, + required this.staggeredTileBuilder, this.onMaxExtentChanged, }) : super(key: key); @@ -25,7 +25,7 @@ class MeasurableItemList extends StatefulWidget { final int itemCount; final IndexedWidgetBuilder itemBuilder; final IndexedStaggeredTileBuilder staggeredTileBuilder; - final ValueChanged onMaxExtentChanged; + final ValueChanged? onMaxExtentChanged; } class _MeasurableItemListState extends State @@ -35,21 +35,21 @@ class _MeasurableItemListState extends State initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { _prevOrientation = MediaQuery.of(context).orientation; - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance!.addObserver(this); }); } @override dispose() { - WidgetsBinding.instance.removeObserver(this); + WidgetsBinding.instance!.removeObserver(this); super.dispose(); } @override didChangeMetrics() { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { final orientation = MediaQuery.of(context).orientation; if (orientation != _prevOrientation) { _log.info( @@ -70,7 +70,8 @@ class _MeasurableItemListState extends State } if (constraints.crossAxisExtent != _prevListWidth) { _log.info("[build] updateListHeight: list viewport width changed"); - WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); + WidgetsBinding.instance! + .addPostFrameCallback((_) => updateListHeight()); _prevListWidth = constraints.crossAxisExtent; } @@ -78,7 +79,8 @@ class _MeasurableItemListState extends State final cellSize = widget.maxCrossAxisExtent; if (cellSize != _prevCellSize) { _log.info("[build] updateListHeight: cell size changed"); - WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); + WidgetsBinding.instance! + .addPostFrameCallback((_) => updateListHeight()); _prevCellSize = cellSize; } _gridKey = _GridKey("$_uniqueToken $cellSize"); @@ -94,9 +96,9 @@ class _MeasurableItemListState extends State @override updateListHeight() { - double newMaxExtent; + double? newMaxExtent; try { - final renderObj = _gridKey.currentContext.findRenderObject() + final renderObj = _gridKey.currentContext!.findRenderObject() as RenderMeasurableSliverStaggeredGrid; final maxExtent = renderObj.calculateExtent(); _log.info("[updateListHeight] Max extent: $maxExtent"); @@ -118,14 +120,14 @@ class _MeasurableItemListState extends State } } - double _prevListWidth; - double _prevCellSize; - double _maxExtent; - Orientation _prevOrientation; + double? _prevListWidth; + double? _prevCellSize; + double? _maxExtent; + Orientation? _prevOrientation; // this unique token is there to keep the global key unique final _uniqueToken = Uuid().v4(); - GlobalObjectKey _gridKey; + late GlobalObjectKey _gridKey; static final _log = Logger("widget.measurable_item_list._MeasurableItemListState"); diff --git a/lib/widget/measure.dart b/lib/widget/measure.dart index 9575c4e3..d82fb76c 100644 --- a/lib/widget/measure.dart +++ b/lib/widget/measure.dart @@ -8,9 +8,9 @@ class MeasureSize extends SingleChildRenderObjectWidget { final OnWidgetSizeChanged onChange; const MeasureSize({ - Key key, - @required this.onChange, - @required Widget child, + Key? key, + required this.onChange, + required Widget child, }) : super(key: key, child: child); @override @@ -20,7 +20,7 @@ class MeasureSize extends SingleChildRenderObjectWidget { } class _MeasureSizeRenderObject extends RenderProxyBox { - Size oldSize; + Size? oldSize; final OnWidgetSizeChanged onChange; _MeasureSizeRenderObject(this.onChange); @@ -29,11 +29,11 @@ class _MeasureSizeRenderObject extends RenderProxyBox { void performLayout() { super.performLayout(); - Size newSize = child.size; - if (oldSize == newSize) return; + var newSize = child?.size; + if (newSize == null || oldSize == newSize) return; oldSize = newSize; - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { onChange(newSize); }); } @@ -41,9 +41,9 @@ class _MeasureSizeRenderObject extends RenderProxyBox { class SliverMeasureExtent extends SingleChildRenderObjectWidget { const SliverMeasureExtent({ - Key key, - @required this.onChange, - @required Widget child, + Key? key, + required this.onChange, + required Widget child, }) : super(key: key, child: child); @override @@ -61,16 +61,16 @@ class _SliverMeasureExtentRenderObject extends RenderProxySliver { void performLayout() { super.performLayout(); - double newExent = child.geometry.scrollExtent; - if (_oldExtent == newExent) { + var newExent = child?.geometry?.scrollExtent; + if (newExent == null || _oldExtent == newExent) { return; } _oldExtent = newExent; - WidgetsBinding.instance.addPostFrameCallback((_) => onChange(newExent)); + WidgetsBinding.instance!.addPostFrameCallback((_) => onChange(newExent)); } final void Function(double) onChange; - double _oldExtent; + double? _oldExtent; } diff --git a/lib/widget/measureable_sliver_staggered_grid.dart b/lib/widget/measureable_sliver_staggered_grid.dart index 415084f5..988f7b29 100644 --- a/lib/widget/measureable_sliver_staggered_grid.dart +++ b/lib/widget/measureable_sliver_staggered_grid.dart @@ -5,11 +5,11 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; // ignore: must_be_immutable class MeasurableSliverStaggeredGrid extends SliverStaggeredGrid { MeasurableSliverStaggeredGrid.extentBuilder({ - Key key, - @required double maxCrossAxisExtent, - @required IndexedStaggeredTileBuilder staggeredTileBuilder, - @required IndexedWidgetBuilder itemBuilder, - @required int itemCount, + Key? key, + required double maxCrossAxisExtent, + required IndexedStaggeredTileBuilder staggeredTileBuilder, + required IndexedWidgetBuilder itemBuilder, + required int itemCount, double mainAxisSpacing = 0, double crossAxisSpacing = 0, }) : super( @@ -32,19 +32,19 @@ class MeasurableSliverStaggeredGrid extends SliverStaggeredGrid { final element = context as SliverVariableSizeBoxAdaptorElement; _renderObject = RenderMeasurableSliverStaggeredGrid( childManager: element, gridDelegate: gridDelegate); - return _renderObject; + return _renderObject!; } - RenderMeasurableSliverStaggeredGrid get renderObject => _renderObject; + RenderMeasurableSliverStaggeredGrid? get renderObject => _renderObject; - RenderMeasurableSliverStaggeredGrid _renderObject; + RenderMeasurableSliverStaggeredGrid? _renderObject; } class RenderMeasurableSliverStaggeredGrid extends RenderSliverStaggeredGrid with WidgetsBindingObserver { RenderMeasurableSliverStaggeredGrid({ - @required RenderSliverVariableSizeBoxChildManager childManager, - @required SliverStaggeredGridDelegate gridDelegate, + required RenderSliverVariableSizeBoxChildManager childManager, + required SliverStaggeredGridDelegate gridDelegate, }) : super(childManager: childManager, gridDelegate: gridDelegate); /// Calculate the height of this staggered grid view @@ -69,13 +69,13 @@ class RenderMeasurableSliverStaggeredGrid extends RenderSliverStaggeredGrid } final bool hasTrailingScrollOffset = geometry.hasTrailingScrollOffset; - RenderBox child; + RenderBox? child; if (!hasTrailingScrollOffset) { // Layout the child to compute its tailingScrollOffset. final constraints = BoxConstraints.tightFor(width: geometry.crossAxisExtent); child = addAndLayoutChild(index, constraints, parentUsesSize: true); - geometry = geometry.copyWith(mainAxisExtent: paintExtentOf(child)); + geometry = geometry.copyWith(mainAxisExtent: paintExtentOf(child!)); } if (child != null) { diff --git a/lib/widget/my_app.dart b/lib/widget/my_app.dart index d76e30ad..93e6bf0d 100644 --- a/lib/widget/my_app.dart +++ b/lib/widget/my_app.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:logging/logging.dart'; +import 'package:nc_photos/account.dart'; import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/language_util.dart' as language_util; import 'package:nc_photos/pref.dart'; @@ -44,10 +45,11 @@ class _MyAppState extends State implements SnackBarHandler { @override build(BuildContext context) { return MaterialApp( - onGenerateTitle: (context) => AppLocalizations.of(context).appTitle, + onGenerateTitle: (context) => AppLocalizations.of(context)!.appTitle, theme: _getLightTheme(), darkTheme: _getDarkTheme(), - themeMode: Pref.inst().isDarkTheme() ? ThemeMode.dark : ThemeMode.light, + themeMode: + Pref.inst().isDarkThemeOr(false) ? ThemeMode.dark : ThemeMode.light, initialRoute: Splash.routeName, onGenerateRoute: _onGenerateRoute, navigatorObservers: [MyApp.routeObserver], @@ -87,9 +89,9 @@ class _MyAppState extends State implements SnackBarHandler { Splash.routeName: (context) => Splash(), }; - Route _onGenerateRoute(RouteSettings settings) { + Route? _onGenerateRoute(RouteSettings settings) { _log.info("[_onGenerateRoute] Route: ${settings.name}"); - Route route; + Route? route; route ??= _handleBasicRoute(settings); route ??= _handleViewerRoute(settings); route ??= _handleConnectRoute(settings); @@ -112,7 +114,7 @@ class _MyAppState extends State implements SnackBarHandler { setState(() {}); } - Route _handleBasicRoute(RouteSettings settings) { + Route? _handleBasicRoute(RouteSettings settings) { for (final e in _getRouter().entries) { if (e.key == settings.name) { return MaterialPageRoute( @@ -123,13 +125,11 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleViewerRoute(RouteSettings settings) { + Route? _handleViewerRoute(RouteSettings settings) { try { if (settings.name == Viewer.routeName && settings.arguments != null) { - final ViewerArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => Viewer.fromArgs(args), - ); + final args = settings.arguments as ViewerArguments; + return Viewer.buildRoute(args); } } catch (e) { _log.severe("[_handleViewerRoute] Failed while handling route", e); @@ -137,13 +137,11 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleConnectRoute(RouteSettings settings) { + Route? _handleConnectRoute(RouteSettings settings) { try { if (settings.name == Connect.routeName && settings.arguments != null) { - final ConnectArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => Connect.fromArgs(args), - ); + final args = settings.arguments as ConnectArguments; + return Connect.buildRoute(args); } } catch (e) { _log.severe("[_handleConnectRoute] Failed while handling route", e); @@ -151,13 +149,11 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleHomeRoute(RouteSettings settings) { + Route? _handleHomeRoute(RouteSettings settings) { try { if (settings.name == Home.routeName && settings.arguments != null) { - final HomeArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => Home.fromArgs(args), - ); + final args = settings.arguments as HomeArguments; + return Home.buildRoute(args); } } catch (e) { _log.severe("[_handleHomeRoute] Failed while handling route", e); @@ -165,13 +161,11 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleRootPickerRoute(RouteSettings settings) { + Route? _handleRootPickerRoute(RouteSettings settings) { try { if (settings.name == RootPicker.routeName && settings.arguments != null) { - final RootPickerArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => RootPicker.fromArgs(args), - ); + final args = settings.arguments as RootPickerArguments; + return RootPicker.buildRoute(args); } } catch (e) { _log.severe("[_handleRootPickerRoute] Failed while handling route", e); @@ -179,14 +173,12 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleAlbumViewerRoute(RouteSettings settings) { + Route? _handleAlbumViewerRoute(RouteSettings settings) { try { if (settings.name == AlbumViewer.routeName && settings.arguments != null) { - final AlbumViewerArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => AlbumViewer.fromArgs(args), - ); + final args = settings.arguments as AlbumViewerArguments; + return AlbumViewer.buildRoute(args); } } catch (e) { _log.severe("[_handleAlbumViewerRoute] Failed while handling route", e); @@ -194,13 +186,11 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleSettingsRoute(RouteSettings settings) { + Route? _handleSettingsRoute(RouteSettings settings) { try { if (settings.name == Settings.routeName && settings.arguments != null) { - final SettingsArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => Settings.fromArgs(args), - ); + final args = settings.arguments as SettingsArguments; + return Settings.buildRoute(args); } } catch (e) { _log.severe("[_handleSettingsRoute] Failed while handling route", e); @@ -208,14 +198,12 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleArchiveViewerRoute(RouteSettings settings) { + Route? _handleArchiveViewerRoute(RouteSettings settings) { try { if (settings.name == ArchiveViewer.routeName && settings.arguments != null) { - final ArchiveViewerArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => ArchiveViewer.fromArgs(args), - ); + final args = settings.arguments as ArchiveViewerArguments; + return ArchiveViewer.buildRoute(args); } } catch (e) { _log.severe("[_handleArchiveViewerRoute] Failed while handling route", e); @@ -223,14 +211,12 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleDynamicAlbumViewerRoute(RouteSettings settings) { + Route? _handleDynamicAlbumViewerRoute(RouteSettings settings) { try { if (settings.name == DynamicAlbumViewer.routeName && settings.arguments != null) { - final DynamicAlbumViewerArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => DynamicAlbumViewer.fromArgs(args), - ); + final args = settings.arguments as DynamicAlbumViewerArguments; + return DynamicAlbumViewer.buildRoute(args); } } catch (e) { _log.severe( @@ -239,14 +225,12 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleAlbumDirPickerRoute(RouteSettings settings) { + Route? _handleAlbumDirPickerRoute(RouteSettings settings) { try { if (settings.name == AlbumDirPicker.routeName && settings.arguments != null) { - final AlbumDirPickerArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => AlbumDirPicker.fromArgs(args), - ); + final args = settings.arguments as AlbumDirPickerArguments; + return AlbumDirPicker.buildRoute(args); } } catch (e) { _log.severe( @@ -255,14 +239,12 @@ class _MyAppState extends State implements SnackBarHandler { return null; } - Route _handleAlbumImporterRoute(RouteSettings settings) { + Route? _handleAlbumImporterRoute(RouteSettings settings) { try { if (settings.name == AlbumImporter.routeName && settings.arguments != null) { - final AlbumImporterArguments args = settings.arguments; - return MaterialPageRoute( - builder: (context) => AlbumImporter.fromArgs(args), - ); + final args = settings.arguments as AlbumImporterArguments; + return AlbumImporter.buildRoute(args); } } catch (e) { _log.severe("[_handleAlbumImporterRoute] Failed while handling route", e); @@ -272,8 +254,8 @@ class _MyAppState extends State implements SnackBarHandler { final _scaffoldMessengerKey = GlobalKey(); - AppEventListener _themeChangedListener; - AppEventListener _langChangedListener; + late AppEventListener _themeChangedListener; + late AppEventListener _langChangedListener; static final _log = Logger("widget.my_app.MyAppState"); } diff --git a/lib/widget/new_album_dialog.dart b/lib/widget/new_album_dialog.dart index 7c22cbc7..13f216ef 100644 --- a/lib/widget/new_album_dialog.dart +++ b/lib/widget/new_album_dialog.dart @@ -7,6 +7,7 @@ import 'package:nc_photos/entity/album.dart'; import 'package:nc_photos/entity/album/cover_provider.dart'; import 'package:nc_photos/entity/album/provider.dart'; import 'package:nc_photos/entity/album/sort_provider.dart'; +import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/use_case/create_album.dart'; import 'package:nc_photos/widget/album_dir_picker.dart'; @@ -16,8 +17,8 @@ import 'package:nc_photos/widget/album_dir_picker.dart'; /// cancelled class NewAlbumDialog extends StatefulWidget { NewAlbumDialog({ - Key key, - @required this.account, + Key? key, + required this.account, this.isAllowDynamic = true, }) : super(key: key); @@ -39,7 +40,7 @@ class _NewAlbumDialogState extends State { return Visibility( visible: _isVisible, child: AlertDialog( - title: Text(AppLocalizations.of(context).createAlbumTooltip), + title: Text(AppLocalizations.of(context)!.createAlbumTooltip), content: Form( key: _formKey, child: Container( @@ -50,17 +51,17 @@ class _NewAlbumDialogState extends State { children: [ TextFormField( decoration: InputDecoration( - hintText: AppLocalizations.of(context).nameInputHint, + hintText: AppLocalizations.of(context)!.nameInputHint, ), validator: (value) { - if (value.isEmpty) { - return AppLocalizations.of(context) + if (value!.isEmpty) { + return AppLocalizations.of(context)! .albumNameInputInvalidEmpty; } return null; }, onSaved: (value) { - _formValue.name = value; + _formValue.name = value!; }, ), if (widget.isAllowDynamic) ...[ @@ -75,7 +76,7 @@ class _NewAlbumDialogState extends State { .toList(), onChanged: (newValue) { setState(() { - _provider = newValue; + _provider = newValue!; }); }, onSaved: (value) { @@ -104,8 +105,8 @@ class _NewAlbumDialogState extends State { } void _onOkPressed(BuildContext context) { - if (_formKey.currentState.validate()) { - _formKey.currentState.save(); + if (_formKey.currentState?.validate() == true) { + _formKey.currentState!.save(); if (_formValue.provider == _Provider.static || _formValue.provider == null) { _onConfirmStaticAlbum(); @@ -136,7 +137,7 @@ class _NewAlbumDialogState extends State { _isVisible = false; }); Navigator.of(context) - .pushNamed(AlbumDirPicker.routeName, + .pushNamed>(AlbumDirPicker.routeName, arguments: AlbumDirPickerArguments(widget.account)) .then((value) { if (value == null) { @@ -174,8 +175,8 @@ class _NewAlbumDialogState extends State { } class _FormValue { - String name; - _Provider provider; + late String name; + _Provider? provider; } enum _Provider { @@ -187,10 +188,10 @@ extension on _Provider { String toValueString(BuildContext context) { switch (this) { case _Provider.static: - return AppLocalizations.of(context).createAlbumDialogBasicLabel; + return AppLocalizations.of(context)!.createAlbumDialogBasicLabel; case _Provider.dir: - return AppLocalizations.of(context).createAlbumDialogFolderBasedLabel; + return AppLocalizations.of(context)!.createAlbumDialogFolderBasedLabel; default: throw StateError("Unknown value: $this"); @@ -200,10 +201,10 @@ extension on _Provider { String toDescription(BuildContext context) { switch (this) { case _Provider.static: - return AppLocalizations.of(context).createAlbumDialogBasicDescription; + return AppLocalizations.of(context)!.createAlbumDialogBasicDescription; case _Provider.dir: - return AppLocalizations.of(context) + return AppLocalizations.of(context)! .createAlbumDialogFolderBasedDescription; default: diff --git a/lib/widget/page_visibility_mixin.dart b/lib/widget/page_visibility_mixin.dart index 6eb474ba..d0870c4e 100644 --- a/lib/widget/page_visibility_mixin.dart +++ b/lib/widget/page_visibility_mixin.dart @@ -5,7 +5,7 @@ mixin PageVisibilityMixin on State, RouteAware { @override didChangeDependencies() { super.didChangeDependencies(); - MyApp.routeObserver.subscribe(this, ModalRoute.of(context)); + MyApp.routeObserver.subscribe(this, ModalRoute.of(context)!); } @override diff --git a/lib/widget/photo_date_time_edit_dialog.dart b/lib/widget/photo_date_time_edit_dialog.dart index 4eff52c4..d7ec3eae 100644 --- a/lib/widget/photo_date_time_edit_dialog.dart +++ b/lib/widget/photo_date_time_edit_dialog.dart @@ -6,8 +6,8 @@ import 'package:nc_photos/num_extension.dart'; class PhotoDateTimeEditDialog extends StatefulWidget { PhotoDateTimeEditDialog({ - Key key, - @required this.initialDateTime, + Key? key, + required this.initialDateTime, }) : super(key: key); @override @@ -20,7 +20,7 @@ class _PhotoDateTimeEditDialogState extends State { @override build(BuildContext context) { return AlertDialog( - title: Text(AppLocalizations.of(context).updateDateTimeDialogTitle), + title: Text(AppLocalizations.of(context)!.updateDateTimeDialogTitle), content: Form( key: _formKey, child: Container( @@ -29,7 +29,7 @@ class _PhotoDateTimeEditDialogState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - AppLocalizations.of(context).dateSubtitle, + AppLocalizations.of(context)!.dateSubtitle, style: Theme.of(context).textTheme.subtitle2, ), Row( @@ -38,20 +38,20 @@ class _PhotoDateTimeEditDialogState extends State { child: TextFormField( decoration: InputDecoration( hintText: - AppLocalizations.of(context).dateYearInputHint, + AppLocalizations.of(context)!.dateYearInputHint, ), keyboardType: TextInputType.number, validator: (value) { try { - int.parse(value); + int.parse(value!); return null; } catch (_) { - return AppLocalizations.of(context) + return AppLocalizations.of(context)! .dateTimeInputInvalid; } }, onSaved: (value) { - _formValue.year = int.parse(value); + _formValue.year = int.parse(value!); }, initialValue: "${widget.initialDateTime.year}", ), @@ -62,18 +62,18 @@ class _PhotoDateTimeEditDialogState extends State { child: TextFormField( decoration: InputDecoration( hintText: - AppLocalizations.of(context).dateMonthInputHint, + AppLocalizations.of(context)!.dateMonthInputHint, ), keyboardType: TextInputType.number, validator: (value) { - if (int.tryParse(value)?.inRange(1, 12) == true) { + if (int.tryParse(value!)?.inRange(1, 12) == true) { return null; } - return AppLocalizations.of(context) + return AppLocalizations.of(context)! .dateTimeInputInvalid; }, onSaved: (value) { - _formValue.month = int.parse(value); + _formValue.month = int.parse(value!); }, initialValue: widget.initialDateTime.month .toString() @@ -85,18 +85,19 @@ class _PhotoDateTimeEditDialogState extends State { Flexible( child: TextFormField( decoration: InputDecoration( - hintText: AppLocalizations.of(context).dateDayInputHint, + hintText: + AppLocalizations.of(context)!.dateDayInputHint, ), keyboardType: TextInputType.number, validator: (value) { - if (int.tryParse(value)?.inRange(1, 31) == true) { + if (int.tryParse(value!)?.inRange(1, 31) == true) { return null; } - return AppLocalizations.of(context) + return AppLocalizations.of(context)! .dateTimeInputInvalid; }, onSaved: (value) { - _formValue.day = int.parse(value); + _formValue.day = int.parse(value!); }, initialValue: widget.initialDateTime.day.toString().padLeft(2, "0"), @@ -107,7 +108,7 @@ class _PhotoDateTimeEditDialogState extends State { ), const SizedBox(height: 16), Text( - AppLocalizations.of(context).timeSubtitle, + AppLocalizations.of(context)!.timeSubtitle, style: Theme.of(context).textTheme.subtitle2, ), Row( @@ -116,18 +117,18 @@ class _PhotoDateTimeEditDialogState extends State { child: TextFormField( decoration: InputDecoration( hintText: - AppLocalizations.of(context).timeHourInputHint, + AppLocalizations.of(context)!.timeHourInputHint, ), keyboardType: TextInputType.number, validator: (value) { - if (int.tryParse(value)?.inRange(0, 23) == true) { + if (int.tryParse(value!)?.inRange(0, 23) == true) { return null; } - return AppLocalizations.of(context) + return AppLocalizations.of(context)! .dateTimeInputInvalid; }, onSaved: (value) { - _formValue.hour = int.parse(value); + _formValue.hour = int.parse(value!); }, initialValue: widget.initialDateTime.hour .toString() @@ -140,18 +141,18 @@ class _PhotoDateTimeEditDialogState extends State { child: TextFormField( decoration: InputDecoration( hintText: - AppLocalizations.of(context).timeMinuteInputHint, + AppLocalizations.of(context)!.timeMinuteInputHint, ), keyboardType: TextInputType.number, validator: (value) { - if (int.tryParse(value)?.inRange(0, 59) == true) { + if (int.tryParse(value!)?.inRange(0, 59) == true) { return null; } - return AppLocalizations.of(context) + return AppLocalizations.of(context)! .dateTimeInputInvalid; }, onSaved: (value) { - _formValue.minute = int.parse(value); + _formValue.minute = int.parse(value!); }, initialValue: widget.initialDateTime.minute .toString() @@ -180,8 +181,8 @@ class _PhotoDateTimeEditDialogState extends State { } void _onSavePressed(BuildContext context) { - if (_formKey.currentState.validate()) { - _formKey.currentState.save(); + if (_formKey.currentState?.validate() == true) { + _formKey.currentState!.save(); final d = DateTime(_formValue.year, _formValue.month, _formValue.day, _formValue.hour, _formValue.minute); _log.info("[_onSavePressed] Set date time: $d"); @@ -197,9 +198,9 @@ class _PhotoDateTimeEditDialogState extends State { } class _FormValue { - int year; - int month; - int day; - int hour; - int minute; + late int year; + late int month; + late int day; + late int hour; + late int minute; } diff --git a/lib/widget/photo_list_item.dart b/lib/widget/photo_list_item.dart index 5d98f978..9e20d273 100644 --- a/lib/widget/photo_list_item.dart +++ b/lib/widget/photo_list_item.dart @@ -7,9 +7,9 @@ import 'package:nc_photos/theme.dart'; class PhotoListImage extends StatelessWidget { const PhotoListImage({ - Key key, - @required this.account, - @required this.previewUrl, + Key? key, + required this.account, + required this.previewUrl, this.isGif = false, }) : super(key: key); @@ -72,9 +72,9 @@ class PhotoListImage extends StatelessWidget { class PhotoListVideo extends StatelessWidget { const PhotoListVideo({ - Key key, - @required this.account, - @required this.previewUrl, + Key? key, + required this.account, + required this.previewUrl, }) : super(key: key); @override diff --git a/lib/widget/popup_menu_zoom.dart b/lib/widget/popup_menu_zoom.dart index cfa9e1ed..1b4604ba 100644 --- a/lib/widget/popup_menu_zoom.dart +++ b/lib/widget/popup_menu_zoom.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; class PopupMenuZoom extends PopupMenuEntry { PopupMenuZoom({ - Key key, - @required this.initialValue, - @required this.minValue, - @required this.maxValue, + Key? key, + required this.initialValue, + required this.minValue, + required this.maxValue, this.onChanged, }) : super(key: key); @@ -22,7 +22,7 @@ class PopupMenuZoom extends PopupMenuEntry { final int initialValue; final double minValue; final double maxValue; - final void Function(double) onChanged; + final void Function(double)? onChanged; } class _PopupMenuZoomState extends State { diff --git a/lib/widget/processing_dialog.dart b/lib/widget/processing_dialog.dart index bdeab6bf..fda2f26e 100644 --- a/lib/widget/processing_dialog.dart +++ b/lib/widget/processing_dialog.dart @@ -3,8 +3,8 @@ import 'package:flutter/widgets.dart'; class ProcessingDialog extends StatelessWidget { ProcessingDialog({ - Key key, - @required this.text, + Key? key, + required this.text, }); @override diff --git a/lib/widget/root_picker.dart b/lib/widget/root_picker.dart index 4c92ae3f..bfc2a3e8 100644 --- a/lib/widget/root_picker.dart +++ b/lib/widget/root_picker.dart @@ -25,12 +25,17 @@ class RootPickerArguments { class RootPicker extends StatefulWidget { static const routeName = "/root-picker"; + static Route buildRoute(RootPickerArguments args) => + MaterialPageRoute( + builder: (context) => RootPicker.fromArgs(args), + ); + RootPicker({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - RootPicker.fromArgs(RootPickerArguments args, {Key key}) + RootPicker.fromArgs(RootPickerArguments args, {Key? key}) : this( key: key, account: args.account, @@ -100,7 +105,7 @@ class _RootPickerState extends State child: Column( children: [ Text( - AppLocalizations.of(context).rootPickerHeaderText, + AppLocalizations.of(context)!.rootPickerHeaderText, style: Theme.of(context).textTheme.headline5, textAlign: TextAlign.center, ), @@ -108,7 +113,7 @@ class _RootPickerState extends State Align( alignment: AlignmentDirectional.topStart, child: Text( - AppLocalizations.of(context).rootPickerSubHeaderText, + AppLocalizations.of(context)!.rootPickerSubHeaderText, ), ), ], @@ -127,11 +132,11 @@ class _RootPickerState extends State children: [ TextButton( onPressed: () => _onSkipPressed(context), - child: Text(AppLocalizations.of(context).skipButtonLabel), + child: Text(AppLocalizations.of(context)!.skipButtonLabel), ), ElevatedButton( onPressed: () => _onConfirmPressed(context), - child: Text(AppLocalizations.of(context).confirmButtonLabel), + child: Text(AppLocalizations.of(context)!.confirmButtonLabel), ), ], ), @@ -145,7 +150,7 @@ class _RootPickerState extends State showDialog( context: context, builder: (context) => AlertDialog( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .rootPickerSkipConfirmationDialogContent), actions: [ TextButton( @@ -174,7 +179,7 @@ class _RootPickerState extends State if (roots.isEmpty) { SnackBarManager().showSnackBar(SnackBar( content: - Text(AppLocalizations.of(context).rootPickerListEmptyNotification), + Text(AppLocalizations.of(context)!.rootPickerListEmptyNotification), duration: k.snackBarDurationNormal, )); return; @@ -189,12 +194,12 @@ class _RootPickerState extends State return; } _isInitDialogShown = true; - SchedulerBinding.instance.addPostFrameCallback((_) { + SchedulerBinding.instance!.addPostFrameCallback((_) { showDialog( barrierDismissible: false, context: context, builder: (context) => ProcessingDialog( - text: AppLocalizations.of(context).genericProcessingDialogContent), + text: AppLocalizations.of(context)!.genericProcessingDialogContent), ); }); } diff --git a/lib/widget/selectable.dart b/lib/widget/selectable.dart index dbd911d7..25b41ad6 100644 --- a/lib/widget/selectable.dart +++ b/lib/widget/selectable.dart @@ -5,10 +5,10 @@ import 'package:nc_photos/theme.dart'; // Overlay a check mark if an item is selected class Selectable extends StatelessWidget { Selectable({ - Key key, - @required this.child, + Key? key, + required this.child, this.isSelected = false, - @required this.iconSize, + required this.iconSize, this.borderRadius, this.onTap, this.onLongPress, @@ -55,8 +55,8 @@ class Selectable extends StatelessWidget { final Widget child; final bool isSelected; final double iconSize; - final BorderRadiusGeometry borderRadius; + final BorderRadius? borderRadius; - final VoidCallback onTap; - final VoidCallback onLongPress; + final VoidCallback? onTap; + final VoidCallback? onLongPress; } diff --git a/lib/widget/selectable_item_stream_list_mixin.dart b/lib/widget/selectable_item_stream_list_mixin.dart index 4f74525b..d38d9211 100644 --- a/lib/widget/selectable_item_stream_list_mixin.dart +++ b/lib/widget/selectable_item_stream_list_mixin.dart @@ -18,7 +18,7 @@ import 'package:nc_photos/widget/selectable.dart'; abstract class SelectableItem { Widget buildWidget(BuildContext context); - VoidCallback get onTap => null; + VoidCallback? get onTap => null; bool get isSelectable => false; StaggeredTile get staggeredTile => const StaggeredTile.count(1, 1); } @@ -33,7 +33,7 @@ mixin SelectableItemStreamListMixin on State { @protected Widget buildItemStreamListOuter( BuildContext context, { - @required Widget child, + required Widget child, }) { if (platform_k.isWeb) { // support shift+click group selection on web @@ -51,8 +51,8 @@ mixin SelectableItemStreamListMixin on State { @protected Widget buildItemStreamList({ - @required double maxCrossAxisExtent, - ValueChanged onMaxExtentChanged, + required double maxCrossAxisExtent, + ValueChanged? onMaxExtentChanged, }) { return MeasurableItemList( key: _listKey, @@ -81,24 +81,25 @@ mixin SelectableItemStreamListMixin on State { @protected set itemStreamListItems(List newItems) { final lastSelectedItem = - _lastSelectPosition != null ? _items[_lastSelectPosition] : null; + _lastSelectPosition != null ? _items[_lastSelectPosition!] : null; _items = newItems; _transformSelectedItems(); // Keep _lastSelectPosition if no changes, drop otherwise - int newLastSelectPosition; + int? newLastSelectPosition; try { if (lastSelectedItem != null && - lastSelectedItem == _items[_lastSelectPosition]) { - newLastSelectPosition = _lastSelectPosition; + lastSelectedItem == _items[_lastSelectPosition!]) { + newLastSelectPosition = _lastSelectPosition!; } } catch (_) {} _lastSelectPosition = newLastSelectPosition; _log.info("[itemStreamListItems] updateListHeight: list item changed"); - WidgetsBinding.instance.addPostFrameCallback((_) => - (_listKey.currentState as MeasurableItemListState)?.updateListHeight()); + WidgetsBinding.instance!.addPostFrameCallback((_) => + (_listKey.currentState as MeasurableItemListState?) + ?.updateListHeight()); } Widget _buildItem(BuildContext context, int index) { @@ -139,8 +140,8 @@ mixin SelectableItemStreamListMixin on State { if (_isRangeSelectionMode && _lastSelectPosition != null) { setState(() { _selectedItems.addAll(_items - .sublist(math.min(_lastSelectPosition, index), - math.max(_lastSelectPosition, index) + 1) + .sublist(math.min(_lastSelectPosition!, index), + math.max(_lastSelectPosition!, index) + 1) .where((e) => e.isSelectable)); _lastSelectPosition = index; }); @@ -166,8 +167,8 @@ mixin SelectableItemStreamListMixin on State { if (!platform_k.isWeb && wasSelectionMode && _lastSelectPosition != null) { setState(() { _selectedItems.addAll(_items - .sublist(math.min(_lastSelectPosition, index), - math.max(_lastSelectPosition, index) + 1) + .sublist(math.min(_lastSelectPosition!, index), + math.max(_lastSelectPosition!, index) + 1) .where((e) => e.isSelectable)); _lastSelectPosition = index; }); @@ -183,8 +184,8 @@ mixin SelectableItemStreamListMixin on State { if (!SessionStorage().hasShowRangeSelectNotification) { SnackBarManager().showSnackBar(SnackBar( content: Text(platform_k.isWeb - ? AppLocalizations.of(context).webSelectRangeNotification - : AppLocalizations.of(context).mobileSelectRangeNotification), + ? AppLocalizations.of(context)!.webSelectRangeNotification + : AppLocalizations.of(context)!.mobileSelectRangeNotification), duration: k.snackBarDurationNormal, )); SessionStorage().hasShowRangeSelectNotification = true; @@ -205,14 +206,14 @@ mixin SelectableItemStreamListMixin on State { return null; } }) - .where((element) => element != null) + .whereType() .toList(); _selectedItems ..clear() ..addAll(newSelectedItems); } - int _lastSelectPosition; + int? _lastSelectPosition; bool _isRangeSelectionMode = false; var _items = []; diff --git a/lib/widget/settings.dart b/lib/widget/settings.dart index 0cfc51ba..fa4920ef 100644 --- a/lib/widget/settings.dart +++ b/lib/widget/settings.dart @@ -23,12 +23,16 @@ class SettingsArguments { class Settings extends StatefulWidget { static const routeName = "/settings"; + static Route buildRoute(SettingsArguments args) => MaterialPageRoute( + builder: (context) => Settings.fromArgs(args), + ); + Settings({ - Key key, - @required this.account, + Key? key, + required this.account, }) : super(key: key); - Settings.fromArgs(SettingsArguments args, {Key key}) + Settings.fromArgs(SettingsArguments args, {Key? key}) : this( account: args.account, ); @@ -43,7 +47,7 @@ class _SettingsState extends State { @override initState() { super.initState(); - _isEnableExif = Pref.inst().isEnableExif(); + _isEnableExif = Pref.inst().isEnableExifOr(); } @override @@ -58,40 +62,41 @@ class _SettingsState extends State { } Widget _buildContent(BuildContext context) { - final translator = AppLocalizations.of(context).translator; + final translator = AppLocalizations.of(context)!.translator; return CustomScrollView( slivers: [ SliverAppBar( pinned: true, - title: Text(AppLocalizations.of(context).settingsWidgetTitle), + title: Text(AppLocalizations.of(context)!.settingsWidgetTitle), ), SliverList( delegate: SliverChildListDelegate( [ ListTile( - title: Text(AppLocalizations.of(context).settingsLanguageTitle), + title: + Text(AppLocalizations.of(context)!.settingsLanguageTitle), subtitle: Text(language_util.getSelectedLanguageName(context)), onTap: () => _onLanguageTap(context), ), SwitchListTile( - title: - Text(AppLocalizations.of(context).settingsExifSupportTitle), + title: Text( + AppLocalizations.of(context)!.settingsExifSupportTitle), subtitle: _isEnableExif - ? Text(AppLocalizations.of(context) + ? Text(AppLocalizations.of(context)! .settingsExifSupportTrueSubtitle) : null, value: _isEnableExif, onChanged: (value) => _onExifSupportChanged(context, value), ), _buildCaption(context, - AppLocalizations.of(context).settingsAboutSectionTitle), + AppLocalizations.of(context)!.settingsAboutSectionTitle), ListTile( - title: Text(AppLocalizations.of(context).settingsVersionTitle), + title: Text(AppLocalizations.of(context)!.settingsVersionTitle), subtitle: const Text(k.versionStr), ), ListTile( title: - Text(AppLocalizations.of(context).settingsSourceCodeTitle), + Text(AppLocalizations.of(context)!.settingsSourceCodeTitle), subtitle: Text(_sourceRepo), onTap: () async { await launch(_sourceRepo); @@ -99,7 +104,7 @@ class _SettingsState extends State { ), ListTile( title: - Text(AppLocalizations.of(context).settingsBugReportTitle), + Text(AppLocalizations.of(context)!.settingsBugReportTitle), onTap: () { launch(_bugReportUrl); }, @@ -107,7 +112,7 @@ class _SettingsState extends State { if (translator.isNotEmpty) ListTile( title: Text( - AppLocalizations.of(context).settingsTranslatorTitle), + AppLocalizations.of(context)!.settingsTranslatorTitle), subtitle: Text(translator), onTap: () { launch(_translationUrl); @@ -172,8 +177,8 @@ class _SettingsState extends State { context: context, builder: (context) => AlertDialog( title: Text( - AppLocalizations.of(context).exifSupportConfirmationDialogTitle), - content: Text(AppLocalizations.of(context).exifSupportDetails), + AppLocalizations.of(context)!.exifSupportConfirmationDialogTitle), + content: Text(AppLocalizations.of(context)!.exifSupportDetails), actions: [ TextButton( onPressed: () { @@ -185,7 +190,7 @@ class _SettingsState extends State { onPressed: () { Navigator.of(context).pop(true); }, - child: Text(AppLocalizations.of(context).enableButtonLabel), + child: Text(AppLocalizations.of(context)!.enableButtonLabel), ), ], ), @@ -215,7 +220,7 @@ class _SettingsState extends State { _log.severe("[_setExifSupport] Failed writing pref"); SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).writePreferenceFailureNotification), + AppLocalizations.of(context)!.writePreferenceFailureNotification), duration: k.snackBarDurationNormal, )); setState(() { @@ -231,7 +236,7 @@ class _SettingsState extends State { static const String _translationUrl = "https://gitlab.com/nkming2/nc-photos/-/tree/master/lib/l10n"; - bool _isEnableExif; + late bool _isEnableExif; static final _log = Logger("widget.settings._SettingsState"); } diff --git a/lib/widget/setup.dart b/lib/widget/setup.dart index e980b742..fcbfbda7 100644 --- a/lib/widget/setup.dart +++ b/lib/widget/setup.dart @@ -9,7 +9,7 @@ import 'package:nc_photos/widget/sign_in.dart'; import 'package:page_view_indicators/circle_page_indicator.dart'; bool isNeedSetup() => - Pref.inst().getSetupProgress() & _PageId.all != _PageId.all; + Pref.inst().getSetupProgressOr() & _PageId.all != _PageId.all; class Setup extends StatefulWidget { static const routeName = "/setup"; @@ -29,15 +29,15 @@ class _SetupState extends State { ); } - Widget _buildAppBar(BuildContext context) { + PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - title: Text(AppLocalizations.of(context).setupWidgetTitle), + title: Text(AppLocalizations.of(context)!.setupWidgetTitle), elevation: 0, ); } Widget _buildContent(BuildContext context) { - final page = _pageController.hasClients ? _pageController.page.round() : 0; + final page = _pageController.hasClients ? _pageController.page!.round() : 0; final pages = [ if (_initialProgress & _PageId.exif == 0) _Exif(), if (_initialProgress & _PageId.hiddenPrefDirNotice == 0) @@ -70,16 +70,21 @@ class _SetupState extends State { ElevatedButton( onPressed: _onDonePressed, child: Text( - AppLocalizations.of(context).doneButtonLabel), + AppLocalizations.of(context)!.doneButtonLabel), ), ] : [ ElevatedButton( - onPressed: () => _onNextPressed( - (pages[_pageController.page.round()] as _Page) - .getPageId()), + onPressed: () { + if (_pageController.hasClients) { + _onNextPressed( + (pages[_pageController.page!.round()] + as _Page) + .getPageId()); + } + }, child: Text( - AppLocalizations.of(context).nextButtonLabel), + AppLocalizations.of(context)!.nextButtonLabel), ), ], ), @@ -107,12 +112,12 @@ class _SetupState extends State { } void _onNextPressed(int pageId) { - Pref.inst().setSetupProgress(Pref.inst().getSetupProgress() | pageId); + Pref.inst().setSetupProgress(Pref.inst().getSetupProgressOr() | pageId); _pageController.nextPage( duration: k.animationDurationNormal, curve: Curves.easeInOut); } - final _initialProgress = Pref.inst().getSetupProgress(); + final _initialProgress = Pref.inst().getSetupProgressOr(); final _pageController = PageController(); var _currentPageNotifier = ValueNotifier(0); } @@ -143,23 +148,23 @@ class _ExifState extends State<_Exif> { crossAxisAlignment: CrossAxisAlignment.start, children: [ SwitchListTile( - title: Text(AppLocalizations.of(context).settingsExifSupportTitle), + title: Text(AppLocalizations.of(context)!.settingsExifSupportTitle), value: _isEnableExif, onChanged: _onValueChanged, ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text(AppLocalizations.of(context).exifSupportDetails), + child: Text(AppLocalizations.of(context)!.exifSupportDetails), ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( - AppLocalizations.of(context).setupSettingsModifyLaterHint, + AppLocalizations.of(context)!.setupSettingsModifyLaterHint, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith(fontStyle: FontStyle.italic)), ), const SizedBox(height: 8), @@ -175,7 +180,7 @@ class _ExifState extends State<_Exif> { }); } - bool _isEnableExif = Pref.inst().isEnableExif(); + bool _isEnableExif = Pref.inst().isEnableExifOr(); } class _HiddenPrefDirNotice extends StatefulWidget implements _Page { @@ -196,7 +201,7 @@ class _HiddenPrefDirNoticeState extends State<_HiddenPrefDirNotice> { Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( - AppLocalizations.of(context).setupHiddenPrefDirNoticeDetail), + AppLocalizations.of(context)!.setupHiddenPrefDirNoticeDetail), ), const SizedBox(height: 24), Align( diff --git a/lib/widget/sign_in.dart b/lib/widget/sign_in.dart index bf9c2145..001c3d50 100644 --- a/lib/widget/sign_in.dart +++ b/lib/widget/sign_in.dart @@ -16,7 +16,7 @@ import 'package:nc_photos/widget/root_picker.dart'; class SignIn extends StatefulWidget { static const routeName = "/sign-in"; - SignIn({Key key}) : super(key: key); + SignIn({Key? key}) : super(key: key); @override createState() => _SignInState(); @@ -49,7 +49,7 @@ class _SignInState extends State { Padding( padding: const EdgeInsets.all(24), child: Text( - AppLocalizations.of(context).signInHeaderText, + AppLocalizations.of(context)!.signInHeaderText, style: Theme.of(context).textTheme.headline5, textAlign: TextAlign.center, ), @@ -70,7 +70,7 @@ class _SignInState extends State { padding: const EdgeInsets.symmetric( horizontal: 32, vertical: 16), child: Text( - AppLocalizations.of(context).signIn2faHintText, + AppLocalizations.of(context)!.signIn2faHintText, style: TextStyle(fontStyle: FontStyle.italic), ), ), @@ -82,7 +82,7 @@ class _SignInState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (!ModalRoute.of(context).isFirst) + if (!ModalRoute.of(context)!.isFirst) TextButton( onPressed: () { Navigator.pop(context); @@ -94,11 +94,11 @@ class _SignInState extends State { Container(), ElevatedButton( onPressed: () { - if (_formKey.currentState.validate()) { + if (_formKey.currentState?.validate() == true) { _connect(); } }, - child: Text(AppLocalizations.of(context) + child: Text(AppLocalizations.of(context)! .connectButtonLabel), ), ], @@ -143,11 +143,11 @@ class _SignInState extends State { .toList(), onChanged: (newValue) { setState(() { - _scheme = newValue; + _scheme = newValue!; }); }, onSaved: (value) { - _formValue.scheme = value.toValueString(); + _formValue.scheme = value!.toValueString(); }, ), ), @@ -159,18 +159,19 @@ class _SignInState extends State { Expanded( child: TextFormField( decoration: InputDecoration( - hintText: AppLocalizations.of(context).serverAddressInputHint, + hintText: + AppLocalizations.of(context)!.serverAddressInputHint, ), keyboardType: TextInputType.url, validator: (value) { - if (value.trim().trimRightAny("/").isEmpty) { - return AppLocalizations.of(context) + if (value!.trim().trimRightAny("/").isEmpty) { + return AppLocalizations.of(context)! .serverAddressInputInvalidEmpty; } return null; }, onSaved: (value) { - _formValue.address = value.trim().trimRightAny("/"); + _formValue.address = value!.trim().trimRightAny("/"); }, ), ), @@ -179,32 +180,32 @@ class _SignInState extends State { const SizedBox(height: 8), TextFormField( decoration: InputDecoration( - hintText: AppLocalizations.of(context).usernameInputHint, + hintText: AppLocalizations.of(context)!.usernameInputHint, ), validator: (value) { - if (value.trim().isEmpty) { - return AppLocalizations.of(context).usernameInputInvalidEmpty; + if (value!.trim().isEmpty) { + return AppLocalizations.of(context)!.usernameInputInvalidEmpty; } return null; }, onSaved: (value) { - _formValue.username = value; + _formValue.username = value!; }, ), const SizedBox(height: 8), TextFormField( decoration: InputDecoration( - hintText: AppLocalizations.of(context).passwordInputHint, + hintText: AppLocalizations.of(context)!.passwordInputHint, ), obscureText: true, validator: (value) { - if (value.trim().isEmpty) { - return AppLocalizations.of(context).passwordInputInvalidEmpty; + if (value!.trim().isEmpty) { + return AppLocalizations.of(context)!.passwordInputInvalidEmpty; } return null; }, onSaved: (value) { - _formValue.password = value; + _formValue.password = value!; }, ), ], @@ -212,22 +213,23 @@ class _SignInState extends State { } void _connect() { - _formKey.currentState.save(); + _formKey.currentState!.save(); final account = Account(_formValue.scheme, _formValue.address, _formValue.username, _formValue.password, [""]); _log.info("[_connect] Try connecting with account: $account"); - Navigator.pushNamed(context, Connect.routeName, + Navigator.pushNamed(context, Connect.routeName, arguments: ConnectArguments(account)) - .then((result) { + .then((result) { return result != null ? Navigator.pushNamed(context, RootPicker.routeName, arguments: RootPickerArguments(result)) - : null; + : Future.value(null); }).then((result) { if (result != null) { // we've got a good account // only signing in with app password would trigger distinct - final accounts = (Pref.inst().getAccounts([])..add(result)).distinct(); + final accounts = + (Pref.inst().getAccountsOr([])..add(result)).distinct(); Pref.inst() ..setAccounts(accounts) ..setCurrentAccountIndex(accounts.indexOf(result)); @@ -267,8 +269,8 @@ extension on _Scheme { } class _FormValue { - String scheme; - String address; - String username; - String password; + late String scheme; + late String address; + late String username; + late String password; } diff --git a/lib/widget/simple_input_dialog.dart b/lib/widget/simple_input_dialog.dart index aa0e87c7..2b3ea879 100644 --- a/lib/widget/simple_input_dialog.dart +++ b/lib/widget/simple_input_dialog.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; class SimpleInputDialog extends StatefulWidget { SimpleInputDialog({ - Key key, + Key? key, this.initialText, this.hintText, this.validator, @@ -12,9 +12,9 @@ class SimpleInputDialog extends StatefulWidget { @override createState() => _SimpleInputDialogState(); - final String initialText; - final String hintText; - final FormFieldValidator validator; + final String? initialText; + final String? hintText; + final FormFieldValidator? validator; } class _SimpleInputDialogState extends State { @@ -29,7 +29,7 @@ class _SimpleInputDialogState extends State { : InputDecoration(hintText: widget.hintText), validator: widget.validator, onSaved: (value) { - _formValue.text = value; + _formValue.text = value!; }, initialValue: widget.initialText, ), @@ -48,15 +48,15 @@ class _SimpleInputDialogState extends State { void _onSavePressed() { if (_formKey.currentState?.validate() == true) { _formValue = _FormValue(); - _formKey.currentState.save(); + _formKey.currentState!.save(); Navigator.of(context).pop(_formValue.text); } } final _formKey = GlobalKey(); - _FormValue _formValue; + var _formValue = _FormValue(); } class _FormValue { - String text; + late String text; } diff --git a/lib/widget/splash.dart b/lib/widget/splash.dart index a91e5bd4..7a7acc8a 100644 --- a/lib/widget/splash.dart +++ b/lib/widget/splash.dart @@ -13,7 +13,7 @@ import 'package:nc_photos/widget/sign_in.dart'; class Splash extends StatefulWidget { static const routeName = "/splash"; - Splash({Key key}) : super(key: key); + Splash({Key? key}) : super(key: key); @override createState() => _SplashState(); @@ -23,7 +23,7 @@ class _SplashState extends State { @override initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { if (_shouldUpgrade()) { _handleUpgrade(); } else { @@ -56,7 +56,7 @@ class _SplashState extends State { ), const SizedBox(height: 8), Text( - AppLocalizations.of(context).appTitle, + AppLocalizations.of(context)!.appTitle, textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline4, ) @@ -81,12 +81,12 @@ class _SplashState extends State { } bool _shouldUpgrade() { - final lastVersion = Pref.inst().getLastVersion(k.version); + final lastVersion = Pref.inst().getLastVersionOr(k.version); return lastVersion < k.version; } void _handleUpgrade() { - final lastVersion = Pref.inst().getLastVersion(k.version); + final lastVersion = Pref.inst().getLastVersionOr(k.version); // ... final change = _gatherChangelog(lastVersion); @@ -94,7 +94,7 @@ class _SplashState extends State { showDialog( context: context, builder: (context) => AlertDialog( - title: Text(AppLocalizations.of(context).changelogTitle), + title: Text(AppLocalizations.of(context)!.changelogTitle), content: SingleChildScrollView( child: Text(change), ), diff --git a/lib/widget/video_viewer.dart b/lib/widget/video_viewer.dart index f086cccc..b36f758f 100644 --- a/lib/widget/video_viewer.dart +++ b/lib/widget/video_viewer.dart @@ -12,8 +12,8 @@ import 'package:wakelock/wakelock.dart'; class VideoViewer extends StatefulWidget { VideoViewer({ - @required this.account, - @required this.file, + required this.account, + required this.file, this.onLoaded, this.onHeightChanged, this.onPlay, @@ -27,10 +27,10 @@ class VideoViewer extends StatefulWidget { final Account account; final File file; - final VoidCallback onLoaded; - final void Function(double height) onHeightChanged; - final VoidCallback onPlay; - final VoidCallback onPause; + final VoidCallback? onLoaded; + final ValueChanged? onHeightChanged; + final VoidCallback? onPlay; + final VoidCallback? onPause; final bool isControlVisible; final bool canPlay; } @@ -47,9 +47,9 @@ class _VideoViewerState extends State { )..initialize().then((_) { widget.onLoaded?.call(); setState(() {}); - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { if (_key.currentContext != null) { - widget.onHeightChanged?.call(_key.currentContext.size.height); + widget.onHeightChanged?.call(_key.currentContext!.size!.height); } }); }).catchError((e, stacktrace) { @@ -80,13 +80,13 @@ class _VideoViewerState extends State { @override dispose() { super.dispose(); - _controller?.dispose(); + _controller.dispose(); Wakelock.disable(); } Widget _buildPlayer(BuildContext context) { if (_controller.value.isPlaying && !widget.canPlay) { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { _pause(); }); } @@ -197,7 +197,7 @@ class _VideoViewerState extends State { } final _key = GlobalKey(); - VideoPlayerController _controller; + late VideoPlayerController _controller; var _isFinished = false; static final _log = Logger("widget.video_viewer._VideoViewerState"); diff --git a/lib/widget/viewer.dart b/lib/widget/viewer.dart index bd737e3a..181e6852 100644 --- a/lib/widget/viewer.dart +++ b/lib/widget/viewer.dart @@ -38,14 +38,18 @@ class ViewerArguments { class Viewer extends StatefulWidget { static const routeName = "/viewer"; + static Route buildRoute(ViewerArguments args) => MaterialPageRoute( + builder: (context) => Viewer.fromArgs(args), + ); + Viewer({ - Key key, - @required this.account, - @required this.streamFiles, - @required this.startIndex, + Key? key, + required this.account, + required this.streamFiles, + required this.startIndex, }) : super(key: key); - Viewer.fromArgs(ViewerArguments args, {Key key}) + Viewer.fromArgs(ViewerArguments args, {Key? key}) : this( key: key, account: args.account, @@ -124,7 +128,7 @@ class _ViewerState extends State { children: [ Container(color: Colors.black), if (!_pageController.hasClients || - !_pageStates[_pageController.page.round()].hasLoaded) + !_pageStates[_pageController.page!.round()]!.hasLoaded) Align( alignment: Alignment.center, child: const CircularProgressIndicator(), @@ -260,7 +264,7 @@ class _ViewerState extends State { if (!_isDetailPaneActive && _canOpenDetailPane()) IconButton( icon: const Icon(Icons.more_vert), - tooltip: AppLocalizations.of(context).detailsTooltip, + tooltip: AppLocalizations.of(context)!.detailsTooltip, onPressed: _onDetailsPressed, ), ], @@ -307,7 +311,7 @@ class _ViewerState extends State { Icons.share_outlined, color: Colors.white.withOpacity(.87), ), - tooltip: AppLocalizations.of(context).shareTooltip, + tooltip: AppLocalizations.of(context)!.shareTooltip, onPressed: () => _onSharePressed(context), ), ), @@ -318,7 +322,7 @@ class _ViewerState extends State { Icons.download_outlined, color: Colors.white.withOpacity(.87), ), - tooltip: AppLocalizations.of(context).downloadTooltip, + tooltip: AppLocalizations.of(context)!.downloadTooltip, onPressed: () => _onDownloadPressed(context), ), ), @@ -329,7 +333,7 @@ class _ViewerState extends State { Icons.delete_outlined, color: Colors.white.withOpacity(.87), ), - tooltip: AppLocalizations.of(context).deleteTooltip, + tooltip: AppLocalizations.of(context)!.deleteTooltip, onPressed: () => _onDeletePressed(context), ), ), @@ -344,7 +348,7 @@ class _ViewerState extends State { Widget _buildPage(BuildContext context, int index) { if (_pageStates[index] == null) { _onCreateNewPage(context, index); - } else if (!_pageStates[index].scrollController.hasClients) { + } else if (!_pageStates[index]!.scrollController.hasClients) { // the page has been moved out of view and is now coming back _log.fine("[_buildPage] Recreating page#$index"); _onRecreatePageAfterMovedOut(context, index); @@ -359,7 +363,7 @@ class _ViewerState extends State { child: NotificationListener( onNotification: (notif) => _onPageContentScrolled(notif, index), child: SingleChildScrollView( - controller: _pageStates[index].scrollController, + controller: _pageStates[index]!.scrollController, physics: _isDetailPaneActive ? null : const NeverScrollableScrollPhysics(), child: Stack( @@ -409,7 +413,7 @@ class _ViewerState extends State { return _buildVideoView(context, index); } else { _log.shout("[_buildItemView] Unknown file format: ${file.contentType}"); - _pageStates[index].itemHeight = 0; + _pageStates[index]!.itemHeight = 0; return Container(); } } @@ -452,7 +456,7 @@ class _ViewerState extends State { return false; } if (notification is ScrollEndNotification) { - final scrollPos = _pageStates[index].scrollController.position; + final scrollPos = _pageStates[index]!.scrollController.position; if (scrollPos.pixels == 0) { setState(() { _onDetailPaneClosed(); @@ -463,14 +467,15 @@ class _ViewerState extends State { // upward, open the pane to its minimal size Future.delayed(Duration.zero, () { setState(() { - _openDetailPane(_pageController.page.toInt(), + _openDetailPane(_pageController.page!.toInt(), shouldAnimate: true); }); }); } else if (scrollPos.userScrollDirection == ScrollDirection.forward) { // downward, close the pane Future.delayed(Duration.zero, () { - _closeDetailPane(_pageController.page.toInt(), shouldAnimate: true); + _closeDetailPane(_pageController.page!.toInt(), + shouldAnimate: true); }); } } @@ -481,8 +486,8 @@ class _ViewerState extends State { void _onImageLoaded(int index) { // currently pageview doesn't pre-load pages, we do it manually // don't pre-load if user already navigated away - if (_pageController.page.round() == index && - !_pageStates[index].hasLoaded) { + if (_pageController.page!.round() == index && + !_pageStates[index]!.hasLoaded) { _log.info("[_onImageLoaded] Pre-loading nearby images"); if (index > 0) { final prevFile = widget.streamFiles[index - 1]; @@ -497,16 +502,16 @@ class _ViewerState extends State { } } setState(() { - _pageStates[index].hasLoaded = true; + _pageStates[index]!.hasLoaded = true; }); } } void _onVideoLoaded(int index) { - if (_pageController.page.round() == index && - !_pageStates[index].hasLoaded) { + if (_pageController.page!.round() == index && + !_pageStates[index]!.hasLoaded) { setState(() { - _pageStates[index].hasLoaded = true; + _pageStates[index]!.hasLoaded = true; }); } } @@ -534,16 +539,16 @@ class _ViewerState extends State { /// Called when the page is being built after previously moved out of view void _onRecreatePageAfterMovedOut(BuildContext context, int index) { if (_isShowDetailPane && !_isClosingDetailPane) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (_pageStates[index].itemHeight != null) { + WidgetsBinding.instance!.addPostFrameCallback((_) { + if (_pageStates[index]!.itemHeight != null) { setState(() { _openDetailPane(index); }); } }); } else { - WidgetsBinding.instance.addPostFrameCallback((_) { - _pageStates[index].scrollController.jumpTo(0); + WidgetsBinding.instance!.addPostFrameCallback((_) { + _pageStates[index]!.scrollController.jumpTo(0); }); } } @@ -551,26 +556,26 @@ class _ViewerState extends State { void _onDetailsPressed() { if (!_isDetailPaneActive) { setState(() { - _openDetailPane(_pageController.page.toInt(), shouldAnimate: true); + _openDetailPane(_pageController.page!.toInt(), shouldAnimate: true); }); } } void _onSharePressed(BuildContext context) { assert(platform_k.isAndroid); - final file = widget.streamFiles[_pageController.page.round()]; + final file = widget.streamFiles[_pageController.page!.round()]; ShareHandler().shareFiles(context, widget.account, [file]); } void _onDownloadPressed(BuildContext context) async { - final file = widget.streamFiles[_pageController.page.round()]; + final file = widget.streamFiles[_pageController.page!.round()]; _log.info("[_onDownloadPressed] Downloading file: ${file.path}"); var controller = SnackBarManager().showSnackBar(SnackBar( content: - Text(AppLocalizations.of(context).downloadProcessingNotification), + Text(AppLocalizations.of(context)!.downloadProcessingNotification), duration: k.snackBarDurationShort, )); - controller?.closed?.whenComplete(() { + controller?.closed.whenComplete(() { controller = null; }); dynamic result; @@ -581,7 +586,7 @@ class _ViewerState extends State { _log.warning("[_onDownloadPressed] Permission not granted"); controller?.close(); SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .downloadFailureNoPermissionNotification), duration: k.snackBarDurationNormal, )); @@ -591,9 +596,9 @@ class _ViewerState extends State { "[_onDownloadPressed] Failed while downloadFile", e, stacktrace); controller?.close(); SnackBarManager().showSnackBar(SnackBar( - content: - Text("${AppLocalizations.of(context).downloadFailureNotification}: " - "${exception_util.toUserString(e, context)}"), + content: Text( + "${AppLocalizations.of(context)!.downloadFailureNotification}: " + "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); return; @@ -622,19 +627,19 @@ class _ViewerState extends State { // fallback SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).downloadSuccessNotification), + content: Text(AppLocalizations.of(context)!.downloadSuccessNotification), duration: k.snackBarDurationShort, )); } void _onDeletePressed(BuildContext context) async { - final file = widget.streamFiles[_pageController.page.round()]; + final file = widget.streamFiles[_pageController.page!.round()]; _log.info("[_onDeletePressed] Removing file: ${file.path}"); var controller = SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).deleteProcessingNotification), + content: Text(AppLocalizations.of(context)!.deleteProcessingNotification), duration: k.snackBarDurationShort, )); - controller?.closed?.whenComplete(() { + controller?.closed.whenComplete(() { controller = null; }); try { @@ -642,7 +647,7 @@ class _ViewerState extends State { AlbumRepo(AlbumCachedDataSource()))(widget.account, file); controller?.close(); SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).deleteSuccessNotification), + content: Text(AppLocalizations.of(context)!.deleteSuccessNotification), duration: k.snackBarDurationNormal, )); Navigator.of(context).pop(); @@ -655,7 +660,7 @@ class _ViewerState extends State { controller?.close(); SnackBarManager().showSnackBar(SnackBar( content: - Text("${AppLocalizations.of(context).deleteFailureNotification}: " + Text("${AppLocalizations.of(context)!.deleteFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -666,8 +671,9 @@ class _ViewerState extends State { if (_pageStates[index]?.itemHeight == null) { return MediaQuery.of(context).size.height; } else { - return _pageStates[index].itemHeight + - (MediaQuery.of(context).size.height - _pageStates[index].itemHeight) / + return _pageStates[index]!.itemHeight! + + (MediaQuery.of(context).size.height - + _pageStates[index]!.itemHeight!) / 2 - 4; } @@ -680,10 +686,10 @@ class _ViewerState extends State { } void _updateItemHeight(int index, double height) { - if (_pageStates[index].itemHeight != height) { + if (_pageStates[index]!.itemHeight != height) { _log.fine("[_updateItemHeight] New height of item#$index: $height"); setState(() { - _pageStates[index].itemHeight = height; + _pageStates[index]!.itemHeight = height; if (_isDetailPaneActive) { _openDetailPane(index); } @@ -709,12 +715,12 @@ class _ViewerState extends State { _isShowDetailPane = true; _isDetailPaneActive = true; if (shouldAnimate) { - _pageStates[index].scrollController.animateTo( + _pageStates[index]!.scrollController.animateTo( _calcDetailPaneOpenedScrollPosition(index), duration: k.animationDurationNormal, curve: Curves.easeOut); } else { - _pageStates[index] + _pageStates[index]! .scrollController .jumpTo(_calcDetailPaneOpenedScrollPosition(index)); } @@ -723,7 +729,7 @@ class _ViewerState extends State { void _closeDetailPane(int index, {bool shouldAnimate = false}) { _isClosingDetailPane = true; if (shouldAnimate) { - _pageStates[index].scrollController.animateTo(0, + _pageStates[index]!.scrollController.animateTo(0, duration: k.animationDurationNormal, curve: Curves.easeOut); } } @@ -739,7 +745,7 @@ class _ViewerState extends State { .previousPage( duration: k.animationDurationNormal, curve: Curves.easeInOut) .whenComplete( - () => _updateNavigationState(_pageController.page.round())); + () => _updateNavigationState(_pageController.page!.round())); } /// Switch to the next image in the stream @@ -747,7 +753,7 @@ class _ViewerState extends State { _pageController .nextPage(duration: k.animationDurationNormal, curve: Curves.easeInOut) .whenComplete( - () => _updateNavigationState(_pageController.page.round())); + () => _updateNavigationState(_pageController.page!.round())); } /// Switch to the image on the "left", what that means depend on the current @@ -819,7 +825,7 @@ class _ViewerState extends State { var _isZoomed = false; - PageController _pageController; + late PageController _pageController; final _pageStates = {}; /// used to gain focus on web for keyboard support @@ -832,6 +838,6 @@ class _PageState { _PageState(this.scrollController); ScrollController scrollController; - double itemHeight; + double? itemHeight; bool hasLoaded = false; } diff --git a/lib/widget/viewer_detail_pane.dart b/lib/widget/viewer_detail_pane.dart index 68ddea36..a7f679ff 100644 --- a/lib/widget/viewer_detail_pane.dart +++ b/lib/widget/viewer_detail_pane.dart @@ -32,9 +32,9 @@ import 'package:tuple/tuple.dart'; class ViewerDetailPane extends StatefulWidget { const ViewerDetailPane({ - Key key, - @required this.account, - @required this.file, + Key? key, + required this.account, + required this.file, }) : super(key: key); @override @@ -54,7 +54,7 @@ class _ViewerDetailPaneState extends State { _log.info("[initState] Metadata missing in File"); } else { _log.info("[initState] Metadata exists in File"); - if (widget.file.metadata.exif != null) { + if (widget.file.metadata!.exif != null) { _initMetadata(); } } @@ -73,29 +73,29 @@ class _ViewerDetailPaneState extends State { const space = " "; if (widget.file.metadata?.imageWidth != null && widget.file.metadata?.imageHeight != null) { - final pixelCount = - widget.file.metadata.imageWidth * widget.file.metadata.imageHeight; + final pixelCount = widget.file.metadata!.imageWidth! * + widget.file.metadata!.imageHeight!; if (pixelCount >= 500000) { final mpCount = pixelCount / 1000000.0; - sizeSubStr += AppLocalizations.of(context) + sizeSubStr += AppLocalizations.of(context)! .megapixelCount(mpCount.toStringAsFixed(1)); sizeSubStr += space; } - sizeSubStr += _byteSizeToString(widget.file.contentLength); + sizeSubStr += _byteSizeToString(widget.file.contentLength ?? 0); } String cameraSubStr = ""; if (_fNumber != null) { - cameraSubStr += "f/${_fNumber.toStringAsFixed(1)}$space"; + cameraSubStr += "f/${_fNumber!.toStringAsFixed(1)}$space"; } if (_exposureTime != null) { cameraSubStr += - AppLocalizations.of(context).secondCountSymbol(_exposureTime); + AppLocalizations.of(context)!.secondCountSymbol(_exposureTime!); cameraSubStr += space; } if (_focalLength != null) { - cameraSubStr += AppLocalizations.of(context) - .millimeterCountSymbol(_focalLength.toStringAsFixedTruncated(2)); + cameraSubStr += AppLocalizations.of(context)! + .millimeterCountSymbol(_focalLength!.toStringAsFixedTruncated(2)); cameraSubStr += space; } if (_isoSpeedRatings != null) { @@ -112,12 +112,12 @@ class _ViewerDetailPaneState extends State { children: [ _DetailPaneButton( icon: Icons.playlist_add_outlined, - label: AppLocalizations.of(context).addToAlbumTooltip, + label: AppLocalizations.of(context)!.addToAlbumTooltip, onPressed: () => _onAddToAlbumPressed(context), ), _DetailPaneButton( icon: Icons.delete_outline, - label: AppLocalizations.of(context).deleteTooltip, + label: AppLocalizations.of(context)!.deleteTooltip, onPressed: () => _onDeletePressed(context), ), ], @@ -154,7 +154,7 @@ class _ViewerDetailPaneState extends State { color: AppTheme.getSecondaryTextColor(context), ), title: Text( - "${widget.file.metadata.imageWidth} x ${widget.file.metadata.imageHeight}"), + "${widget.file.metadata!.imageWidth} x ${widget.file.metadata!.imageHeight}"), subtitle: Text(sizeSubStr), ) else @@ -163,7 +163,7 @@ class _ViewerDetailPaneState extends State { Icons.aspect_ratio, color: AppTheme.getSecondaryTextColor(context), ), - title: Text(_byteSizeToString(widget.file.contentLength)), + title: Text(_byteSizeToString(widget.file.contentLength ?? 0)), ), if (_model != null) ListTile( @@ -171,14 +171,14 @@ class _ViewerDetailPaneState extends State { Icons.camera_outlined, color: AppTheme.getSecondaryTextColor(context), ), - title: Text(_model), + title: Text(_model!), subtitle: cameraSubStr.isNotEmpty ? Text(cameraSubStr) : null, ), if (features.isSupportMapView && _gps != null) SizedBox( height: 256, child: platform.Map( - center: _gps, + center: _gps!, zoom: 16, onTap: _onMapTap, ), @@ -190,35 +190,35 @@ class _ViewerDetailPaneState extends State { /// Convert EXIF data to readable format void _initMetadata() { - final exif = widget.file.metadata.exif; + final exif = widget.file.metadata!.exif!; _log.info("[_initMetadata] $exif"); if (exif.make != null && exif.model != null) { _model = "${exif.make} ${exif.model}"; } if (exif.fNumber != null) { - _fNumber = exif.fNumber.toDouble(); + _fNumber = exif.fNumber!.toDouble(); } if (exif.exposureTime != null) { - if (exif.exposureTime.denominator == 1) { - _exposureTime = exif.exposureTime.numerator.toString(); + if (exif.exposureTime!.denominator == 1) { + _exposureTime = exif.exposureTime!.numerator.toString(); } else { _exposureTime = exif.exposureTime.toString(); } } if (exif.focalLength != null) { - _focalLength = exif.focalLength.toDouble(); + _focalLength = exif.focalLength!.toDouble(); } if (exif.isoSpeedRatings != null) { - _isoSpeedRatings = exif.isoSpeedRatings; + _isoSpeedRatings = exif.isoSpeedRatings!; } if (exif.gpsLatitudeRef != null && exif.gpsLatitude != null && exif.gpsLongitudeRef != null && exif.gpsLongitude != null) { - final lat = _gpsDmsToDouble(exif.gpsLatitude) * + final lat = _gpsDmsToDouble(exif.gpsLatitude!) * (exif.gpsLatitudeRef == "S" ? -1 : 1); - final lng = _gpsDmsToDouble(exif.gpsLongitude) * + final lng = _gpsDmsToDouble(exif.gpsLongitude!) * (exif.gpsLongitudeRef == "W" ? -1 : 1); _log.fine("GPS: ($lat, $lng)"); _gps = Tuple2(lat, lng); @@ -238,7 +238,7 @@ class _ViewerDetailPaneState extends State { _log.info("[_onAddToAlbumPressed] Album picked: ${value.name}"); _addToAlbum(context, value).then((_) { SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context) + content: Text(AppLocalizations.of(context)! .addToAlbumSuccessNotification(value.name)), duration: k.snackBarDurationNormal, )); @@ -246,7 +246,7 @@ class _ViewerDetailPaneState extends State { } else { SnackBarManager().showSnackBar(SnackBar( content: - Text(AppLocalizations.of(context).addToAlbumFailureNotification), + Text(AppLocalizations.of(context)!.addToAlbumFailureNotification), duration: k.snackBarDurationNormal, )); } @@ -255,7 +255,7 @@ class _ViewerDetailPaneState extends State { "[_onAddToAlbumPressed] Failed while showDialog", e, stacktrace); SnackBarManager().showSnackBar(SnackBar( content: Text( - "${AppLocalizations.of(context).addToAlbumFailureNotification}: " + "${AppLocalizations.of(context)!.addToAlbumFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -265,10 +265,10 @@ class _ViewerDetailPaneState extends State { void _onDeletePressed(BuildContext context) async { _log.info("[_onDeletePressed] Removing file: ${widget.file.path}"); var controller = SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).deleteProcessingNotification), + content: Text(AppLocalizations.of(context)!.deleteProcessingNotification), duration: k.snackBarDurationShort, )); - controller?.closed?.whenComplete(() { + controller?.closed.whenComplete(() { controller = null; }); try { @@ -276,7 +276,7 @@ class _ViewerDetailPaneState extends State { AlbumRepo(AlbumCachedDataSource()))(widget.account, widget.file); controller?.close(); SnackBarManager().showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).deleteSuccessNotification), + content: Text(AppLocalizations.of(context)!.deleteSuccessNotification), duration: k.snackBarDurationNormal, )); Navigator.of(context).pop(); @@ -289,7 +289,7 @@ class _ViewerDetailPaneState extends State { controller?.close(); SnackBarManager().showSnackBar(SnackBar( content: - Text("${AppLocalizations.of(context).deleteFailureNotification}: " + Text("${AppLocalizations.of(context)!.deleteFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -300,7 +300,7 @@ class _ViewerDetailPaneState extends State { if (platform_k.isAndroid) { final intent = AndroidIntent( action: "action_view", - data: Uri.encodeFull("geo:${_gps.item1},${_gps.item2}?z=16"), + data: Uri.encodeFull("geo:${_gps!.item1},${_gps!.item2}?z=16"), ); intent.launch(); } @@ -329,7 +329,7 @@ class _ViewerDetailPaneState extends State { stacktrace); SnackBarManager().showSnackBar(SnackBar( content: Text( - AppLocalizations.of(context).updateDateTimeFailureNotification), + AppLocalizations.of(context)!.updateDateTimeFailureNotification), duration: k.snackBarDurationNormal, )); } @@ -362,7 +362,7 @@ class _ViewerDetailPaneState extends State { _log.info("[_addToAlbum] File already in album: ${widget.file.path}"); SnackBarManager().showSnackBar(SnackBar( content: Text( - "${AppLocalizations.of(context).addToAlbumAlreadyAddedNotification}"), + "${AppLocalizations.of(context)!.addToAlbumAlreadyAddedNotification}"), duration: k.snackBarDurationNormal, )); return Future.error(ArgumentError("File already in album")); @@ -381,7 +381,7 @@ class _ViewerDetailPaneState extends State { _log.shout("[_addToAlbum] Failed while updating album", e, stacktrace); SnackBarManager().showSnackBar(SnackBar( content: Text( - "${AppLocalizations.of(context).addToAlbumFailureNotification}: " + "${AppLocalizations.of(context)!.addToAlbumFailureNotification}: " "${exception_util.toUserString(e, context)}"), duration: k.snackBarDurationNormal, )); @@ -389,22 +389,26 @@ class _ViewerDetailPaneState extends State { } } - DateTime _dateTime; + late DateTime _dateTime; // EXIF data - String _model; - double _fNumber; - String _exposureTime; - double _focalLength; - int _isoSpeedRatings; - Tuple2 _gps; + String? _model; + double? _fNumber; + String? _exposureTime; + double? _focalLength; + int? _isoSpeedRatings; + Tuple2? _gps; static final _log = Logger("widget.viewer_detail_pane._ViewerDetailPaneState"); } class _DetailPaneButton extends StatelessWidget { - const _DetailPaneButton({Key key, this.icon, this.label, this.onPressed}) - : super(key: key); + const _DetailPaneButton({ + Key? key, + required this.icon, + required this.label, + required this.onPressed, + }) : super(key: key); @override build(BuildContext context) { @@ -439,7 +443,7 @@ class _DetailPaneButton extends StatelessWidget { final IconData icon; final String label; - final VoidCallback onPressed; + final VoidCallback? onPressed; } String _byteSizeToString(int byteSize) { diff --git a/pubspec.yaml b/pubspec.yaml index 1066106f..1fb14045 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.22.0+2200 environment: - sdk: ">=2.10.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: diff --git a/test/entity/album_test.dart b/test/entity/album_test.dart index edfa50f0..7a89b3b3 100644 --- a/test/entity/album_test.dart +++ b/test/entity/album_test.dart @@ -14,6 +14,7 @@ void main() { final json = { "version": Album.version, "lastUpdated": "2020-01-02T03:04:05.678901Z", + "name": "", "provider": { "type": "static", "content": { @@ -30,7 +31,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "", @@ -63,7 +65,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "album", @@ -114,7 +117,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "", @@ -161,7 +165,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "", @@ -203,7 +208,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "", @@ -242,7 +248,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "", @@ -260,6 +267,7 @@ void main() { final json = { "version": Album.version, "lastUpdated": "2020-01-02T03:04:05.678901Z", + "name": "", "provider": { "type": "static", "content": { @@ -279,7 +287,8 @@ void main() { }, }; expect( - Album.fromJson(json), + Album.fromJson(json, + upgraderV1: null, upgraderV2: null, upgraderV3: null), Album( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), name: "", diff --git a/test/entity/file_test.dart b/test/entity/file_test.dart index e108f569..db043f2a 100644 --- a/test/entity/file_test.dart +++ b/test/entity/file_test.dart @@ -109,7 +109,7 @@ void main() { "version": Metadata.version, "lastUpdated": "2020-01-02T03:04:05.678901Z", }; - expect(Metadata.fromJson(json), + expect(Metadata.fromJson(json, upgraderV1: null, upgraderV2: null), Metadata(lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901))); }); @@ -120,7 +120,7 @@ void main() { "fileEtag": "8a3e0799b6f0711c23cc2d93950eceb5", }; expect( - Metadata.fromJson(json), + Metadata.fromJson(json, upgraderV1: null, upgraderV2: null), Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), fileEtag: "8a3e0799b6f0711c23cc2d93950eceb5", @@ -134,7 +134,7 @@ void main() { "imageWidth": 1024, }; expect( - Metadata.fromJson(json), + Metadata.fromJson(json, upgraderV1: null, upgraderV2: null), Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), imageWidth: 1024, @@ -148,7 +148,7 @@ void main() { "imageHeight": 768, }; expect( - Metadata.fromJson(json), + Metadata.fromJson(json, upgraderV1: null, upgraderV2: null), Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), imageHeight: 768, @@ -164,7 +164,7 @@ void main() { }, }; expect( - Metadata.fromJson(json), + Metadata.fromJson(json, upgraderV1: null, upgraderV2: null), Metadata( lastUpdated: DateTime.utc(2020, 1, 2, 3, 4, 5, 678, 901), exif: Exif({