Compare commits

..

3 commits

Author SHA1 Message Date
a65c7d9253 Merge pull request 'Added proper linting #noissue' (#9) from feature/implement-linting into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #9
2023-01-04 20:42:53 +00:00
bac39aebdf Update build runner #noissue
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-04 21:23:21 +01:00
b55e932204 Added proper linting #noissue
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-04 21:17:54 +01:00
47 changed files with 322 additions and 256 deletions

View file

@ -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

30
analysis_options.yaml Normal file
View file

@ -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

View file

@ -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<NavigationService>().navigationKey,
home: StartUpView(),
home: const StartUpView(),
supportedLocales: localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
);

View file

@ -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
}

View file

@ -1 +1 @@
enum RefreshEvent { RefreshHistory }
enum RefreshEvent { refreshHistory }

View file

@ -1 +1 @@
enum ViewState { Idle, Busy }
enum ViewState { idle, busy }

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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<DialogManager> {
DialogService _dialogService = locator<DialogService>();
final DialogService _dialogService = locator<DialogService>();
@override
void initState() {

View file

@ -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<LifeCycleManager> 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();
}
});
}
}
}

View file

@ -11,7 +11,7 @@ import '../models/rest/uploaded_response.dart';
import '../services/api.dart';
class FileRepository {
Api _api = locator<Api>();
final Api _api = locator<Api>();
Future<History> getHistory() async {
var response = await _api.post('/file/history');
@ -39,11 +39,11 @@ class FileRepository {
}
Future<UploadedMultiResponse> postCreateMultiPaste(List<String> ids) async {
Map<String, String> multiPasteIds = Map();
Map<String, String> 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));

View file

@ -6,7 +6,7 @@ import '../models/rest/create_apikey_response.dart';
import '../services/api.dart';
class UserRepository {
Api _api = locator<Api>();
final Api _api = locator<Api>();
Future<CreateApiKeyResponse> postApiKey(
String url, String username, String password, String accessLevel, String comment) async {

View file

@ -24,8 +24,8 @@ class Api implements ApiErrorConverter {
String _url = "";
String _apiKey = "";
Map<String, String> _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson};
Duration _timeout = Duration(seconds: Constants.apiRequestTimeoutLimit);
final Map<String, String> _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson};
Duration _timeout = const Duration(seconds: Constants.apiRequestTimeoutLimit);
Future<http.Response> fetch<T>(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<String> 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);
}
}

View file

@ -7,7 +7,7 @@ import '../datamodels/dialog_request.dart';
import '../datamodels/dialog_response.dart';
class DialogService {
GlobalKey<NavigatorState> _dialogNavigationKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> _dialogNavigationKey = GlobalKey<NavigatorState>();
late Function(DialogRequest) _showDialogListener;
Completer<DialogResponse>? _dialogCompleter;

View file

@ -4,7 +4,7 @@ import 'package:logger/logger.dart';
import '../util/logger.dart';
class NavigationService {
GlobalKey<NavigatorState> _navigationKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> _navigationKey = GlobalKey<NavigatorState>();
GlobalKey<NavigatorState> get navigationKey => _navigationKey;

View file

@ -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');

View file

@ -31,7 +31,7 @@ class SessionService extends StoppableService {
Future<bool> 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');

View file

@ -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<bool> storeLastUrl(String url) {
return _store(_LAST_URL_KEY, url);
return _store(_lastUrlKey, url);
}
Future<String?> retrieveLastUrl() async {
return await _retrieve(_LAST_URL_KEY);
return await _retrieve(_lastUrlKey);
}
Future<bool> hasLastUrl() async {
return await _exists(_LAST_URL_KEY);
return await _exists(_lastUrlKey);
}
Future<bool> storeSession(Session session) {
return _store(_SESSION_KEY, json.encode(session));
return _store(_sessionKey, json.encode(session));
}
Future<Session> retrieveSession() async {
var retrieve = await _retrieve(_SESSION_KEY);
var retrieve = await _retrieve(_sessionKey);
return Session.fromJson(json.decode(retrieve!));
}
Future<bool> hasSession() {
return _exists(_SESSION_KEY);
return _exists(_sessionKey);
}
Future<bool> removeSession() {
return _remove(_SESSION_KEY);
return _remove(_sessionKey);
}
Future<bool> storeStoragePermissionDialogIgnored() {
return _store(_STORAGE_PERMISSION_DIALOG_IGNORED, true.toString());
return _store(_storagePermissionDialogIgnoredKey, true.toString());
}
Future<bool> hasStoragePermissionDialogIgnored() {
return _exists(_STORAGE_PERMISSION_DIALOG_IGNORED);
return _exists(_storagePermissionDialogIgnoredKey);
}
Future<bool> _exists(String key) async {

View file

@ -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);
}
}
}

View file

@ -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]}';
}
}

View file

@ -20,10 +20,10 @@ class AboutModel extends BaseModel {
}
Future<void> _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) {

View file

@ -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<String, Object?> _stateMap = {STATE_VIEW: ViewState.Idle, STATE_MESSAGE: null};
final Map<String, Object?> _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

View file

@ -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) {

View file

@ -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<String>);
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;
}

View file

@ -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;
}
}

View file

@ -12,16 +12,16 @@ class StartUpViewModel extends BaseModel {
final NavigationService _navigationService = locator<NavigationService>();
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);
}
}

View file

@ -31,7 +31,7 @@ class UploadModel extends BaseModel {
final LinkService _linkService = locator<LinkService>();
final RefreshService _refreshService = locator<RefreshService>();
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<SharedMediaFile> 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<SharedMediaFile> 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<String, bool>? 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<Map<String, bool>?> upload() async {
setStateView(ViewState.Busy);
setStateView(ViewState.busy);
setStateMessage(translate('upload.uploading_now'));
Map<String, bool> uploadedPasteIds = new Map();
Map<String, bool> uploadedPasteIds = {};
try {
List<File>? files;
Map<String, String>? 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;
}

View file

@ -14,15 +14,15 @@ class AppRouter {
static Route<dynamic> 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(

View file

@ -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

View file

@ -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: <Widget>[
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),
),
)

View file

@ -10,7 +10,7 @@ class BaseView<T extends BaseModel> 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<T> createState() => _BaseViewState<T>();

View file

@ -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<HistoryModel>(
@ -33,15 +35,15 @@ class HistoryView extends StatelessWidget {
Widget _render(HistoryModel model, BuildContext context) {
var url = Provider.of<Session>(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<Widget> cards = [];
if (model.pastes.length > 0) {
model.pastes.reversed.forEach((paste) {
if (model.pastes.isNotEmpty) {
for (var paste in model.pastes.reversed) {
List<Widget> 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);
});

View file

@ -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<HomeModel>(
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()),
);
}
}

View file

@ -20,6 +20,8 @@ class LoginView extends StatelessWidget {
final NavigationService _navigationService = locator<NavigationService>();
final DialogService _dialogService = locator<DialogService>();
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: <Widget>[
UIHelper.verticalSpaceMedium(),
Center(child: logo),
@ -50,7 +52,7 @@ class LoginView extends StatelessWidget {
alignment: WrapAlignment.center,
children: <Widget>[
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();

View file

@ -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<AuthenticatedNavBarView> 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],
);

View file

@ -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<ProfileModel>(
@ -26,8 +28,8 @@ class ProfileView extends StatelessWidget {
var url = Provider.of<Session>(context).url;
var apiKey = Provider.of<Session>(context).apiKey;
return model.state == ViewState.Busy
? Center(child: CircularProgressIndicator())
return model.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: ListView(
children: <Widget>[
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'),
),

View file

@ -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<StartUpViewModel>.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()));

View file

@ -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<Session?>(context);
bool isAuthenticated = currentSession != null ? currentSession.apiKey.isNotEmpty : false;
if (isAuthenticated) {
return AuthenticatedNavBarView();
return const AuthenticatedNavBarView();
}
return Container(
child: LoginView(),
);
return LoginView();
}
}

View file

@ -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<UploadModel>(
@ -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<Session>(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: <Widget>[
@ -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()
: '';

View file

@ -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<NavigationService>();
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.help),
icon: const Icon(Icons.help),
onPressed: () {
_navigationService.navigateTo(AboutView.routeName);
});

View file

@ -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: <Widget>[
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: <Widget>[
Center(
child: IconButton(
icon: Icon(Icons.refresh),
icon: const Icon(Icons.refresh),
color: primaryAccentColor,
onPressed: () {
retryCallback!();

View file

@ -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: <Widget>[
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,
),
]);

View file

@ -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: <Widget>[
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),
]);
}

View file

@ -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),

View file

@ -10,9 +10,7 @@ class MyAppBar extends AppBar {
: super(key: key, title: Row(children: <Widget>[title]), actions: _renderIconButtons(actionWidgets, enableAbout));
static List<Widget> _renderIconButtons(List<Widget>? actionWidgets, bool aboutEnabled) {
if (actionWidgets == null) {
actionWidgets = [];
}
actionWidgets ??= [];
List<Widget> widgets = [...actionWidgets];

View file

@ -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"

View file

@ -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