Files
ifrbuddy/lib/screens/comparison_page.dart
2024-10-20 18:13:38 +02:00

289 lines
9.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../utils/frequency_input_formatter.dart';
import 'final_clearance_page.dart';
class ComparisonPage extends StatefulWidget {
final String expectedClearanceLimit;
final String expectedRoute;
final String expectedAltitude;
final String expectedSquawk;
final String expectedFrequency;
final bool isDarkMode;
final Function toggleDarkMode;
const ComparisonPage({
super.key,
required this.expectedClearanceLimit,
required this.expectedRoute,
required this.expectedAltitude,
required this.expectedSquawk,
required this.expectedFrequency,
required this.isDarkMode,
required this.toggleDarkMode,
});
@override
ComparisonPageState createState() => ComparisonPageState();
}
class ComparisonPageState extends State<ComparisonPage> {
final TextEditingController _actualClearanceLimitController = TextEditingController();
final TextEditingController _actualRouteController = TextEditingController();
final TextEditingController _actualAltitudeController = TextEditingController();
final TextEditingController _actualSquawkController = TextEditingController();
final TextEditingController _actualFrequencyController = TextEditingController();
final FocusNode _actualAltitudeFocusNode = FocusNode();
@override
void initState() {
super.initState();
_actualAltitudeFocusNode.addListener(() {
if (!_actualAltitudeFocusNode.hasFocus) {
_formatAltitude(_actualAltitudeController);
}
});
}
@override
void dispose() {
_actualClearanceLimitController.dispose();
_actualRouteController.dispose();
_actualAltitudeController.dispose();
_actualSquawkController.dispose();
_actualFrequencyController.dispose();
_actualAltitudeFocusNode.dispose();
super.dispose();
}
void _formatAltitude(TextEditingController controller) {
String text = controller.text.trim();
if (text.isEmpty) {
return;
}
if (text.startsWith('FL') || text.endsWith('ft')) {
return;
}
if (RegExp(r'^\d{2,3}$').hasMatch(text)) {
controller.text = 'FL$text';
} else if (RegExp(r'^\d{4,}$').hasMatch(text)) {
controller.text = '$text ft';
}
}
void _validateFrequency() {
String text = _actualFrequencyController.text;
if (text.isEmpty) {
_actualFrequencyController.text = widget.expectedFrequency;
}
}
void _navigateToFinalClearance() {
_validateFrequency();
if (_actualClearanceLimitController.text.isEmpty) {
_actualClearanceLimitController.text = widget.expectedClearanceLimit;
}
if (_actualRouteController.text.isEmpty) {
_actualRouteController.text = widget.expectedRoute;
}
if (_actualAltitudeController.text.isEmpty) {
_actualAltitudeController.text = widget.expectedAltitude;
}
if (_actualSquawkController.text.isEmpty) {
_actualSquawkController.text = widget.expectedSquawk;
}
if (_actualFrequencyController.text.isEmpty) {
_actualFrequencyController.text = widget.expectedFrequency;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FinalClearanceDisplay(
clearanceLimit: _actualClearanceLimitController.text,
route: _actualRouteController.text,
altitude: _actualAltitudeController.text,
squawk: _actualSquawkController.text,
frequency: _actualFrequencyController.text,
isDarkMode: widget.isDarkMode,
toggleDarkMode: widget.toggleDarkMode,
),
),
);
}
Widget buildExpectedClearanceField(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextFormField(
initialValue: value,
readOnly: true,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
),
);
}
Widget buildActualClearanceField(
String label,
TextEditingController controller,
String expectedValue, {
List<TextInputFormatter>? formatterList,
FocusNode? focusNode,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextFormField(
controller: controller,
focusNode: focusNode,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: const Icon(Icons.arrow_forward),
tooltip: 'Use Expected Value',
onPressed: () {
setState(() {
controller.text = expectedValue;
});
},
),
),
inputFormatters: formatterList,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('IFR Buddy'), // Global title
actions: [
IconButton(
icon: Icon(widget.isDarkMode ? Icons.dark_mode : Icons.light_mode),
onPressed: () {
widget.toggleDarkMode();
},
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: [
// Explanation Text Card
const Card(
elevation: 2,
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'Use this Page while receiving your clearance. The arrow button copies the expected value. Same if left empty.',
style: TextStyle(fontSize: 16),
textAlign: TextAlign.center,
),
),
),
const SizedBox(height: 16),
// Main content side-by-side
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Expected Clearance Column
Expanded(
child: Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Expected',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
const Divider(),
buildExpectedClearanceField('Clearance Limit', widget.expectedClearanceLimit),
buildExpectedClearanceField('Route', widget.expectedRoute),
buildExpectedClearanceField('Altitude', widget.expectedAltitude),
buildExpectedClearanceField('Frequency', widget.expectedFrequency),
buildExpectedClearanceField('Transponder (Squawk)', widget.expectedSquawk),
],
),
),
),
),
const SizedBox(width: 16), // Spacer between columns
// Actual Clearance Column
Expanded(
child: Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Clearance',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
const Divider(),
buildActualClearanceField(
'Clearance Limit',
_actualClearanceLimitController,
widget.expectedClearanceLimit,
),
buildActualClearanceField(
'Route',
_actualRouteController,
widget.expectedRoute,
),
buildActualClearanceField(
'Altitude',
_actualAltitudeController,
widget.expectedAltitude,
focusNode: _actualAltitudeFocusNode,
),
buildActualClearanceField(
'Frequency',
_actualFrequencyController,
widget.expectedFrequency,
formatterList: [FrequencyInputFormatter()],
),
buildActualClearanceField(
'Transponder (Squawk)',
_actualSquawkController,
widget.expectedSquawk,
formatterList: [
FilteringTextInputFormatter.allow(RegExp(r'[0-7]')),
LengthLimitingTextInputFormatter(4),
],
),
],
),
),
),
),
],
),
const SizedBox(height: 20),
// Proceed Button
ElevatedButton(
onPressed: _navigateToFinalClearance,
child: const Text('Proceed to Readback page'),
),
],
),
),
);
}
}