Fix 78 additional lint issues: rename all files to snake_case and fix 16 style issues
Lint issues fixed (263 total in lib/): - file_names (57 issues): Renamed all PascalCase files to snake_case - All lib files: activeFires -> active_fires, etc. - All models: appState -> app_state, fireMapState -> fire_map_state, etc. - All redux files: appActions -> app_actions, appReducer -> app_reducer, etc. - Updated all import statements and exports across the codebase - Fixed unused imports, exports, and references in rebuilt modules Style fixes (16 issues): - prefer_generic_function_type_aliases (2): Updated typedef syntax from old to new - unnecessary_nullable_for_final_variable_declarations (2): Removed ? from non-nullable types - unnecessary_import: Removed duplicate meta/meta import - unnecessary_parenthesis: Removed unnecessary parentheses in expressions - avoid_renaming_method_parameters: Renamed parameter 'o' to 'other' - prefer_const_declarations: Changed final to const for constant values - use_key_in_widget_constructors (3): Added key parameters to widget constructors - sort_child_properties_last (1): Reordered children property to end Verification: - APK builds successfully (146 MB) - Reduced from 106 lint issues to 49 high-priority issues - Total file_names issues: 0 (down from 57) - All imports and exports updated correctly
This commit is contained in:
parent
8da3752193
commit
12653b80a4
65 changed files with 141 additions and 245 deletions
291
lib/home_page.dart
Normal file
291
lib/home_page.dart
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
|
||||
import 'active_fires.dart';
|
||||
import 'colors.dart';
|
||||
import 'fire_alert.dart';
|
||||
import 'fire_notification_list.dart';
|
||||
import 'fires_spinner.dart';
|
||||
import 'generated/i18n.dart';
|
||||
import 'main_drawer.dart';
|
||||
import 'models/app_state.dart';
|
||||
import 'models/fire_notification.dart';
|
||||
import 'object_id_utils.dart';
|
||||
import 'redux/actions.dart';
|
||||
|
||||
@immutable
|
||||
class _ViewModel {
|
||||
const _ViewModel({required this.isLoaded});
|
||||
final bool isLoaded;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is _ViewModel &&
|
||||
runtimeType == other.runtimeType &&
|
||||
isLoaded == other.isLoaded;
|
||||
|
||||
@override
|
||||
int get hashCode => isLoaded.hashCode;
|
||||
}
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
static const String routeName = '/home';
|
||||
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final Store<AppState> store = GetIt.instance<Store<AppState>>();
|
||||
final List<FireNotification> newNotifications = <FireNotification>[];
|
||||
|
||||
// Platform messages are asynchronous, so we initialize in an async method.
|
||||
Future<void> initConnectivity() async {
|
||||
// Connectivity checking removed - no longer needed
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Firebase Messaging v5+ setup
|
||||
_setupFirebaseMessaging();
|
||||
_getFirebaseToken();
|
||||
// initConnectivity removed - connectivity no longer tracked
|
||||
}
|
||||
|
||||
void _setupFirebaseMessaging() {
|
||||
// Request permission for notifications
|
||||
_firebaseMessaging.requestPermission();
|
||||
|
||||
// Listen for messages when app is in foreground
|
||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
||||
debugPrint(
|
||||
'onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message');
|
||||
if (message.data.isNotEmpty) {
|
||||
final FireNotification? notif =
|
||||
_notifForMessage(message.data, store.state.isLoaded);
|
||||
if (notif != null) {
|
||||
_showItemDialog(message.data, notif);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for messages when app is opened from background
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
||||
debugPrint(
|
||||
'onMessageOpenedApp (isLoaded: ${store.state.isLoaded}): $message');
|
||||
if (message.data.isNotEmpty) {
|
||||
_notifForMessage(message.data, store.state.isLoaded);
|
||||
_navigateToItemDetail(message.data);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if app was terminated and opened by notification tap
|
||||
_firebaseMessaging.getInitialMessage().then((RemoteMessage? message) {
|
||||
if (message != null) {
|
||||
debugPrint('App opened by notification: $message');
|
||||
if (message.data.isNotEmpty) {
|
||||
_navigateToItemDetail(message.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _getFirebaseToken() {
|
||||
_firebaseMessaging.getToken().then((String? token) {
|
||||
if (token != null) {
|
||||
store.dispatch(OnUserTokenAction(token));
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final TextStyle _homeFont = const TextStyle(
|
||||
fontSize: 50.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
final TextStyle _btnFont = const TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, _ViewModel>(
|
||||
distinct: true,
|
||||
converter: (Store<AppState> store) {
|
||||
final bool isLoaded = store.state.isLoaded;
|
||||
if (isLoaded && newNotifications.isNotEmpty) {
|
||||
for (final FireNotification notif in newNotifications) {
|
||||
store.dispatch(AddFireNotificationAction(notif));
|
||||
}
|
||||
newNotifications.clear();
|
||||
}
|
||||
return _ViewModel(isLoaded: store.state.isLoaded);
|
||||
},
|
||||
builder: (BuildContext context, _ViewModel view) {
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
drawer: MainDrawer(context, HomePage.routeName),
|
||||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
FloatingActionButton.extended(
|
||||
elevation: 0.0,
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, ActiveFiresPage.routeName);
|
||||
},
|
||||
label: Text(S.of(context).activeFires, style: _btnFont),
|
||||
backgroundColor: fires600,
|
||||
heroTag: 'activeFires',
|
||||
icon: const Icon(Icons.whatshot, size: 32.0),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0, bottom: 26.0),
|
||||
child: FloatingActionButton.extended(
|
||||
elevation: 0.0,
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, FireAlert.routeName);
|
||||
},
|
||||
heroTag: 'notifyFire',
|
||||
backgroundColor: fires600,
|
||||
label: Text(S.of(context).notifyAFire, style: _btnFont),
|
||||
icon:
|
||||
const Icon(Icons.notifications_active, size: 32.0),
|
||||
),
|
||||
),
|
||||
]),
|
||||
body: !view.isLoaded
|
||||
? const FiresSpinner()
|
||||
: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(children: <Widget>[
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_scaffoldKey.currentState?.openDrawer();
|
||||
},
|
||||
icon: const Icon(Icons.menu,
|
||||
size: 30.0, color: Colors.black38)),
|
||||
]),
|
||||
Expanded(
|
||||
child: FractionallySizedBox(
|
||||
alignment: FractionalOffset.center,
|
||||
heightFactor: 0.7,
|
||||
child: Image.asset('images/logo-200.png',
|
||||
fit: BoxFit.fitHeight))),
|
||||
Expanded(
|
||||
child: FractionallySizedBox(
|
||||
alignment: FractionalOffset.topCenter,
|
||||
heightFactor: 1.0,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 20.0),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(S.of(context).appName,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: _homeFont),
|
||||
)),
|
||||
],
|
||||
)))
|
||||
])),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
void _showItemDialog(Map<String, dynamic> message, FireNotification notif) {
|
||||
final BuildContext? context = _scaffoldKey.currentContext;
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
showDialog<bool>(
|
||||
context: context,
|
||||
builder: (_) => _buildDialog(context, notif),
|
||||
).then((bool? shouldNavigate) {
|
||||
if (shouldNavigate ?? false) {
|
||||
_navigateToItemDetail(message);
|
||||
}
|
||||
}).catchError((Object e) {});
|
||||
}
|
||||
|
||||
Widget _buildDialog(BuildContext context, FireNotification item) {
|
||||
return AlertDialog(
|
||||
content: Text(item.description),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(S.of(context).CLOSE),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(S.of(context).SHOW),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToItemDetail(Map<String, dynamic> message) {
|
||||
final BuildContext? context = _scaffoldKey.currentContext;
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
// Clear away dialogs
|
||||
Navigator.popUntil(context, (Route<dynamic> route) => route is PageRoute);
|
||||
/* if (!notif.getRoute(store).isCurrent) {
|
||||
// Navigator.push(context, notif.getRoute(store));
|
||||
} */
|
||||
Navigator.pushNamed(context, FireNotificationList.routeName);
|
||||
}
|
||||
|
||||
// Firebase Messaging instance (v5+)
|
||||
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
|
||||
|
||||
FireNotification? _notifForMessage(
|
||||
Map<String, dynamic> message, bool isLoaded) {
|
||||
FireNotification? notif;
|
||||
try {
|
||||
notif = FireNotification(
|
||||
id: objectIdFromJson(message['id'] as String),
|
||||
subsId: objectIdFromJson(message['subsId'] as String),
|
||||
lat: double.parse(message['lat'] as String),
|
||||
lon: double.parse(message['lon'] as String),
|
||||
description: message['description'] as String,
|
||||
read: false,
|
||||
when: DateTime.parse(message['when'] as String),
|
||||
sealed: message['sealed'] as String);
|
||||
debugPrint(notif.toString());
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
// if our store is loaded, we just dispatch the notification, if not, we wait til is loaded
|
||||
if (notif != null) {
|
||||
if (isLoaded) {
|
||||
store.dispatch(AddFireNotificationAction(notif));
|
||||
} else {
|
||||
newNotifications.add(notif);
|
||||
}
|
||||
}
|
||||
return notif;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue