Fix 23 additional lint issues to reduce from 129 to 106 issues

- Add const constructors to @immutable classes (5 issues: prefer_const_constructors_in_immutables)
- Fix nullable value casts in firesApi.dart (3 issues: cast_nullable_to_non_nullable)
- Remove redundant default argument values (2 issues: avoid_redundant_argument_values)
- Make YourLocation immutable with final fields (2 issues: avoid_equals_and_hash_code_on_mutable_classes)
- Update imports and fix formatting

Reduces lint issues from 129 to 106. APK builds successfully (146 MB).
This commit is contained in:
vjrj 2026-03-11 23:45:06 +01:00
parent 270d9a569e
commit 8da3752193
12 changed files with 61 additions and 47 deletions

View file

@ -59,7 +59,7 @@ class _FiresAppState extends State<FiresApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final StatefulWidget home = MaterialAppWithIntroHome( final StatefulWidget home = const MaterialAppWithIntroHome(
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01'); introWidget, continueWidget, 'showInitialWizard-2018-06-27-01');
return StoreProvider<AppState>( return StoreProvider<AppState>(
store: store, store: store,

View file

@ -20,7 +20,7 @@ import 'redux/actions.dart';
@immutable @immutable
class _ViewModel { class _ViewModel {
_ViewModel({required this.isLoaded}); const _ViewModel({required this.isLoaded});
final bool isLoaded; final bool isLoaded;
@override @override

View file

@ -4,7 +4,7 @@ import 'package:meta/meta.dart';
class BasicLocation implements Comparable<BasicLocation> { class BasicLocation implements Comparable<BasicLocation> {
// static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0); // static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0);
BasicLocation({required this.lat, required this.lon, this.description}); const BasicLocation({required this.lat, required this.lon, this.description});
BasicLocation.fromJson(Map<String, dynamic> json) BasicLocation.fromJson(Map<String, dynamic> json)
: lat = (json['lat'] as num).toDouble(), : lat = (json['lat'] as num).toDouble(),

View file

@ -10,7 +10,7 @@ part 'fireNotification.g.dart';
@immutable @immutable
@JsonSerializable() @JsonSerializable()
class FireNotification { class FireNotification {
FireNotification( const FireNotification(
{required this.id, {required this.id,
required this.lat, required this.lat,
required this.lon, required this.lon,

View file

@ -31,7 +31,11 @@ class FiresApi {
final Response<Map<String, dynamic>> response = final Response<Map<String, dynamic>> response =
await _dio.post<Map<String, dynamic>>(url, data: params); await _dio.post<Map<String, dynamic>>(url, data: params);
if (response.statusCode == 200) { if (response.statusCode == 200) {
final Map<String, dynamic> data = response.data as Map<String, dynamic>; final Map<String, dynamic>? responseData = response.data;
if (responseData == null) {
throw Exception('Response data is null');
}
final Map<String, dynamic> data = responseData;
final Map<String, dynamic> dataData = final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>; data['data'] as Map<String, dynamic>;
return dataData['userId'] as String; return dataData['userId'] as String;
@ -52,7 +56,11 @@ class FiresApi {
final Response<Map<String, dynamic>> response = final Response<Map<String, dynamic>> response =
await _dio.get<Map<String, dynamic>>(url); await _dio.get<Map<String, dynamic>>(url);
if (response.statusCode == 200) { if (response.statusCode == 200) {
final Map<String, dynamic> data = response.data as Map<String, dynamic>; final Map<String, dynamic>? responseData = response.data;
if (responseData == null) {
throw Exception('Response data is null');
}
final Map<String, dynamic> data = responseData;
final Map<String, dynamic> dataData = final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>; data['data'] as Map<String, dynamic>;
final List<dynamic> dataSubscriptions = final List<dynamic> dataSubscriptions =
@ -96,7 +104,11 @@ class FiresApi {
final Response<Map<String, dynamic>> response = final Response<Map<String, dynamic>> response =
await _dio.post<Map<String, dynamic>>(url, data: params); await _dio.post<Map<String, dynamic>>(url, data: params);
if (response.statusCode == 200) { if (response.statusCode == 200) {
final Map<String, dynamic> data = response.data as Map<String, dynamic>; final Map<String, dynamic>? responseData = response.data;
if (responseData == null) {
throw Exception('Response data is null');
}
final Map<String, dynamic> data = responseData;
final Map<String, dynamic> dataData = final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>; data['data'] as Map<String, dynamic>;
return dataData['subsId'] as String; return dataData['subsId'] as String;

View file

@ -6,9 +6,10 @@ import '../objectIdUtils.dart';
part 'yourLocation.g.dart'; part 'yourLocation.g.dart';
@immutable
@JsonSerializable() @JsonSerializable()
class YourLocation { class YourLocation {
YourLocation( const YourLocation(
{required this.id, {required this.id,
required this.lat, required this.lat,
required this.lon, required this.lon,
@ -24,10 +25,10 @@ class YourLocation {
final ObjectId id; final ObjectId id;
final double lat; final double lat;
final double lon; final double lon;
String description; final String description;
bool subscribed; final bool subscribed;
int distance; final int distance;
int currentNumFires; final int currentNumFires;
static YourLocation get noLocation { static YourLocation get noLocation {
_noLocation ??= YourLocation(id: ObjectId(), lat: 0.0, lon: 0.0); _noLocation ??= YourLocation(id: ObjectId(), lat: 0.0, lon: 0.0);

View file

@ -13,7 +13,7 @@ import 'models/appState.dart';
@immutable @immutable
class _ViewModel { class _ViewModel {
_ViewModel(this.monitoredAreas); const _ViewModel(this.monitoredAreas);
final List<Polyline> monitoredAreas; final List<Polyline> monitoredAreas;
@override @override

View file

@ -109,7 +109,6 @@ class _PlaceSelectionDialogState extends State<_PlaceSelectionDialog> {
lat: location.latitude, lat: location.latitude,
lon: location.longitude, lon: location.longitude,
description: description, description: description,
distance: 10,
); );
Navigator.of(context).pop(yourLocation); Navigator.of(context).pop(yourLocation);

View file

@ -5,16 +5,16 @@ import 'package:flutter/services.dart' show rootBundle;
/// Loads secret/config files from Flutter assets as JSON. /// Loads secret/config files from Flutter assets as JSON.
/// Used to load configuration files at app startup. /// Used to load configuration files at app startup.
class SecretLoader { class SecretLoader {
final String secretPath;
SecretLoader({required this.secretPath}); SecretLoader({required this.secretPath});
final String secretPath;
/// Load and parse JSON from asset bundle. /// Load and parse JSON from asset bundle.
/// Returns a map of the parsed JSON content. /// Returns a map of the parsed JSON content.
Future<Map<String, dynamic>> load() { Future<Map<String, dynamic>> load() {
return rootBundle.loadStructuredData<Map<String, dynamic>>( return rootBundle.loadStructuredData<Map<String, dynamic>>(
secretPath, secretPath,
(jsonStr) async => json.decode(jsonStr) as Map<String, dynamic>, (String jsonStr) async => json.decode(jsonStr) as Map<String, dynamic>,
); );
} }
} }

View file

@ -6,25 +6,25 @@ import 'package:flutter/material.dart';
import '../utils/widget_utils.dart'; import '../utils/widget_utils.dart';
class AppIntroItem { class AppIntroItem {
AppIntroItem({required this.icon, required this.title, this.subTitle = ''});
final IconData icon; final IconData icon;
final String title; final String title;
final String subTitle; final String subTitle;
AppIntroItem({required this.icon, required this.title, this.subTitle = ''});
} }
typedef void OnIntroFinish(BuildContext context); typedef void OnIntroFinish(BuildContext context);
typedef List<AppIntroItem> ListItems(BuildContext context); typedef List<AppIntroItem> ListItems(BuildContext context);
abstract class AppIntroPage extends StatelessWidget { abstract class AppIntroPage extends StatelessWidget {
static const String routeName = '/appintro';
final ListItems items;
final OnIntroFinish onIntroFinish;
const AppIntroPage( const AppIntroPage(
{Key? key, required this.items, required this.onIntroFinish}) {Key? key, required this.items, required this.onIntroFinish})
: super(key: key); : super(key: key);
static const String routeName = '/appintro';
final ListItems items;
final OnIntroFinish onIntroFinish;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -63,7 +63,6 @@ class _AppIntroPageSelector extends StatelessWidget {
theme.textTheme.titleMedium ?? const TextStyle(); theme.textTheme.titleMedium ?? const TextStyle();
return SafeArea( return SafeArea(
top: true,
bottom: false, bottom: false,
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
@ -75,7 +74,8 @@ class _AppIntroPageSelector extends StatelessWidget {
icon: icon:
const Icon(Icons.close, size: 30.0, color: Colors.black38)), const Icon(Icons.close, size: 30.0, color: Colors.black38)),
]), ]),
Expanded(child: OrientationBuilder(builder: (context, orientation) { Expanded(child: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
return IconTheme( return IconTheme(
data: IconThemeData( data: IconThemeData(
size: orientation == Orientation.portrait ? 200.0 : 100.0, size: orientation == Orientation.portrait ? 200.0 : 100.0,

View file

@ -1,8 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async'; import 'dart:async';
@immutable
abstract class MaterialAppWithIntro extends StatelessWidget { abstract class MaterialAppWithIntro extends StatelessWidget {
const MaterialAppWithIntro(this.name, this.theme, this.routes,
this.introWidget, this.continueWidget, this.prefsKey);
final String name; final String name;
final ThemeData theme; final ThemeData theme;
final Map<String, WidgetBuilder> routes; final Map<String, WidgetBuilder> routes;
@ -10,9 +15,6 @@ abstract class MaterialAppWithIntro extends StatelessWidget {
final WidgetBuilder continueWidget; final WidgetBuilder continueWidget;
final String prefsKey; final String prefsKey;
MaterialAppWithIntro(this.name, this.theme, this.routes, this.introWidget,
this.continueWidget, this.prefsKey);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
@ -25,26 +27,26 @@ abstract class MaterialAppWithIntro extends StatelessWidget {
} }
class MaterialAppWithIntroHome extends StatefulWidget { class MaterialAppWithIntroHome extends StatefulWidget {
const MaterialAppWithIntroHome(
this.introWidget, this.continueWidget, this.prefsKey);
final WidgetBuilder introWidget; final WidgetBuilder introWidget;
final WidgetBuilder continueWidget; final WidgetBuilder continueWidget;
final String prefsKey; final String prefsKey;
const MaterialAppWithIntroHome(
this.introWidget, this.continueWidget, this.prefsKey);
@override @override
_MaterialAppWithIntroState createState() => _MaterialAppWithIntroState createState() =>
_MaterialAppWithIntroState(introWidget, continueWidget, prefsKey); _MaterialAppWithIntroState(introWidget, continueWidget, prefsKey);
} }
class _MaterialAppWithIntroState extends State<MaterialAppWithIntroHome> { class _MaterialAppWithIntroState extends State<MaterialAppWithIntroHome> {
_MaterialAppWithIntroState(
this.introWidget, this.continueWidget, this.prefsKey);
final WidgetBuilder introWidget; final WidgetBuilder introWidget;
final WidgetBuilder continueWidget; final WidgetBuilder continueWidget;
final String prefsKey; final String prefsKey;
_MaterialAppWithIntroState(
this.introWidget, this.continueWidget, this.prefsKey);
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -63,19 +65,19 @@ class _MaterialAppWithIntroState extends State<MaterialAppWithIntroHome> {
await prefs.setBool(initialWizardKey, false); await prefs.setBool(initialWizardKey, false);
if (mounted) { if (mounted) {
await Navigator.of(context) await Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: introWidget)); .pushReplacement(MaterialPageRoute<void>(builder: introWidget));
} }
} else { } else {
if (mounted) { if (mounted) {
await Navigator.of(context) await Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: continueWidget)); .pushReplacement(MaterialPageRoute<void>(builder: continueWidget));
} }
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return const Scaffold(
body: Center( body: Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),

View file

@ -3,15 +3,6 @@ import 'package:flutter/material.dart';
/// Rounded button widget with icon and text. /// Rounded button widget with icon and text.
/// Used primarily in the active fires page. /// Used primarily in the active fires page.
class RoundedBtn extends StatelessWidget { class RoundedBtn extends StatelessWidget {
static const btnRadius = Radius.circular(90.0);
final IconData icon;
final String text;
final Color backColor;
final Color fontColor;
final TextStyle textStyle;
final VoidCallback onPressed;
const RoundedBtn({ const RoundedBtn({
required this.icon, required this.icon,
required this.text, required this.text,
@ -42,6 +33,15 @@ class RoundedBtn extends StatelessWidget {
); );
} }
static const Radius btnRadius = Radius.circular(90.0);
final IconData icon;
final String text;
final Color backColor;
final Color fontColor;
final TextStyle textStyle;
final VoidCallback onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
@ -49,7 +49,7 @@ class RoundedBtn extends StatelessWidget {
onPressed: onPressed, onPressed: onPressed,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backColor, backgroundColor: backColor,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(btnRadius), borderRadius: BorderRadius.all(btnRadius),
), ),
), ),