linter and lint fixes

This commit is contained in:
Mike Schwörer 2024-05-25 21:36:05 +02:00
parent 7e347a70c2
commit 72be45feb4
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
15 changed files with 256 additions and 158 deletions

View File

@ -4,6 +4,8 @@ run:
dart run build_runner build
flutter run
test:
dart analyze
gen:
dart run build_runner build

View File

@ -1,29 +1,117 @@
# 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
include:
- package:lints/recommended.yaml
- 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.dev/lints.
#
# 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:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
prefer_relative_imports: true,
always_use_package_imports: true,
avoid_empty_else: true,
avoid_returning_null_for_future: true,
avoid_type_to_string: true,
avoid_types_as_parameter_names: true,
avoid_web_libraries_in_flutter: true,
collection_methods_unrelated_type: true,
discarded_futures: true,
empty_statements: true,
hash_and_equals: true,
implicit_reopen: true,
invalid_case_patterns: true,
invariant_booleans: true,
no_duplicate_case_values: true,
no_logic_in_create_state: true,
no_self_assignments: true,
no_wildcard_variable_uses: true,
prefer_void_to_null: true,
unnecessary_statements: true,
valid_regexps: true,
always_declare_return_types: true,
always_put_control_body_on_new_line: true,
always_specify_types: true,
annotate_overrides: true,
annotate_redeclares: true,
avoid_annotating_with_dynamic: true,
avoid_function_literals_in_foreach_calls: true,
avoid_init_to_null: true,
avoid_null_checks_in_equality_operators: true,
avoid_renaming_method_parameters: true,
avoid_return_types_on_setters: true,
avoid_returning_null: true,
avoid_returning_null_for_void: true,
avoid_returning_this: true,
avoid_shadowing_type_parameters: true,
avoid_single_cascade_in_expression_statements: true,
avoid_unnecessary_containers: true,
avoid_unused_constructor_parameters: true,
avoid_void_async: true,
await_only_futures: true,
camel_case_extensions: true,
camel_case_types: true,
cast_nullable_to_non_nullable: true,
constant_identifier_names: true,
empty_catches: true,
eol_at_end_of_file: true,
exhaustive_cases: true,
file_names: true,
no_literal_bool_comparisons: true,
null_check_on_nullable_type_parameter: true,
null_closures: true,
overridden_fields: true,
prefer_adjacent_string_concatenation: true,
prefer_collection_literals: true,
prefer_conditional_assignment: true,
prefer_const_constructors: true,
prefer_const_constructors_in_immutables: true,
prefer_const_declarations: true,
prefer_const_literals_to_create_immutables: true,
prefer_contains: true,
prefer_final_fields: true,
prefer_for_elements_to_map_fromIterable: true,
prefer_function_declarations_over_variables: true,
prefer_generic_function_type_aliases: true,
prefer_if_null_operators: true,
prefer_initializing_formals: true,
prefer_inlined_adds: true,
prefer_interpolation_to_compose_strings: true,
prefer_is_empty: true,
prefer_is_not_empty: true,
prefer_is_not_operator: true,
prefer_iterable_whereType: true,
prefer_null_aware_operators: true,
prefer_spread_collections: true,
prefer_typing_uninitialized_variables: true,
provide_deprecation_message: true,
recursive_getters: true,
sized_box_for_whitespace: true,
type_init_formals: true,
type_literal_in_constant_pattern: true,
unnecessary_const: true,
unnecessary_constructor_name: true,
unnecessary_getters_setters: true,
unnecessary_late: true,
unnecessary_new: true,
unnecessary_null_aware_assignments: true,
unnecessary_null_in_if_null_operators: true,
unnecessary_nullable_for_final_variable_declarations: true,
unnecessary_overrides: true,
unnecessary_string_escapes: true,
unnecessary_string_interpolations: true,
unnecessary_this: true,
unnecessary_to_list_in_spreads: true,
use_full_hex_values_for_flutter_colors: true,
use_function_type_syntax_for_parameters: true,
use_rethrow_when_possible: true,
use_string_in_part_of_directives: true,
use_super_parameters: true,
void_checks: true,
depend_on_referenced_packages: true,
package_names: true,
secure_pubspec_urls: true,
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true

View File

@ -74,7 +74,7 @@ class APIClient {
if (responseStatusCode != 200) {
try {
final apierr = APIError.fromJson(jsonDecode(responseBody));
final apierr = APIError.fromJson(jsonDecode(responseBody) as Map<String, dynamic>);
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
@ -90,7 +90,7 @@ class APIClient {
final data = jsonDecode(responseBody);
if (fn != null) {
final result = fn(data);
final result = fn(data as Map<String, dynamic>);
RequestLog.addRequestSuccess(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders);
return result;
} else {
@ -137,7 +137,7 @@ class APIClient {
method: 'GET',
relURL: 'users/${auth.userId}/channels',
query: {'selector': sel.apiKey},
fn: (json) => ChannelWithSubscription.fromJsonArray(json['channels']),
fn: (json) => ChannelWithSubscription.fromJsonArray(json['channels'] as List<dynamic>),
auth: auth,
);
}

View File

@ -39,7 +39,7 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
icon: const Icon(FontAwesomeIcons.solidSpiderBlackWidow),
tooltip: 'Debug',
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => DebugMainPage()));
Navigator.push(context, MaterialPageRoute<DebugMainPage>(builder: (context) => DebugMainPage()));
},
),
if (!showDebug) SizedBox.square(dimension: 40),

View File

@ -13,10 +13,10 @@ class APIError {
factory APIError.fromJson(Map<String, dynamic> json) {
return APIError(
success: json['success'],
error: json['error'],
errhighlight: json['errhighlight'],
message: json['message'],
success: json['success'] as String,
error: json['error'] as String,
errhighlight: json['errhighlight'] as String,
message: json['message'] as String,
);
}
}

View File

@ -25,15 +25,15 @@ class Channel {
factory Channel.fromJson(Map<String, dynamic> json) {
return Channel(
channelID: json['channel_id'],
ownerUserID: json['owner_user_id'],
internalName: json['internal_name'],
displayName: json['display_name'],
descriptionName: json['description_name'],
subscribeKey: json['subscribe_key'],
timestampCreated: json['timestamp_created'],
timestampLastSent: json['timestamp_lastsent'],
messagesSent: json['messages_sent'],
channelID: json['channel_id'] as String,
ownerUserID: json['owner_user_id'] as String,
internalName: json['internal_name'] as String,
displayName: json['display_name'] as String,
descriptionName: json['description_name'] as String?,
subscribeKey: json['subscribe_key'] as String?,
timestampCreated: json['timestamp_created'] as String,
timestampLastSent: json['timestamp_lastsent'] as String?,
messagesSent: json['messages_sent'] as int,
);
}
}
@ -56,20 +56,20 @@ class ChannelWithSubscription extends Channel {
factory ChannelWithSubscription.fromJson(Map<String, dynamic> json) {
return ChannelWithSubscription(
channelID: json['channel_id'],
ownerUserID: json['owner_user_id'],
internalName: json['internal_name'],
displayName: json['display_name'],
descriptionName: json['description_name'],
subscribeKey: json['subscribe_key'],
timestampCreated: json['timestamp_created'],
timestampLastSent: json['timestamp_lastsent'],
messagesSent: json['messages_sent'],
subscription: Subscription.fromJson(json['subscription']),
channelID: json['channel_id'] as String,
ownerUserID: json['owner_user_id'] as String,
internalName: json['internal_name'] as String,
displayName: json['display_name'] as String,
descriptionName: json['description_name'] as String?,
subscribeKey: json['subscribe_key'] as String?,
timestampCreated: json['timestamp_created'] as String,
timestampLastSent: json['timestamp_lastsent'] as String?,
messagesSent: json['messages_sent'] as int,
subscription: Subscription.fromJson(json['subscription'] as Map<String, dynamic>),
);
}
static List<ChannelWithSubscription> fromJsonArray(List<dynamic> jsonArr) {
return jsonArr.map<ChannelWithSubscription>((e) => ChannelWithSubscription.fromJson(e)).toList();
return jsonArr.map<ChannelWithSubscription>((e) => ChannelWithSubscription.fromJson(e as Map<String, dynamic>)).toList();
}
}

View File

