diff --git a/CHANGELOG.md b/CHANGELOG.md index 108c9d2..d93716a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # CHANGELOG ## 1.5.2+18 -* ... +* Added proper linting to project ## 1.5.1+17 * Fixed white background button in AppBar when light theme enabled diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..84ec813 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + library_private_types_in_public_api: false + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/lib/app.dart b/lib/app.dart index 51dac04..76efa68 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -21,7 +21,7 @@ class MyApp extends StatelessWidget { static final _defaultLightColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.light); static final _defaultDarkColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.dark); - MyApp() { + MyApp({super.key}) { initializeDateFormatting('en'); } @@ -52,7 +52,7 @@ class MyApp extends StatelessWidget { darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme ?? _defaultDarkColorScheme), onGenerateRoute: AppRouter.generateRoute, navigatorKey: locator().navigationKey, - home: StartUpView(), + home: const StartUpView(), supportedLocales: localizationDelegate.supportedLocales, locale: localizationDelegate.currentLocale, ); diff --git a/lib/core/enums/error_code.dart b/lib/core/enums/error_code.dart index 9ebe1bb..24f2dca 100644 --- a/lib/core/enums/error_code.dart +++ b/lib/core/enums/error_code.dart @@ -1,15 +1,15 @@ /// Enums for error codes enum ErrorCode { /// A generic error - GENERAL_ERROR, + generalError, /// Errors related to connections - SOCKET_ERROR, - SOCKET_TIMEOUT, + socketError, + socketTimeout, /// A REST error (response code wasn't 200 or 204) - REST_ERROR, + restError, /// Custom errors - INVALID_API_KEY + invalidApiKey } diff --git a/lib/core/enums/refresh_event.dart b/lib/core/enums/refresh_event.dart index 914dba5..4aab480 100644 --- a/lib/core/enums/refresh_event.dart +++ b/lib/core/enums/refresh_event.dart @@ -1 +1 @@ -enum RefreshEvent { RefreshHistory } +enum RefreshEvent { refreshHistory } diff --git a/lib/core/enums/viewstate.dart b/lib/core/enums/viewstate.dart index 7b60208..24f865f 100644 --- a/lib/core/enums/viewstate.dart +++ b/lib/core/enums/viewstate.dart @@ -1 +1 @@ -enum ViewState { Idle, Busy } +enum ViewState { idle, busy } diff --git a/lib/core/error/rest_service_exception.dart b/lib/core/error/rest_service_exception.dart index 8592f86..3f1bd35 100644 --- a/lib/core/error/rest_service_exception.dart +++ b/lib/core/error/rest_service_exception.dart @@ -6,8 +6,9 @@ class RestServiceException extends ServiceException { final dynamic responseBody; RestServiceException(this.statusCode, {this.responseBody, String? message}) - : super(code: ErrorCode.REST_ERROR, message: message); + : super(code: ErrorCode.restError, message: message); + @override String toString() { return "$code $statusCode $message"; } diff --git a/lib/core/error/service_exception.dart b/lib/core/error/service_exception.dart index 0b0f6ee..cc263fb 100644 --- a/lib/core/error/service_exception.dart +++ b/lib/core/error/service_exception.dart @@ -4,8 +4,9 @@ class ServiceException implements Exception { final ErrorCode code; final String? message; - ServiceException({this.code = ErrorCode.GENERAL_ERROR, this.message = ''}); + ServiceException({this.code = ErrorCode.generalError, this.message = ''}); + @override String toString() { return "$code: $message"; } diff --git a/lib/core/manager/dialog_manager.dart b/lib/core/manager/dialog_manager.dart index 23974d0..b1c14bb 100644 --- a/lib/core/manager/dialog_manager.dart +++ b/lib/core/manager/dialog_manager.dart @@ -8,13 +8,14 @@ import '../services/dialog_service.dart'; class DialogManager extends StatefulWidget { final Widget? child; - DialogManager({Key? key, this.child}) : super(key: key); + const DialogManager({Key? key, this.child}) : super(key: key); + @override _DialogManagerState createState() => _DialogManagerState(); } class _DialogManagerState extends State { - DialogService _dialogService = locator(); + final DialogService _dialogService = locator(); @override void initState() { diff --git a/lib/core/manager/lifecycle_manager.dart b/lib/core/manager/lifecycle_manager.dart index c28a95c..b2da48a 100644 --- a/lib/core/manager/lifecycle_manager.dart +++ b/lib/core/manager/lifecycle_manager.dart @@ -11,8 +11,9 @@ import '../util/logger.dart'; class LifeCycleManager extends StatefulWidget { final Widget? child; - LifeCycleManager({Key? key, this.child}) : super(key: key); + const LifeCycleManager({Key? key, this.child}) : super(key: key); + @override _LifeCycleManagerState createState() => _LifeCycleManagerState(); } @@ -43,12 +44,12 @@ class _LifeCycleManagerState extends State with WidgetsBinding logger.d('LifeCycle event ${state.toString()}'); super.didChangeAppLifecycleState(state); - servicesToManage.forEach((service) { + for (var service in servicesToManage) { if (state == AppLifecycleState.resumed) { service.start(); } else { service.stop(); } - }); + } } } diff --git a/lib/core/repositories/file_repository.dart b/lib/core/repositories/file_repository.dart index be94fef..e93ae3a 100644 --- a/lib/core/repositories/file_repository.dart +++ b/lib/core/repositories/file_repository.dart @@ -11,7 +11,7 @@ import '../models/rest/uploaded_response.dart'; import '../services/api.dart'; class FileRepository { - Api _api = locator(); + final Api _api = locator(); Future getHistory() async { var response = await _api.post('/file/history'); @@ -39,11 +39,11 @@ class FileRepository { } Future postCreateMultiPaste(List ids) async { - Map multiPasteIds = Map(); + Map multiPasteIds = {}; - ids.forEach((element) { + for (var element in ids) { multiPasteIds.putIfAbsent("ids[${ids.indexOf(element) + 1}]", () => element); - }); + } var response = await _api.post('/file/create_multipaste', fields: multiPasteIds); return UploadedMultiResponse.fromJson(json.decode(response.body)); diff --git a/lib/core/repositories/user_repository.dart b/lib/core/repositories/user_repository.dart index 5fe8fd0..f8d53e1 100644 --- a/lib/core/repositories/user_repository.dart +++ b/lib/core/repositories/user_repository.dart @@ -6,7 +6,7 @@ import '../models/rest/create_apikey_response.dart'; import '../services/api.dart'; class UserRepository { - Api _api = locator(); + final Api _api = locator(); Future postApiKey( String url, String username, String password, String accessLevel, String comment) async { diff --git a/lib/core/services/api.dart b/lib/core/services/api.dart index 53b80ac..2cf65b9 100644 --- a/lib/core/services/api.dart +++ b/lib/core/services/api.dart @@ -24,8 +24,8 @@ class Api implements ApiErrorConverter { String _url = ""; String _apiKey = ""; - Map _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson}; - Duration _timeout = Duration(seconds: Constants.apiRequestTimeoutLimit); + final Map _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson}; + Duration _timeout = const Duration(seconds: Constants.apiRequestTimeoutLimit); Future fetch(String route) async { try { @@ -35,9 +35,9 @@ class Api implements ApiErrorConverter { handleRestErrors(response); return response; } on TimeoutException { - throw ServiceException(code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout); + throw ServiceException(code: ErrorCode.socketTimeout, message: _errorTimeout); } on SocketException { - throw ServiceException(code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection); + throw ServiceException(code: ErrorCode.socketError, message: _errorNoConnection); } } @@ -58,12 +58,12 @@ class Api implements ApiErrorConverter { } if (files != null && files.isNotEmpty) { - files.forEach((element) async { + for (var element in files) { request.files.add(await http.MultipartFile.fromPath('file[${files.indexOf(element) + 1}]', element.path)); - }); + } } - if (additionalFiles != null && additionalFiles.length > 0) { + if (additionalFiles != null && additionalFiles.isNotEmpty) { List keys = additionalFiles.keys.toList(); additionalFiles.forEach((key, value) { var index = files != null ? files.length + keys.indexOf(key) + 1 : keys.indexOf(key) + 1; @@ -77,9 +77,9 @@ class Api implements ApiErrorConverter { handleRestErrors(response); return response; } on TimeoutException { - throw ServiceException(code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout); + throw ServiceException(code: ErrorCode.socketTimeout, message: _errorTimeout); } on SocketException { - throw ServiceException(code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection); + throw ServiceException(code: ErrorCode.socketError, message: _errorNoConnection); } } @@ -114,11 +114,11 @@ class Api implements ApiErrorConverter { if (ContentType.json.primaryType == responseContentType.primaryType && ContentType.json.subType == responseContentType.subType) { var parsedBody = convert(response); - throw new RestServiceException(response.statusCode, responseBody: parsedBody); + throw RestServiceException(response.statusCode, responseBody: parsedBody); } } - throw new RestServiceException(response.statusCode); + throw RestServiceException(response.statusCode); } } diff --git a/lib/core/services/dialog_service.dart b/lib/core/services/dialog_service.dart index e1fcd1a..ccf0d28 100644 --- a/lib/core/services/dialog_service.dart +++ b/lib/core/services/dialog_service.dart @@ -7,7 +7,7 @@ import '../datamodels/dialog_request.dart'; import '../datamodels/dialog_response.dart'; class DialogService { - GlobalKey _dialogNavigationKey = GlobalKey(); + final GlobalKey _dialogNavigationKey = GlobalKey(); late Function(DialogRequest) _showDialogListener; Completer? _dialogCompleter; diff --git a/lib/core/services/navigation_service.dart b/lib/core/services/navigation_service.dart index abcc186..cd0b539 100644 --- a/lib/core/services/navigation_service.dart +++ b/lib/core/services/navigation_service.dart @@ -4,7 +4,7 @@ import 'package:logger/logger.dart'; import '../util/logger.dart'; class NavigationService { - GlobalKey _navigationKey = GlobalKey(); + final GlobalKey _navigationKey = GlobalKey(); GlobalKey get navigationKey => _navigationKey; diff --git a/lib/core/services/permission_service.dart b/lib/core/services/permission_service.dart index 5f68e79..4000c54 100644 --- a/lib/core/services/permission_service.dart +++ b/lib/core/services/permission_service.dart @@ -105,11 +105,11 @@ class PermissionService extends StoppableService { await checkEnabledAndPermission(); _serviceCheckTimer = - Timer.periodic(Duration(milliseconds: Constants.mediaPermissionCheckInterval), (_serviceTimer) async { + Timer.periodic(const Duration(milliseconds: Constants.mediaPermissionCheckInterval), (serviceTimer) async { if (!super.serviceStopped) { await checkEnabledAndPermission(); } else { - _serviceTimer.cancel(); + serviceTimer.cancel(); } }); _logger.d('PermissionService started'); diff --git a/lib/core/services/session_service.dart b/lib/core/services/session_service.dart index 073ae20..79ffd9f 100644 --- a/lib/core/services/session_service.dart +++ b/lib/core/services/session_service.dart @@ -31,7 +31,7 @@ class SessionService extends StoppableService { Future login(String url, String apiKey) async { setApiConfig(url, apiKey); - var session = new Session(url: url, apiKey: apiKey); + var session = Session(url: url, apiKey: apiKey); sessionController.add(session); await _storageService.storeSession(session); _logger.d('Session created'); diff --git a/lib/core/services/storage_service.dart b/lib/core/services/storage_service.dart index 2aa4c83..322c607 100644 --- a/lib/core/services/storage_service.dart +++ b/lib/core/services/storage_service.dart @@ -5,45 +5,45 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../models/session.dart'; class StorageService { - static const _SESSION_KEY = 'session'; - static const _LAST_URL_KEY = 'last_url'; - static const _STORAGE_PERMISSION_DIALOG_IGNORED = 'storage_permission_ignored'; + static const _sessionKey = 'session'; + static const _lastUrlKey = 'last_url'; + static const _storagePermissionDialogIgnoredKey = 'storage_permission_ignored'; Future storeLastUrl(String url) { - return _store(_LAST_URL_KEY, url); + return _store(_lastUrlKey, url); } Future retrieveLastUrl() async { - return await _retrieve(_LAST_URL_KEY); + return await _retrieve(_lastUrlKey); } Future hasLastUrl() async { - return await _exists(_LAST_URL_KEY); + return await _exists(_lastUrlKey); } Future storeSession(Session session) { - return _store(_SESSION_KEY, json.encode(session)); + return _store(_sessionKey, json.encode(session)); } Future retrieveSession() async { - var retrieve = await _retrieve(_SESSION_KEY); + var retrieve = await _retrieve(_sessionKey); return Session.fromJson(json.decode(retrieve!)); } Future hasSession() { - return _exists(_SESSION_KEY); + return _exists(_sessionKey); } Future removeSession() { - return _remove(_SESSION_KEY); + return _remove(_sessionKey); } Future storeStoragePermissionDialogIgnored() { - return _store(_STORAGE_PERMISSION_DIALOG_IGNORED, true.toString()); + return _store(_storagePermissionDialogIgnoredKey, true.toString()); } Future hasStoragePermissionDialogIgnored() { - return _exists(_STORAGE_PERMISSION_DIALOG_IGNORED); + return _exists(_storagePermissionDialogIgnoredKey); } Future _exists(String key) async { diff --git a/lib/core/services/user_service.dart b/lib/core/services/user_service.dart index e4ea69c..ef10096 100644 --- a/lib/core/services/user_service.dart +++ b/lib/core/services/user_service.dart @@ -26,7 +26,7 @@ class UserService { try { await _fileService.getHistory(); } on ServiceException catch (e) { - throw new ServiceException(code: ErrorCode.INVALID_API_KEY, message: e.message); + throw ServiceException(code: ErrorCode.invalidApiKey, message: e.message); } } } diff --git a/lib/core/util/formatter_util.dart b/lib/core/util/formatter_util.dart index 8043f13..ba606f7 100644 --- a/lib/core/util/formatter_util.dart +++ b/lib/core/util/formatter_util.dart @@ -13,6 +13,6 @@ class FormatterUtil { if (bytes <= 0) return "0 B"; const suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; var i = (log(bytes) / log(1024)).floor(); - return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) + ' ' + suffixes[i]; + return '${(bytes / pow(1024, i)).toStringAsFixed(decimals)} ${suffixes[i]}'; } } diff --git a/lib/core/viewmodels/about_model.dart b/lib/core/viewmodels/about_model.dart index 304781f..f3d7c7b 100644 --- a/lib/core/viewmodels/about_model.dart +++ b/lib/core/viewmodels/about_model.dart @@ -20,10 +20,10 @@ class AboutModel extends BaseModel { } Future _initPackageInfo() async { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); final PackageInfo info = await PackageInfo.fromPlatform(); packageInfo = info; - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } void openLink(String link) { diff --git a/lib/core/viewmodels/base_model.dart b/lib/core/viewmodels/base_model.dart index a8225ed..9fc7570 100644 --- a/lib/core/viewmodels/base_model.dart +++ b/lib/core/viewmodels/base_model.dart @@ -5,18 +5,18 @@ import '../../core/util/logger.dart'; import '../enums/viewstate.dart'; class BaseModel extends ChangeNotifier { - static const String STATE_VIEW = 'viewState'; - static const String STATE_MESSAGE = 'viewMessage'; + static const String stateViewKey = 'viewState'; + static const String stateMessageKey = 'viewMessage'; final Logger _logger = getLogger(); bool _isDisposed = false; - Map _stateMap = {STATE_VIEW: ViewState.Idle, STATE_MESSAGE: null}; + final Map _stateMap = {stateViewKey: ViewState.idle, stateMessageKey: null}; - ViewState? get state => _stateMap[STATE_VIEW] as ViewState?; + ViewState? get state => _stateMap[stateViewKey] as ViewState?; - String? get stateMessage => _stateMap[STATE_MESSAGE] as String?; + String? get stateMessage => _stateMap[stateMessageKey] as String?; bool getStateValueAsBoolean(String key) { if (_stateMap.containsKey(key) && _stateMap[key] is bool) { @@ -71,11 +71,11 @@ class BaseModel extends ChangeNotifier { } void setStateView(ViewState stateView) { - _setStateValue(STATE_VIEW, stateView); + _setStateValue(stateViewKey, stateView); } void setStateMessage(String? stateMessage) { - _setStateValue(STATE_MESSAGE, stateMessage); + _setStateValue(stateMessageKey, stateMessage); } @override diff --git a/lib/core/viewmodels/history_model.dart b/lib/core/viewmodels/history_model.dart index ced0e2c..4007ced 100644 --- a/lib/core/viewmodels/history_model.dart +++ b/lib/core/viewmodels/history_model.dart @@ -35,7 +35,7 @@ class HistoryModel extends BaseModel { void init() { _refreshTriggerSubscription = _refreshService.refreshEventController.stream.listen((event) { - if (event == RefreshEvent.RefreshHistory) { + if (event == RefreshEvent.refreshHistory) { _logger.d('History needs a refresh'); getHistory(); } @@ -43,13 +43,13 @@ class HistoryModel extends BaseModel { } Future getHistory() async { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); try { pastes.clear(); - History _history = await _fileService.getHistory(); - if (_history.items.isNotEmpty) { - _history.items.forEach((key, value) { + History history = await _fileService.getHistory(); + if (history.items.isNotEmpty) { + history.items.forEach((key, value) { var millisecondsSinceEpoch = int.parse(value.date) * 1000; pastes.add( UploadedPaste( @@ -66,8 +66,8 @@ class HistoryModel extends BaseModel { }); } - if (_history.multipasteItems.isNotEmpty) { - _history.multipasteItems.forEach((key, multiPaste) { + if (history.multipasteItems.isNotEmpty) { + history.multipasteItems.forEach((key, multiPaste) { var millisecondsSinceEpoch = int.parse(multiPaste.date) * 1000; pastes.add(UploadedPaste( id: key, @@ -97,19 +97,19 @@ class HistoryModel extends BaseModel { } else { errorMessage = translate('api.general_rest_error'); } - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) { + } else if (e is ServiceException && e.code == ErrorCode.socketError) { errorMessage = translate('api.socket_error'); - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) { + } else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { errorMessage = translate('api.socket_timeout'); } else { errorMessage = translate('app.unknown_error'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); _logger.e('An unknown error occurred', e); - throw e; + rethrow; } } - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } Future deletePaste(String id) async { @@ -123,7 +123,7 @@ class HistoryModel extends BaseModel { return; } - setStateView(ViewState.Busy); + setStateView(ViewState.busy); try { await _fileService.deletePaste(id); @@ -143,19 +143,19 @@ class HistoryModel extends BaseModel { } else { errorMessage = translate('api.general_rest_error'); } - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) { + } else if (e is ServiceException && e.code == ErrorCode.socketError) { errorMessage = translate('api.socket_error'); - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) { + } else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { errorMessage = translate('api.socket_timeout'); } else { errorMessage = translate('app.unknown_error'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); _logger.e('An unknown error occurred', e); - throw e; + rethrow; } } - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } void openLink(String link) { diff --git a/lib/core/viewmodels/login_model.dart b/lib/core/viewmodels/login_model.dart index 81dde0b..afa9182 100644 --- a/lib/core/viewmodels/login_model.dart +++ b/lib/core/viewmodels/login_model.dart @@ -20,12 +20,12 @@ import '../util/logger.dart'; import 'base_model.dart'; class LoginModel extends BaseModel { - TextEditingController _uriController = new TextEditingController(); + TextEditingController _uriController = TextEditingController(); - final TextEditingController _userNameController = new TextEditingController(); - final TextEditingController _passwordController = new TextEditingController(); + final TextEditingController _userNameController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); - final TextEditingController _apiKeyController = new TextEditingController(); + final TextEditingController _apiKeyController = TextEditingController(); TextEditingController get uriController => _uriController; @@ -44,23 +44,23 @@ class LoginModel extends BaseModel { String? errorMessage; void toggleLoginMethod() { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); useCredentialsLogin = !useCredentialsLogin; - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } void init() async { bool hasLastUrl = await _storageService.hasLastUrl(); if (hasLastUrl) { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); var s = await (_storageService.retrieveLastUrl() as FutureOr); if (s.isNotEmpty) { - _uriController = new TextEditingController(text: s); + _uriController = TextEditingController(text: s); } - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } } @@ -70,45 +70,45 @@ class LoginModel extends BaseModel { var password = passwordController.text; var apiKey = apiKeyController.text; - setStateView(ViewState.Busy); + setStateView(ViewState.busy); url = trim(url); username = trim(username); if (url.isEmpty) { errorMessage = translate('login.errors.empty_url'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return false; } if (!url.contains("https://") && !url.contains("http://")) { errorMessage = translate('login.errors.no_protocol'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return false; } bool validUri = Uri.parse(url).isAbsolute; if (!validUri || !isURL(url)) { errorMessage = translate('login.errors.invalid_url'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return false; } if (useCredentialsLogin) { if (username.isEmpty) { errorMessage = translate('login.errors.empty_username'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return false; } if (password.isEmpty) { errorMessage = translate('login.errors.empty_password'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return false; } } else { if (apiKey.isEmpty) { errorMessage = translate('login.errors.empty_apikey'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return false; } } @@ -117,14 +117,13 @@ class LoginModel extends BaseModel { try { if (useCredentialsLogin) { CreateApiKeyResponse apiKeyResponse = await _userService.createApiKey( - url, username, password, 'apikey', 'fbmobile-${new DateTime.now().millisecondsSinceEpoch}'); + url, username, password, 'apikey', 'fbmobile-${DateTime.now().millisecondsSinceEpoch}'); var newKey = apiKeyResponse.data['new_key']; if (newKey != null) { success = await _sessionService.login(url, newKey); } else { - throw new ServiceException( - code: ErrorCode.INVALID_API_KEY, message: translate('login.errors.invalid_api_key')); + throw ServiceException(code: ErrorCode.invalidApiKey, message: translate('login.errors.invalid_api_key')); } } else { _sessionService.setApiConfig(url, apiKey); @@ -146,25 +145,25 @@ class LoginModel extends BaseModel { } else { errorMessage = translate('api.general_rest_error'); } - } else if (e is ServiceException && e.code == ErrorCode.INVALID_API_KEY) { + } else if (e is ServiceException && e.code == ErrorCode.invalidApiKey) { errorMessage = translate('login.errors.invalid_api_key'); - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) { + } else if (e is ServiceException && e.code == ErrorCode.socketError) { errorMessage = translate('api.socket_error'); - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) { + } else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { errorMessage = translate('api.socket_timeout'); } else { errorMessage = translate('app.unknown_error'); _sessionService.logout(); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); _logger.e('An unknown error occurred', e); - throw e; + rethrow; } if (errorMessage!.isNotEmpty) { _sessionService.logout(); } - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return success; } diff --git a/lib/core/viewmodels/profile_model.dart b/lib/core/viewmodels/profile_model.dart index 90708ab..edf835d 100644 --- a/lib/core/viewmodels/profile_model.dart +++ b/lib/core/viewmodels/profile_model.dart @@ -64,9 +64,9 @@ class ProfileModel extends BaseModel { } else { errorMessage = translate('api.general_rest_error'); } - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) { + } else if (e is ServiceException && e.code == ErrorCode.socketError) { errorMessage = translate('api.socket_error'); - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) { + } else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { errorMessage = translate('api.socket_timeout'); } else { errorMessage = translate('app.unknown_error'); @@ -74,7 +74,7 @@ class ProfileModel extends BaseModel { _sessionService.logout(); setStateBoolValue(_configurationButtonLoading, false); _logger.e('An unknown error occurred', e); - throw e; + rethrow; } } diff --git a/lib/core/viewmodels/startup_model.dart b/lib/core/viewmodels/startup_model.dart index 3ba1263..767e1cb 100644 --- a/lib/core/viewmodels/startup_model.dart +++ b/lib/core/viewmodels/startup_model.dart @@ -12,16 +12,16 @@ class StartUpViewModel extends BaseModel { final NavigationService _navigationService = locator(); Future handleStartUpLogic() async { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); setStateMessage(translate('startup.init')); - await Future.delayed(Duration(milliseconds: 150)); + await Future.delayed(const Duration(milliseconds: 150)); setStateMessage(translate('startup.start_services')); await _sessionService.start(); - await Future.delayed(Duration(milliseconds: 150)); + await Future.delayed(const Duration(milliseconds: 150)); _navigationService.navigateAndReplaceTo(HomeView.routeName); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } } diff --git a/lib/core/viewmodels/upload_model.dart b/lib/core/viewmodels/upload_model.dart index 8be82a9..2be59b6 100644 --- a/lib/core/viewmodels/upload_model.dart +++ b/lib/core/viewmodels/upload_model.dart @@ -31,7 +31,7 @@ class UploadModel extends BaseModel { final LinkService _linkService = locator(); final RefreshService _refreshService = locator(); - TextEditingController _pasteTextController = TextEditingController(); + final TextEditingController _pasteTextController = TextEditingController(); bool pasteTextTouched = false; late StreamSubscription _intentDataStreamSubscription; @@ -53,8 +53,8 @@ class UploadModel extends BaseModel { // For sharing images coming from outside the app while the app is in the memory _intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream().listen((List value) { - if (value.length > 0) { - setStateView(ViewState.Busy); + if (value.isNotEmpty) { + setStateView(ViewState.busy); paths = value.map((sharedFile) { return PlatformFile.fromMap({ 'path': sharedFile.path, @@ -63,7 +63,7 @@ class UploadModel extends BaseModel { 'bytes': null }); }).toList(); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } }, onError: (err) { _errorIntentHandle(err); @@ -71,8 +71,8 @@ class UploadModel extends BaseModel { // For sharing images coming from outside the app while the app is closed ReceiveSharingIntent.getInitialMedia().then((List value) { - if (value.length > 0) { - setStateView(ViewState.Busy); + if (value.isNotEmpty) { + setStateView(ViewState.busy); paths = value.map((sharedFile) { return PlatformFile.fromMap({ 'path': sharedFile.path, @@ -81,7 +81,7 @@ class UploadModel extends BaseModel { 'bytes': null }); }).toList(); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } }, onError: (err) { _errorIntentHandle(err); @@ -90,9 +90,9 @@ class UploadModel extends BaseModel { // For sharing or opening urls/text coming from outside the app while the app is in the memory _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream().listen((String value) { if (value.isNotEmpty) { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); pasteTextController.text = value; - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } }, onError: (err) { _errorIntentHandle(err); @@ -101,9 +101,9 @@ class UploadModel extends BaseModel { // For sharing or opening urls/text coming from outside the app while the app is closed ReceiveSharingIntent.getInitialText().then((String? value) { if (value != null && value.isNotEmpty) { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); pasteTextController.text = value; - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } }, onError: (err) { _errorIntentHandle(err); @@ -111,14 +111,14 @@ class UploadModel extends BaseModel { } void _errorIntentHandle(err) { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); errorMessage = translate('upload.retrieval_intent'); _logger.e('Error while retrieving shared data: $err'); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } String? generatePasteLinks(Map? uploads, String url) { - if (uploads != null && uploads.length > 0) { + if (uploads != null && uploads.isNotEmpty) { var links = ''; uploads.forEach((id, isMulti) { @@ -134,13 +134,13 @@ class UploadModel extends BaseModel { } void toggleCreateMulti() { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); createMulti = !createMulti; - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } void openFileExplorer() async { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); setStateMessage(translate('upload.file_explorer_open')); loadingPath = true; @@ -163,40 +163,40 @@ class UploadModel extends BaseModel { fileName = paths != null ? paths!.map((e) => e.name).toString() : '...'; setStateMessage(null); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } void clearCachedFiles() async { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); await FilePicker.platform.clearTemporaryFiles(); paths = null; fileName = null; errorMessage = null; - setStateView(ViewState.Idle); + setStateView(ViewState.idle); } Future?> upload() async { - setStateView(ViewState.Busy); + setStateView(ViewState.busy); setStateMessage(translate('upload.uploading_now')); - Map uploadedPasteIds = new Map(); + Map uploadedPasteIds = {}; try { List? files; Map? additionalFiles; if (pasteTextController.text.isNotEmpty) { - additionalFiles = Map.from( - {'paste-${(new DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': pasteTextController.text}); + additionalFiles = + Map.from({'paste-${(DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': pasteTextController.text}); } - if (paths != null && paths!.length > 0) { - files = paths!.map((e) => new File(e.path!)).toList(); + if (paths != null && paths!.isNotEmpty) { + files = paths!.map((e) => File(e.path!)).toList(); } UploadedResponse response = await _fileService.uploadPaste(files, additionalFiles); - response.data.ids.forEach((element) { + for (var element in response.data.ids) { uploadedPasteIds.putIfAbsent(element, () => false); - }); + } if (createMulti && response.data.ids.length > 1) { UploadedMultiResponse multiResponse = await _fileService.uploadMultiPaste(response.data.ids); @@ -205,7 +205,7 @@ class UploadModel extends BaseModel { clearCachedFiles(); _pasteTextController.clear(); - _refreshService.addEvent(RefreshEvent.RefreshHistory); + _refreshService.addEvent(RefreshEvent.refreshHistory); errorMessage = null; return uploadedPasteIds; } catch (e) { @@ -226,21 +226,21 @@ class UploadModel extends BaseModel { } else { errorMessage = translate('api.general_rest_error'); } - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) { + } else if (e is ServiceException && e.code == ErrorCode.socketError) { errorMessage = translate('api.socket_error'); - } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) { + } else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { errorMessage = translate('api.socket_timeout'); } else { errorMessage = translate('app.unknown_error'); setStateMessage(null); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); _logger.e('An unknown error occurred', e); - throw e; + rethrow; } } setStateMessage(null); - setStateView(ViewState.Idle); + setStateView(ViewState.idle); return null; } diff --git a/lib/ui/app_router.dart b/lib/ui/app_router.dart index 4da33d7..f43647e 100644 --- a/lib/ui/app_router.dart +++ b/lib/ui/app_router.dart @@ -14,15 +14,15 @@ class AppRouter { static Route generateRoute(RouteSettings settings) { switch (settings.name) { case StartUpView.routeName: - return MaterialPageRoute(builder: (_) => StartUpView()); + return MaterialPageRoute(builder: (_) => const StartUpView()); case AboutView.routeName: - return MaterialPageRoute(builder: (_) => AboutView()); + return MaterialPageRoute(builder: (_) => const AboutView()); case HomeView.routeName: - return MaterialPageRoute(builder: (_) => TabBarContainerView()); + return MaterialPageRoute(builder: (_) => const TabBarContainerView()); case LoginView.routeName: return MaterialPageRoute(builder: (_) => LoginView()); case ProfileView.routeName: - return MaterialPageRoute(builder: (_) => ProfileView()); + return MaterialPageRoute(builder: (_) => const ProfileView()); default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/ui/shared/ui_helpers.dart b/lib/ui/shared/ui_helpers.dart index cbf8ed6..d21f039 100644 --- a/lib/ui/shared/ui_helpers.dart +++ b/lib/ui/shared/ui_helpers.dart @@ -1,27 +1,27 @@ import 'package:flutter/material.dart'; class UIHelper { - static const double _VerticalSpaceSmall = 10.0; - static const double _VerticalSpaceMedium = 20.0; - static const double _VerticalSpaceLarge = 60.0; + static const double _verticalSpaceSmall = 10.0; + static const double _verticalSpaceMedium = 20.0; + static const double _verticalSpaceLarge = 60.0; - static const double _HorizontalSpaceSmall = 10.0; - static const double _HorizontalSpaceMedium = 20.0; - static const double HorizontalSpaceLarge = 60.0; + static const double _horizontalSpaceSmall = 10.0; + static const double _horizontalSpaceMedium = 20.0; + static const double _horizontalSpaceLarge = 60.0; - /// Returns a vertical space with height set to [_VerticalSpaceSmall] + /// Returns a vertical space with height set to [_verticalSpaceSmall] static Widget verticalSpaceSmall() { - return verticalSpace(_VerticalSpaceSmall); + return verticalSpace(_verticalSpaceSmall); } - /// Returns a vertical space with height set to [_VerticalSpaceMedium] + /// Returns a vertical space with height set to [_verticalSpaceMedium] static Widget verticalSpaceMedium() { - return verticalSpace(_VerticalSpaceMedium); + return verticalSpace(_verticalSpaceMedium); } - /// Returns a vertical space with height set to [_VerticalSpaceLarge] + /// Returns a vertical space with height set to [_verticalSpaceLarge] static Widget verticalSpaceLarge() { - return verticalSpace(_VerticalSpaceLarge); + return verticalSpace(_verticalSpaceLarge); } /// Returns a vertical space equal to the [height] supplied @@ -29,19 +29,19 @@ class UIHelper { return Container(height: height); } - /// Returns a vertical space with height set to [_HorizontalSpaceSmall] + /// Returns a vertical space with height set to [_horizontalSpaceSmall] static Widget horizontalSpaceSmall() { - return horizontalSpace(_HorizontalSpaceSmall); + return horizontalSpace(_horizontalSpaceSmall); } - /// Returns a vertical space with height set to [_HorizontalSpaceMedium] + /// Returns a vertical space with height set to [_horizontalSpaceMedium] static Widget horizontalSpaceMedium() { - return horizontalSpace(_HorizontalSpaceMedium); + return horizontalSpace(_horizontalSpaceMedium); } - /// Returns a vertical space with height set to [HorizontalSpaceLarge] + /// Returns a vertical space with height set to [_horizontalSpaceLarge] static Widget horizontalSpaceLarge() { - return horizontalSpace(HorizontalSpaceLarge); + return horizontalSpace(_horizontalSpaceLarge); } /// Returns a vertical space equal to the [width] supplied diff --git a/lib/ui/views/about_view.dart b/lib/ui/views/about_view.dart index 524e68b..ddd4c45 100644 --- a/lib/ui/views/about_view.dart +++ b/lib/ui/views/about_view.dart @@ -12,6 +12,8 @@ import 'base_view.dart'; class AboutView extends StatelessWidget { static const routeName = '/about'; + const AboutView({super.key}); + @override Widget build(BuildContext context) { final logo = Hero( @@ -30,13 +32,13 @@ class AboutView extends StatelessWidget { title: Text(translate('titles.about')), enableAbout: false, ), - body: model.state == ViewState.Busy - ? Center(child: CircularProgressIndicator()) + body: model.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) : Container( - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), child: ListView( shrinkWrap: true, - padding: EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10, top: 10), + padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10, top: 10), children: [ UIHelper.verticalSpaceMedium(), Center(child: logo), @@ -75,7 +77,7 @@ class AboutView extends StatelessWidget { Center( child: Linkify( text: translate('about.website'), - options: LinkifyOptions(humanize: false), + options: const LinkifyOptions(humanize: false), onOpen: (link) => model.openLink(link.url), ), ) diff --git a/lib/ui/views/base_view.dart b/lib/ui/views/base_view.dart index c4ba15a..224a819 100644 --- a/lib/ui/views/base_view.dart +++ b/lib/ui/views/base_view.dart @@ -10,7 +10,7 @@ class BaseView extends StatefulWidget { final Widget Function(BuildContext context, T model, Widget? child)? builder; final Function(T)? onModelReady; - BaseView({this.builder, this.onModelReady}); + const BaseView({super.key, this.builder, this.onModelReady}); @override _BaseViewState createState() => _BaseViewState(); diff --git a/lib/ui/views/history_view.dart b/lib/ui/views/history_view.dart index a9f5e80..53e659b 100644 --- a/lib/ui/views/history_view.dart +++ b/lib/ui/views/history_view.dart @@ -18,6 +18,8 @@ import 'base_view.dart'; class HistoryView extends StatelessWidget { static const routeName = '/history'; + const HistoryView({super.key}); + @override Widget build(BuildContext context) { return BaseView( @@ -33,15 +35,15 @@ class HistoryView extends StatelessWidget { Widget _render(HistoryModel model, BuildContext context) { var url = Provider.of(context).url; - return model.state == ViewState.Busy - ? Center(child: CircularProgressIndicator()) + return model.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) : (model.errorMessage == null ? Container( - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), child: RefreshIndicator( onRefresh: () async => await model.getHistory(), child: _renderItems(model, url, context))) : Container( - padding: EdgeInsets.all(25), + padding: const EdgeInsets.all(25), child: CenteredErrorRow( model.errorMessage, retryCallback: () => model.getHistory(), @@ -51,8 +53,8 @@ class HistoryView extends StatelessWidget { Widget _renderItems(HistoryModel model, String url, BuildContext context) { List cards = []; - if (model.pastes.length > 0) { - model.pastes.reversed.forEach((paste) { + if (model.pastes.isNotEmpty) { + for (var paste in model.pastes.reversed) { List widgets = []; var fullPasteUrl = PasteUtil.generateLink(url, paste.id); @@ -71,7 +73,7 @@ class HistoryView extends StatelessWidget { var copyWidget = ListTile( title: Text(translate('history.copy_link.description')), trailing: IconButton( - icon: Icon(Icons.copy, color: blueColor, textDirection: TextDirection.ltr), + icon: const Icon(Icons.copy, color: blueColor, textDirection: TextDirection.ltr), onPressed: () { FlutterClipboard.copy(fullPasteUrl).then((value) { final snackBar = SnackBar( @@ -83,7 +85,7 @@ class HistoryView extends StatelessWidget { }, ), content: Text(translate('history.copy_link.copied')), - duration: Duration(seconds: 10), + duration: const Duration(seconds: 10), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }); @@ -92,7 +94,7 @@ class HistoryView extends StatelessWidget { var deleteWidget = ListTile( title: Text(translate('history.delete')), trailing: IconButton( - icon: Icon(Icons.delete, color: redColor), + icon: const Icon(Icons.delete, color: redColor), onPressed: () { model.deletePaste(paste.id); })); @@ -120,13 +122,13 @@ class HistoryView extends StatelessWidget { widgets.add(fileSizeWidget); widgets.add(mimeTypeWidget); } else { - paste.items!.forEach((element) { + for (var element in paste.items!) { widgets.add(ListTile( title: Text(element!), subtitle: Text(translate('history.multipaste_element')), trailing: _renderOpenInBrowser(model, '$url/$element'), )); - }); + } } widgets.add(dateWidget); @@ -135,7 +137,7 @@ class HistoryView extends StatelessWidget { widgets.add(deleteWidget); var expandable = ExpandableTheme( - data: ExpandableThemeData( + data: const ExpandableThemeData( iconPlacement: ExpandablePanelIconPlacement.right, headerAlignment: ExpandablePanelHeaderAlignment.center, hasIcon: true, @@ -146,7 +148,7 @@ class HistoryView extends StatelessWidget { header: InkWell( child: Text( paste.id, - style: TextStyle(color: blueColor), + style: const TextStyle(color: blueColor), textAlign: TextAlign.left, )), expanded: Column( @@ -162,15 +164,15 @@ class HistoryView extends StatelessWidget { trailing: Wrap(children: [ openInBrowserButton, IconButton( - icon: Icon(Icons.share, color: blueColor, textDirection: TextDirection.ltr), + icon: const Icon(Icons.share, color: blueColor, textDirection: TextDirection.ltr), onPressed: () async { await Share.share(fullPasteUrl); }) ]), - subtitle: Text(!paste.isMulti! ? paste.filename! : '', style: TextStyle(fontStyle: FontStyle.italic)), + subtitle: Text(!paste.isMulti! ? paste.filename! : '', style: const TextStyle(fontStyle: FontStyle.italic)), ), )); - }); + } } else { cards.add(Card( child: ListTile( @@ -181,14 +183,14 @@ class HistoryView extends StatelessWidget { return ListView( padding: const EdgeInsets.all(8), + physics: const AlwaysScrollableScrollPhysics(), children: cards, - physics: AlwaysScrollableScrollPhysics(), ); } Widget _renderOpenInBrowser(HistoryModel model, String url) { return IconButton( - icon: Icon(Icons.open_in_new, color: blueColor, textDirection: TextDirection.ltr), + icon: const Icon(Icons.open_in_new, color: blueColor, textDirection: TextDirection.ltr), onPressed: () { return model.openLink(url); }); diff --git a/lib/ui/views/home_view.dart b/lib/ui/views/home_view.dart index 3214916..3c9f3c3 100644 --- a/lib/ui/views/home_view.dart +++ b/lib/ui/views/home_view.dart @@ -9,12 +9,14 @@ import 'base_view.dart'; class HomeView extends StatelessWidget { static const routeName = '/home'; + const HomeView({super.key}); + @override Widget build(BuildContext context) { return BaseView( builder: (context, model, child) => Scaffold( appBar: MyAppBar(title: Text(translate('app.title'))), - body: model.state == ViewState.Busy ? Center(child: CircularProgressIndicator()) : Container()), + body: model.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) : Container()), ); } } diff --git a/lib/ui/views/login_view.dart b/lib/ui/views/login_view.dart index 86b635b..87125a6 100644 --- a/lib/ui/views/login_view.dart +++ b/lib/ui/views/login_view.dart @@ -20,6 +20,8 @@ class LoginView extends StatelessWidget { final NavigationService _navigationService = locator(); final DialogService _dialogService = locator(); + LoginView({super.key}); + @override Widget build(BuildContext context) { final logo = Hero( @@ -35,11 +37,11 @@ class LoginView extends StatelessWidget { onModelReady: (model) => model.init(), builder: (context, model, child) => Scaffold( appBar: MyAppBar(title: Text(translate('titles.login'))), - body: model.state == ViewState.Busy - ? Center(child: CircularProgressIndicator()) + body: model.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) : ListView( shrinkWrap: true, - padding: EdgeInsets.only(left: 10.0, right: 10.0), + padding: const EdgeInsets.only(left: 10.0, right: 10.0), children: [ UIHelper.verticalSpaceMedium(), Center(child: logo), @@ -50,7 +52,7 @@ class LoginView extends StatelessWidget { alignment: WrapAlignment.center, children: [ InkWell( - child: Icon(Icons.help), + child: const Icon(Icons.help), onTap: () { _dialogService.showDialog( title: translate('login.compatibility_dialog.title'), @@ -79,7 +81,7 @@ class LoginView extends StatelessWidget { apiKeyController: model.apiKeyController), UIHelper.verticalSpaceMedium(), ElevatedButton.icon( - icon: Icon(Icons.login, color: blueColor), + icon: const Icon(Icons.login, color: blueColor), label: Text(translate('login.button')), onPressed: () async { var loginSuccess = await model.login(); diff --git a/lib/ui/views/navbar_authenticated.dart b/lib/ui/views/navbar_authenticated.dart index 681ee9a..18b95fa 100644 --- a/lib/ui/views/navbar_authenticated.dart +++ b/lib/ui/views/navbar_authenticated.dart @@ -8,6 +8,8 @@ import '../shared/app_colors.dart'; import 'history_view.dart'; class AuthenticatedNavBarView extends StatefulWidget { + const AuthenticatedNavBarView({super.key}); + @override AuthenticatedNavBarState createState() => AuthenticatedNavBarState(); } @@ -52,17 +54,17 @@ class AuthenticatedNavBarState extends State with Singl Container( color: myColor, alignment: Alignment.center, - child: UploadView(), + child: const UploadView(), ), Container( color: myColor, alignment: Alignment.center, - child: HistoryView(), + child: const HistoryView(), ), Container( color: myColor, alignment: Alignment.center, - child: ProfileView(), + child: const ProfileView(), ), ][_currentTabIndex], ); diff --git a/lib/ui/views/profile_view.dart b/lib/ui/views/profile_view.dart index 965b948..e9e3e2c 100644 --- a/lib/ui/views/profile_view.dart +++ b/lib/ui/views/profile_view.dart @@ -15,6 +15,8 @@ import 'base_view.dart'; class ProfileView extends StatelessWidget { static const routeName = '/profile'; + const ProfileView({super.key}); + @override Widget build(BuildContext context) { return BaseView( @@ -26,8 +28,8 @@ class ProfileView extends StatelessWidget { var url = Provider.of(context).url; var apiKey = Provider.of(context).apiKey; - return model.state == ViewState.Busy - ? Center(child: CircularProgressIndicator()) + return model.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) : ListView( children: [ UIHelper.verticalSpaceMedium(), @@ -45,7 +47,7 @@ class ProfileView extends StatelessWidget { child: Linkify( onOpen: (link) => model.openLink(link.url), text: translate('profile.connection', args: {'url': url}), - options: LinkifyOptions(humanize: false), + options: const LinkifyOptions(humanize: false), ))), UIHelper.verticalSpaceMedium(), Padding( @@ -56,12 +58,12 @@ class ProfileView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - CircularProgressIndicator(), + const CircularProgressIndicator(), Text(translate('profile.show_config_loading')), ], )) : ElevatedButton.icon( - icon: Icon(Icons.settings, color: blueColor), + icon: const Icon(Icons.settings, color: blueColor), label: Text( translate('profile.show_config'), ), @@ -72,7 +74,7 @@ class ProfileView extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 25.0, right: 25.0), child: ElevatedButton.icon( - icon: Icon(Icons.lock, color: orangeColor), + icon: const Icon(Icons.lock, color: orangeColor), label: Text( translate('profile.reveal_api_key'), ), @@ -83,7 +85,7 @@ class ProfileView extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 25.0, right: 25.0), child: ElevatedButton.icon( - icon: Icon(Icons.exit_to_app, color: redColor), + icon: const Icon(Icons.exit_to_app, color: redColor), label: Text( translate('profile.logout'), ), diff --git a/lib/ui/views/startup_view.dart b/lib/ui/views/startup_view.dart index b0273ac..e37f642 100644 --- a/lib/ui/views/startup_view.dart +++ b/lib/ui/views/startup_view.dart @@ -7,19 +7,21 @@ import '../../core/viewmodels/startup_model.dart'; class StartUpView extends StatelessWidget { static const routeName = '/'; + const StartUpView({super.key}); + @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( viewModelBuilder: () => StartUpViewModel(), onModelReady: (model) => model.handleStartUpLogic(), builder: (context, model, child) => Scaffold( - body: model.state == ViewState.Busy + body: model.state == ViewState.busy ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - CircularProgressIndicator(), + const CircularProgressIndicator(), (model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container()) ])) : Container())); diff --git a/lib/ui/views/tabbar_container_view.dart b/lib/ui/views/tabbar_container_view.dart index a166355..1228005 100644 --- a/lib/ui/views/tabbar_container_view.dart +++ b/lib/ui/views/tabbar_container_view.dart @@ -6,17 +6,17 @@ import 'package:provider/provider.dart'; import '../../core/models/session.dart'; class TabBarContainerView extends StatelessWidget { + const TabBarContainerView({super.key}); + @override Widget build(BuildContext context) { Session? currentSession = Provider.of(context); bool isAuthenticated = currentSession != null ? currentSession.apiKey.isNotEmpty : false; if (isAuthenticated) { - return AuthenticatedNavBarView(); + return const AuthenticatedNavBarView(); } - return Container( - child: LoginView(), - ); + return LoginView(); } } diff --git a/lib/ui/views/upload_view.dart b/lib/ui/views/upload_view.dart index 589e70a..21048b6 100644 --- a/lib/ui/views/upload_view.dart +++ b/lib/ui/views/upload_view.dart @@ -14,6 +14,8 @@ import 'base_view.dart'; class UploadView extends StatelessWidget { static const routeName = '/upload'; + const UploadView({super.key}); + @override Widget build(BuildContext context) { return BaseView( @@ -23,19 +25,19 @@ class UploadView extends StatelessWidget { } bool _isUploadButtonEnabled(UploadModel model) { - return model.pasteTextTouched || (model.paths != null && model.paths!.length > 0); + return model.pasteTextTouched || (model.paths != null && model.paths!.isNotEmpty); } Widget _render(UploadModel model, BuildContext context) { var url = Provider.of(context).url; - return model.state == ViewState.Busy + return model.state == ViewState.busy ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - CircularProgressIndicator(), + const CircularProgressIndicator(), (model.stateMessage != null && model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container()) ])) : ListView(children: [ @@ -50,15 +52,15 @@ class UploadView extends StatelessWidget { minLines: 1, maxLines: 7, decoration: InputDecoration( - prefixIcon: Icon( + prefixIcon: const Icon( Icons.text_snippet, ), suffixIcon: IconButton( onPressed: () => model.pasteTextController.clear(), - icon: Icon(Icons.clear), + icon: const Icon(Icons.clear), ), hintText: translate('upload.text_to_be_pasted'), - contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), + contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)), ), controller: model.pasteTextController)), @@ -75,14 +77,14 @@ class UploadView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ ElevatedButton.icon( - icon: Icon(Icons.file_copy_sharp, color: blueColor), + icon: const Icon(Icons.file_copy_sharp, color: blueColor), onPressed: () => model.openFileExplorer(), label: Text( translate('upload.open_file_explorer'), )), ElevatedButton.icon( - icon: Icon(Icons.cancel, color: orangeColor), - onPressed: model.paths != null && model.paths!.length > 0 + icon: const Icon(Icons.cancel, color: orangeColor), + onPressed: model.paths != null && model.paths!.isNotEmpty ? () => model.clearCachedFiles() : null, label: Text( @@ -126,13 +128,13 @@ class UploadView extends StatelessWidget { }, ), content: Text(translate('upload.uploaded')), - duration: Duration(seconds: 10), + duration: const Duration(seconds: 10), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }); } }, - icon: Icon(Icons.upload_rounded, color: greenColor), + icon: const Icon(Icons.upload_rounded, color: greenColor), label: Text( translate('upload.upload'), )), @@ -144,9 +146,9 @@ class UploadView extends StatelessWidget { : Container(), Builder( builder: (BuildContext context) => model.loadingPath - ? Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: const CircularProgressIndicator(), + ? const Padding( + padding: EdgeInsets.only(bottom: 10.0), + child: CircularProgressIndicator(), ) : model.paths != null ? Container( @@ -159,7 +161,7 @@ class UploadView extends StatelessWidget { final String name = (isMultiPath ? model.paths!.map((e) => e.name).toList()[index] : model.fileName ?? '...'); - final path = model.paths!.length > 0 + final path = model.paths!.isNotEmpty ? model.paths!.map((e) => e.path).toList()[index].toString() : ''; diff --git a/lib/ui/widgets/about_iconbutton.dart b/lib/ui/widgets/about_iconbutton.dart index 5c0b072..2c700b6 100644 --- a/lib/ui/widgets/about_iconbutton.dart +++ b/lib/ui/widgets/about_iconbutton.dart @@ -5,14 +5,14 @@ import '../../locator.dart'; import '../../ui/views/about_view.dart'; class AboutIconButton extends StatelessWidget { - AboutIconButton(); + AboutIconButton({super.key}); final NavigationService _navigationService = locator(); @override Widget build(BuildContext context) { return IconButton( - icon: Icon(Icons.help), + icon: const Icon(Icons.help), onPressed: () { _navigationService.navigateTo(AboutView.routeName); }); diff --git a/lib/ui/widgets/centered_error_row.dart b/lib/ui/widgets/centered_error_row.dart index 9e43cc9..b7fa563 100644 --- a/lib/ui/widgets/centered_error_row.dart +++ b/lib/ui/widgets/centered_error_row.dart @@ -6,7 +6,7 @@ class CenteredErrorRow extends StatelessWidget { final Function? retryCallback; final String? message; - CenteredErrorRow(this.message, {this.retryCallback}); + const CenteredErrorRow(this.message, {super.key, this.retryCallback}); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class CenteredErrorRow extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded(child: Center(child: Text(message!, style: TextStyle(color: redColor)))), + Expanded(child: Center(child: Text(message!, style: const TextStyle(color: redColor)))), ], ), (retryCallback != null @@ -30,7 +30,7 @@ class CenteredErrorRow extends StatelessWidget { children: [ Center( child: IconButton( - icon: Icon(Icons.refresh), + icon: const Icon(Icons.refresh), color: primaryAccentColor, onPressed: () { retryCallback!(); diff --git a/lib/ui/widgets/login_header_apikey.dart b/lib/ui/widgets/login_header_apikey.dart index 29c8622..90a90fe 100644 --- a/lib/ui/widgets/login_header_apikey.dart +++ b/lib/ui/widgets/login_header_apikey.dart @@ -10,18 +10,19 @@ class LoginApiKeyHeaders extends StatelessWidget { final String? validationMessage; - LoginApiKeyHeaders({required this.uriController, required this.apiKeyController, this.validationMessage}); + const LoginApiKeyHeaders( + {super.key, required this.uriController, required this.apiKeyController, this.validationMessage}); @override Widget build(BuildContext context) { return Column(children: [ - this.validationMessage != null ? Text(validationMessage!, style: TextStyle(color: redColor)) : Container(), - LoginTextField(uriController, translate('login.url_placeholder'), Icon(Icons.link), + validationMessage != null ? Text(validationMessage!, style: const TextStyle(color: redColor)) : Container(), + LoginTextField(uriController, translate('login.url_placeholder'), const Icon(Icons.link), keyboardType: TextInputType.url), LoginTextField( apiKeyController, translate('login.apikey_placeholder'), - Icon(Icons.vpn_key), + const Icon(Icons.vpn_key), obscureText: true, ), ]); diff --git a/lib/ui/widgets/login_header_credentials.dart b/lib/ui/widgets/login_header_credentials.dart index f51e017..9657a66 100644 --- a/lib/ui/widgets/login_header_credentials.dart +++ b/lib/ui/widgets/login_header_credentials.dart @@ -11,8 +11,9 @@ class LoginCredentialsHeaders extends StatelessWidget { final String? validationMessage; - LoginCredentialsHeaders( - {required this.uriController, + const LoginCredentialsHeaders( + {super.key, + required this.uriController, required this.usernameController, required this.passwordController, this.validationMessage}); @@ -20,12 +21,12 @@ class LoginCredentialsHeaders extends StatelessWidget { @override Widget build(BuildContext context) { return Column(children: [ - this.validationMessage != null ? Text(validationMessage!, style: TextStyle(color: redColor)) : Container(), - LoginTextField(uriController, translate('login.url_placeholder'), Icon(Icons.link), + validationMessage != null ? Text(validationMessage!, style: const TextStyle(color: redColor)) : Container(), + LoginTextField(uriController, translate('login.url_placeholder'), const Icon(Icons.link), keyboardType: TextInputType.url), - LoginTextField(usernameController, translate('login.username_placeholder'), Icon(Icons.person), + LoginTextField(usernameController, translate('login.username_placeholder'), const Icon(Icons.person), keyboardType: TextInputType.name), - LoginTextField(passwordController, translate('login.password_placeholder'), Icon(Icons.vpn_key), + LoginTextField(passwordController, translate('login.password_placeholder'), const Icon(Icons.vpn_key), obscureText: true), ]); } diff --git a/lib/ui/widgets/login_text_field.dart b/lib/ui/widgets/login_text_field.dart index c0a6987..56c0e85 100644 --- a/lib/ui/widgets/login_text_field.dart +++ b/lib/ui/widgets/login_text_field.dart @@ -9,14 +9,14 @@ class LoginTextField extends StatelessWidget { final bool obscureText; final Widget prefixIcon; - LoginTextField(this.controller, this.placeHolder, this.prefixIcon, - {this.keyboardType = TextInputType.text, this.obscureText = false}); + const LoginTextField(this.controller, this.placeHolder, this.prefixIcon, + {super.key, this.keyboardType = TextInputType.text, this.obscureText = false}); @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.symmetric(horizontal: 10.0), - margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), + padding: const EdgeInsets.symmetric(horizontal: 10.0), + margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), height: 50.0, alignment: Alignment.centerLeft, decoration: BoxDecoration(color: whiteColor, borderRadius: BorderRadius.circular(10.0)), @@ -26,11 +26,11 @@ class LoginTextField extends StatelessWidget { decoration: InputDecoration( suffixIcon: IconButton( onPressed: () => controller.clear(), - icon: Icon(Icons.clear), + icon: const Icon(Icons.clear), ), prefixIcon: prefixIcon, hintText: placeHolder, - contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), + contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)), ), controller: controller), diff --git a/lib/ui/widgets/my_appbar.dart b/lib/ui/widgets/my_appbar.dart index 92940a6..9e802e4 100644 --- a/lib/ui/widgets/my_appbar.dart +++ b/lib/ui/widgets/my_appbar.dart @@ -10,9 +10,7 @@ class MyAppBar extends AppBar { : super(key: key, title: Row(children: [title]), actions: _renderIconButtons(actionWidgets, enableAbout)); static List _renderIconButtons(List? actionWidgets, bool aboutEnabled) { - if (actionWidgets == null) { - actionWidgets = []; - } + actionWidgets ??= []; List widgets = [...actionWidgets]; diff --git a/pubspec.lock b/pubspec.lock index 37a0741..7771c72 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -70,7 +70,7 @@ packages: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "2.3.2" + version: "2.3.3" build_runner_core: dependency: transitive description: @@ -237,6 +237,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.2" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" flutter_localizations: dependency: "direct main" description: flutter @@ -316,7 +323,7 @@ packages: source: hosted version: "4.0.2" intl: - dependency: transitive + dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" @@ -357,6 +364,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.1.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" logger: dependency: "direct main" description: @@ -428,7 +442,7 @@ packages: source: hosted version: "2.0.1" path: - dependency: transitive + dependency: "direct main" description: name: path url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index b6b258b..fcd276a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,13 +41,16 @@ dependencies: package_info_plus: 3.0.2 json_annotation: 4.7.0 dynamic_color: 1.5.4 + intl: 0.17.0 + path: 1.8.2 dev_dependencies: flutter_test: sdk: flutter - build_runner: 2.3.2 + build_runner: 2.3.3 built_value_generator: 8.4.2 json_serializable: 6.5.4 + flutter_lints: 2.0.1 # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec