From 6782347cf4cb32623856eb86c70cda5cdc1c67a1 Mon Sep 17 00:00:00 2001 From: Michael Lynch Date: Sat, 17 Jan 2026 11:00:34 -0500 Subject: [PATCH] Fix issues flagged by flutter analyze This fixes code quality issues that flutter analyze catches and adds a CI step to Github Actions to flag on any future issues. --- .github/workflows/flutter_analyze.yml | 23 ++++++++++++++++ lib/connector/meshcore_connector.dart | 6 ++--- lib/main.dart | 2 +- lib/screens/map_screen.dart | 3 ++- lib/screens/repeater_settings_screen.dart | 6 ++--- lib/screens/telemetry_screen.dart | 11 +++----- lib/services/ble_debug_log_service.dart | 32 ++++++++++++++++++++--- lib/services/message_retry_service.dart | 4 +-- 8 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/flutter_analyze.yml diff --git a/.github/workflows/flutter_analyze.yml b/.github/workflows/flutter_analyze.yml new file mode 100644 index 0000000..af4a3b7 --- /dev/null +++ b/.github/workflows/flutter_analyze.yml @@ -0,0 +1,23 @@ +name: Flutter Analyze + +on: + pull_request: + push: + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + channel: stable + + - name: Install dependencies + run: flutter pub get + + - name: Analyze + run: flutter analyze --fatal-infos --fatal-warnings diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 584fbdc..8b32870 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -1766,7 +1766,7 @@ class MeshCoreConnector extends ChangeNotifier { _batteryMillivolts = readUint16LE(frame, 1); final volts = (_batteryMillivolts! / 1000.0).toStringAsFixed(2); _appDebugLogService?.info( - 'Pulled battery: $volts V (${_batteryMillivolts} mV)', + 'Pulled battery: $volts V ($_batteryMillivolts mV)', tag: 'Battery', ); notifyListeners(); @@ -2272,7 +2272,6 @@ class MeshCoreConnector extends ChangeNotifier { // [6-9] = estimated_timeout_ms (uint32) if (frame.length >= 10) { - final isFlood = frame[1] != 0; final ackHash = Uint8List.fromList(frame.sublist(2, 6)); final timeoutMs = readUint32LE(frame, 6); @@ -2618,7 +2617,7 @@ class MeshCoreConnector extends ChangeNotifier { final keyLen = psk.length < 16 ? psk.length : 16; key16.setRange(0, keyLen, psk); - final cipher = ECBBlockCipher(AESFastEngine()); + final cipher = ECBBlockCipher(AESEngine()); cipher.init(false, KeyParameter(key16)); final out = Uint8List(cipherText.length); for (var i = 0; i < cipherText.length; i += 16) { @@ -2992,7 +2991,6 @@ const int _phVerMask = 0x03; const int _routeTransportFlood = 0x00; const int _routeFlood = 0x01; -const int _routeDirect = 0x02; const int _routeTransportDirect = 0x03; const int _payloadTypeGroupText = 0x05; diff --git a/lib/main.dart b/lib/main.dart index 4b59d8b..6dac19b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,7 +27,7 @@ void main() async { final storage = StorageService(); final connector = MeshCoreConnector(); final pathHistoryService = PathHistoryService(storage); - final retryService = MessageRetryService(storage); + final retryService = MessageRetryService(); final appSettingsService = AppSettingsService(); final bleDebugLogService = BleDebugLogService(); final appDebugLogService = AppDebugLogService(); diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index 12e4470..2aa3daa 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -388,8 +388,9 @@ class _MapScreenState extends State { if (!contact.hasLocation) continue; // Apply node type filters - if (contact.type == advTypeRepeater && !settings.mapShowRepeaters) + if (contact.type == advTypeRepeater && !settings.mapShowRepeaters) { continue; + } if (contact.type == advTypeChat && !settings.mapShowChatNodes) continue; if (contact.type != advTypeChat && contact.type != advTypeRepeater && diff --git a/lib/screens/repeater_settings_screen.dart b/lib/screens/repeater_settings_screen.dart index 79da459..c757404 100644 --- a/lib/screens/repeater_settings_screen.dart +++ b/lib/screens/repeater_settings_screen.dart @@ -895,7 +895,7 @@ class _RepeaterSettingsScreenState extends State { ), const SizedBox(height: 16), DropdownButtonFormField( - value: _bandwidth, + initialValue: _bandwidth, decoration: InputDecoration( labelText: l10n.repeater_bandwidth, border: const OutlineInputBorder(), @@ -917,7 +917,7 @@ class _RepeaterSettingsScreenState extends State { ), const SizedBox(height: 16), DropdownButtonFormField( - value: _spreadingFactor, + initialValue: _spreadingFactor, decoration: InputDecoration( labelText: l10n.repeater_spreadingFactor, border: const OutlineInputBorder(), @@ -939,7 +939,7 @@ class _RepeaterSettingsScreenState extends State { ), const SizedBox(height: 16), DropdownButtonFormField( - value: _codingRate, + initialValue: _codingRate, decoration: InputDecoration( labelText: l10n.repeater_codingRate, border: const OutlineInputBorder(), diff --git a/lib/screens/telemetry_screen.dart b/lib/screens/telemetry_screen.dart index 9210e3d..8770938 100644 --- a/lib/screens/telemetry_screen.dart +++ b/lib/screens/telemetry_screen.dart @@ -1,7 +1,4 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -34,7 +31,6 @@ class _TelemetryScreenState extends State { static const int _statusResponseBytes = _statusPayloadOffset + _statusStatsSize; Uint8List _tagData = Uint8List(4); - int _timeEstment = 0; bool _isLoading = false; bool _isLoaded = false; @@ -64,18 +60,19 @@ class _TelemetryScreenState extends State { if (frame[0] == respCodeSent) { _tagData = frame.sublist(2, 6); - _timeEstment = frame.buffer.asByteData().getUint32(6, Endian.little); } // Check if it's a binary response if (frame[0] == pushCodeBinaryResponse && listEquals(frame.sublist(2, 6), _tagData)) { - _handleStatusResponse(context, frame.sublist(6)); + if (!mounted) return; + _handleStatusResponse(frame.sublist(6)); } }); } - void _handleStatusResponse(BuildContext context, Uint8List frame) { + void _handleStatusResponse(Uint8List frame) { + if (!mounted) return; setState(() { _parsedTelemetry = CayenneLpp.parseByChannel(frame); }); diff --git a/lib/services/ble_debug_log_service.dart b/lib/services/ble_debug_log_service.dart index 6313971..002161b 100644 --- a/lib/services/ble_debug_log_service.dart +++ b/lib/services/ble_debug_log_service.dart @@ -86,14 +86,26 @@ class BleDebugLogService extends ChangeNotifier { } String _describeFrame(int code, Uint8List frame, bool outgoing, String? note) { - final label = _codeLabel(code); + final label = _codeLabel(code, outgoing: outgoing); final prefix = outgoing ? 'TX' : 'RX'; final extra = _frameDetail(code, frame); final noteText = note != null ? ' • $note' : ''; return '$prefix $label$extra$noteText'; } - String _codeLabel(int code) { + String _codeLabel(int code, {required bool outgoing}) { + if (outgoing) { + return _commandLabel(code) ?? 'CODE_$code'; + } + + final pushLabel = _pushLabel(code); + if (pushLabel != null) return pushLabel; + final responseLabel = _responseLabel(code); + if (responseLabel != null) return responseLabel; + return 'CODE_$code'; + } + + String? _commandLabel(int code) { switch (code) { case cmdAppStart: return 'CMD_APP_START'; @@ -135,6 +147,13 @@ class BleDebugLogService extends ChangeNotifier { return 'CMD_SET_CHANNEL'; case cmdGetRadioSettings: return 'CMD_GET_RADIO_SETTINGS'; + default: + return null; + } + } + + String? _responseLabel(int code) { + switch (code) { case respCodeOk: return 'RESP_CODE_OK'; case respCodeErr: @@ -167,6 +186,13 @@ class BleDebugLogService extends ChangeNotifier { return 'RESP_CODE_CHANNEL_INFO'; case respCodeRadioSettings: return 'RESP_CODE_RADIO_SETTINGS'; + default: + return null; + } + } + + String? _pushLabel(int code) { + switch (code) { case pushCodeAdvert: return 'PUSH_CODE_ADVERT'; case pushCodePathUpdated: @@ -184,7 +210,7 @@ class BleDebugLogService extends ChangeNotifier { case pushCodeNewAdvert: return 'PUSH_CODE_NEW_ADVERT'; default: - return 'CODE_$code'; + return null; } } diff --git a/lib/services/message_retry_service.dart b/lib/services/message_retry_service.dart index 0b96ba4..403cd93 100644 --- a/lib/services/message_retry_service.dart +++ b/lib/services/message_retry_service.dart @@ -6,7 +6,6 @@ import 'package:crypto/crypto.dart'; import '../models/contact.dart'; import '../models/message.dart'; import '../models/path_selection.dart'; -import 'storage_service.dart'; import 'app_settings_service.dart'; import 'app_debug_log_service.dart'; @@ -36,7 +35,6 @@ class MessageRetryService extends ChangeNotifier { static const int maxRetries = 5; static const int maxAckHistorySize = 100; - final StorageService _storage; final Map _timeoutTimers = {}; final Map _pendingMessages = {}; final Map _pendingContacts = {}; @@ -59,7 +57,7 @@ class MessageRetryService extends ChangeNotifier { AppDebugLogService? _debugLogService; Function(String, PathSelection, bool, int?)? _recordPathResultCallback; - MessageRetryService(this._storage); + MessageRetryService(); void initialize({ required Function(Contact, String, int, int) sendMessageCallback,