@ -31,26 +31,26 @@ class Message {
factory Message.fromJson(Map<String, dynamic> json) {
return Message(
messageID: json['message_id'],
senderUserID: json['sender_user_id'],
channelInternalName: json['channel_internal_name'],
channelID: json['channel_id'],
senderName: json['sender_name'],
senderIP: json['sender_ip'],
timestamp: json['timestamp'],
title: json['title'],
content: json['content'],
priority: json['priority'],
userMessageID: json['usr_message_id'],
usedKeyID: json['used_key_id'],
trimmed: json['trimmed'],
messageID: json['message_id'] as String,
senderUserID: json['sender_user_id'] as String,
channelInternalName: json['channel_internal_name'] as String,
channelID: json['channel_id'] as String,
senderName: json['sender_name'] as String,
senderIP: json['sender_ip'] as String,
timestamp: json['timestamp'] as String,
title: json['title'] as String,
content: json['content'] as String,
priority: json['priority'] as int,
userMessageID: json['usr_message_id'] as String,
usedKeyID: json['used_key_id'] as String,
trimmed: json['trimmed'] as bool,
);
}
static fromPaginatedJsonArray(Map<String, dynamic> data, String keyMessages, String keyToken) {
static (String, List<Message>) fromPaginatedJsonArray(Map<String, dynamic> data, String keyMessages, String keyToken) {
final npt = data[keyToken] as String;
final messages = (data[keyMessages] as List<dynamic>).map<Message>((e) => Message.fromJson(e)).toList();
final messages = (data[keyMessages] as List<dynamic>).map<Message>((e) => Message.fromJson(e as Map<String, dynamic>)).toList();
return (npt, messages);
}

View File

@ -19,13 +19,13 @@ class Subscription {
factory Subscription.fromJson(Map<String, dynamic> json) {
return Subscription(
subscriptionID: json['subscription_id'],
subscriberUserID: json['subscriber_user_id'],
channelOwnerUserID: json['channel_owner_user_id'],
channelID: json['channel_id'],
channelInternalName: json['channel_internal_name'],
timestampCreated: json['timestamp_created'],
confirmed: json['confirmed'],
subscriptionID: json['subscription_id'] as String,
subscriberUserID: json['subscriber_user_id'] as String,
channelOwnerUserID: json['channel_owner_user_id'] as String,
channelID: json['channel_id'] as String,
channelInternalName: json['channel_internal_name'] as String,
timestampCreated: json['timestamp_created'] as String,
confirmed: json['confirmed'] as bool,
);
}
}

View File

@ -41,24 +41,24 @@ class User {
factory User.fromJson(Map<String, dynamic> json) {
return User(
userID: json['user_id'],
username: json['username'],
timestampCreated: json['timestamp_created'],
timestampLastRead: json['timestamp_lastread'],
timestampLastSent: json['timestamp_lastsent'],
messagesSent: json['messages_sent'],
quotaUsed: json['quota_used'],
quotaRemaining: json['quota_remaining'],
quotaPerDay: json['quota_max'],
isPro: json['is_pro'],
defaultChannel: json['default_channel'],
maxBodySize: json['max_body_size'],
maxTitleLength: json['max_title_length'],
defaultPriority: json['default_priority'],
maxChannelNameLength: json['max_channel_name_length'],
maxChannelDescriptionLength: json['max_channel_description_length'],
maxSenderNameLength: json['max_sender_name_length'],
maxUserMessageIDLength: json['max_user_message_id_length'],
userID: json['user_id'] as String,
username: json['username'] as String?,
timestampCreated: json['timestamp_created'] as String,
timestampLastRead: json['timestamp_lastread'] as String?,
timestampLastSent: json['timestamp_lastsent'] as String?,
messagesSent: json['messages_sent'] as int,
quotaUsed: json['quota_used'] as int,
quotaRemaining: json['quota_remaining'] as int,
quotaPerDay: json['quota_max'] as int,
isPro: json['is_pro'] as bool,
defaultChannel: json['default_channel'] as String,
maxBodySize: json['max_body_size'] as int,
maxTitleLength: json['max_title_length'] as int,
defaultPriority: json['default_priority'] as int,
maxChannelNameLength: json['max_channel_name_length'] as int,
maxChannelDescriptionLength: json['max_channel_description_length'] as int,
maxSenderNameLength: json['max_sender_name_length'] as int,
maxUserMessageIDLength: json['max_user_message_id_length'] as int,
);
}
}

View File

@ -25,62 +25,9 @@ class _DebugRequestsPageState extends State<DebugRequestsPage> {
itemBuilder: (context, listIndex) {
final req = requestsBox.getAt(requestsBox.length - listIndex - 1)!;
if (req.type == 'SUCCESS') {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => DebugRequestViewPage(request: req))),
child: ListTile(
title: Row(
children: [
SizedBox(
width: 120,
child: Text(_dateFormat.format(req.timestampStart), style: TextStyle(fontSize: 12)),
),
Expanded(
child: Text(req.name, style: TextStyle(fontWeight: FontWeight.bold)),
),
SizedBox(width: 2),
Text('${req.timestampEnd.difference(req.timestampStart).inMilliseconds}ms', style: TextStyle(fontSize: 12)),
],
),
subtitle: Text(req.type),
),
),
);
return buildItemSuccess(context, req);
} else {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => DebugRequestViewPage(request: req))),
child: ListTile(
tileColor: Theme.of(context).colorScheme.errorContainer,
textColor: Theme.of(context).colorScheme.onErrorContainer,
title: Row(
children: [
SizedBox(
width: 120,
child: Text(_dateFormat.format(req.timestampStart), style: TextStyle(fontSize: 12)),
),
Expanded(
child: Text(req.name, style: TextStyle(fontWeight: FontWeight.bold)),
),
SizedBox(width: 2),
Text('${req.timestampEnd.difference(req.timestampStart).inMilliseconds}ms', style: TextStyle(fontSize: 12)),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(req.type),
Text(
req.error,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
)),
),
);
return buildItemError(context, req);
}
},
);
@ -88,4 +35,65 @@ class _DebugRequestsPageState extends State<DebugRequestsPage> {
),
);
}
Padding buildItemError(BuildContext context, SCNRequest req) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute<DebugRequestViewPage>(builder: (context) => DebugRequestViewPage(request: req))),
child: ListTile(
tileColor: Theme.of(context).colorScheme.errorContainer,
textColor: Theme.of(context).colorScheme.onErrorContainer,
title: Row(
children: [
SizedBox(
width: 120,
child: Text(_dateFormat.format(req.timestampStart), style: TextStyle(fontSize: 12)),
),
Expanded(
child: Text(req.name, style: TextStyle(fontWeight: FontWeight.bold)),
),
SizedBox(width: 2),
Text('${req.timestampEnd.difference(req.timestampStart).inMilliseconds}ms', style: TextStyle(fontSize: 12)),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(req.type),
Text(
req.error,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
)),
),
);
}
Padding buildItemSuccess(BuildContext context, SCNRequest req) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute<DebugRequestViewPage>(builder: (context) => DebugRequestViewPage(request: req))),
child: ListTile(
title: Row(
children: [
SizedBox(
width: 120,
child: Text(_dateFormat.format(req.timestampStart), style: TextStyle(fontSize: 12)),
),
Expanded(
child: Text(req.name, style: TextStyle(fontWeight: FontWeight.bold)),
),
SizedBox(width: 2),
Text('${req.timestampEnd.difference(req.timestampStart).inMilliseconds}ms', style: TextStyle(fontSize: 12)),
],
),
subtitle: Text(req.type),
),
),
);
}
}

View File

@ -48,7 +48,7 @@ class _MessageListPageState extends State<MessageListPage> {
try {
if (_channels == null) {
final channels = await APIClient.getChannelList(acc.auth!, ChannelSelector.allAny);
_channels = Map.fromIterable(channels, key: (e) => e.channelID);
_channels = <String, ChannelWithSubscription>{for (var v in channels) v.channelID: v};
}
final (npt, newItems) = await APIClient.getMessageList(acc.auth!, thisPageToken, _pageSize);
@ -76,7 +76,7 @@ class _MessageListPageState extends State<MessageListPage> {
message: item,
allChannels: _channels ?? {},
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => MessageViewPage(message: item)));
Navigator.push(context, MaterialPageRoute<MessageViewPage>(builder: (context) => MessageViewPage(message: item)));
},
),
),

View File

@ -145,7 +145,7 @@ class MessageListItem extends StatelessWidget {
);
}
processContent(String? v) {
String processContent(String? v) {
if (v == null) {
return '';
}
@ -158,7 +158,7 @@ class MessageListItem extends StatelessWidget {
return lines.sublist(0, min(_lineCount, lines.length)).join("\n").trim();
}
processTitle(String? v) {
String processTitle(String? v) {
if (v == null) {
return '';
}
@ -174,7 +174,7 @@ class MessageListItem extends StatelessWidget {
return allChannels[message.channelID]?.displayName ?? message.channelInternalName;
}
showChannel(Message message) {
bool showChannel(Message message) {
return message.channelInternalName != 'main';
}
}

View File

@ -79,7 +79,7 @@ class _SendRootPageState extends State<SendRootPage> {
//...
}
_buildQRCode(BuildContext context, UserAccount acc) {
Widget _buildQRCode(BuildContext context, UserAccount acc) {
if (acc.auth == null) {
return const Placeholder();
}

View File

@ -18,7 +18,7 @@ class Globals {
String platform = '';
String hostname = '';
init() async {
Future<void> init() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
this.appName = packageInfo.appName;

View File

@ -38,7 +38,7 @@ class UserAccount extends ChangeNotifier {
notifyListeners();
}
load() async {
void load() async {
final prefs = await SharedPreferences.getInstance();
final uid = prefs.getString('auth.userid');
@ -51,7 +51,7 @@ class UserAccount extends ChangeNotifier {
}
}
save() async {
Future<void> save() async {
final prefs = await SharedPreferences.getInstance();
if (_auth == null) {
await prefs.remove('auth.userid');
@ -62,9 +62,9 @@ class UserAccount extends ChangeNotifier {
}
}
loadUser(bool force) async {
Future<User> loadUser(bool force) async {
if (!force && _user != null) {
return _user;
return _user!;
}
if (_auth == null) {