Up2Date
18
README.md
@@ -1,14 +1,16 @@
|
|||||||
# IFR Buddy
|
# ifrbuddy
|
||||||
|
|
||||||
**IFR Buddy** is a Flutter-based web application designed to help pilots and aviation enthusiasts easily record Instrument Flight Rules (IFR) clearances without the need for a pen. Featuring seamless navigation between input fields, dark mode, and a user-friendly interface, IFR Buddy simplifies the process of entering essential IFR details such as Clearance Limit, Route/SID, Altitude, Squawk Code, and Departure Frequency.
|
A new Flutter project.
|
||||||
|
|
||||||
## Features
|
## Getting Started
|
||||||
|
|
||||||
- **Seamless Navigation:** Move between input fields using keyboard buttons.
|
This project is a starting point for a Flutter application.
|
||||||
- **Dark Mode:** Toggle between light and dark themes for comfortable usage in any lighting condition.
|
|
||||||
- **Efficient Data Entry:** Quickly input and manage Clearance Limit, Route/SID, Altitude, Squawk Code, and Departure Frequency.
|
|
||||||
|
|
||||||
## License
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<application
|
<application
|
||||||
android:label="ifrbuddy"
|
android:label="ifrbuddy"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/launcher_icon">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
@@ -539,7 +539,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
@@ -596,7 +596,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
|||||||
@@ -1 +1,122 @@
|
|||||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "83.5x83.5",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"filename" : "Icon-App-1024x1024@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 295 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 282 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 704 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 586 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 862 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 862 B |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 762 B |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -12,7 +12,8 @@ class ComparisonPage extends StatefulWidget {
|
|||||||
final bool isDarkMode;
|
final bool isDarkMode;
|
||||||
final Function toggleDarkMode;
|
final Function toggleDarkMode;
|
||||||
|
|
||||||
const ComparisonPage({super.key,
|
const ComparisonPage({
|
||||||
|
super.key,
|
||||||
required this.expectedClearanceLimit,
|
required this.expectedClearanceLimit,
|
||||||
required this.expectedRoute,
|
required this.expectedRoute,
|
||||||
required this.expectedAltitude,
|
required this.expectedAltitude,
|
||||||
@@ -159,130 +160,130 @@ class ComparisonPageState extends State<ComparisonPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('IFR Buddy'), // Global title
|
title: const Text('IFR Buddy'), // Global title
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(widget.isDarkMode ? Icons.dark_mode : Icons.light_mode),
|
icon: Icon(widget.isDarkMode ? Icons.dark_mode : Icons.light_mode),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
widget.toggleDarkMode();
|
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 excpected 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('Transponder (Squawk)', widget.expectedSquawk),
|
|
||||||
buildExpectedClearanceField('Frequency', widget.expectedFrequency),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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(
|
|
||||||
'Transponder (Squawk)',
|
|
||||||
_actualSquawkController,
|
|
||||||
widget.expectedSquawk,
|
|
||||||
formatterList: [
|
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'[0-7]')),
|
|
||||||
LengthLimitingTextInputFormatter(4),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
buildActualClearanceField(
|
|
||||||
'Frequency',
|
|
||||||
_actualFrequencyController,
|
|
||||||
widget.expectedFrequency,
|
|
||||||
formatterList: [FrequencyInputFormatter()],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Proceed Button
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _navigateToFinalClearance,
|
|
||||||
child: const Text('Proceed to Clearance Display'),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
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 Clearance Display'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:keyboard_actions/keyboard_actions.dart'; // Correct package import
|
|
||||||
import 'comparison_page.dart';
|
import 'comparison_page.dart';
|
||||||
import 'final_clearance_page.dart'; // Import final clearance page
|
import 'final_clearance_page.dart'; // Import final clearance page
|
||||||
import '../utils/frequency_input_formatter.dart';
|
import '../utils/frequency_input_formatter.dart'; // Ensure this path is correct
|
||||||
|
|
||||||
class ExpectationInputPage extends StatefulWidget {
|
class ExpectationInputPage extends StatefulWidget {
|
||||||
final bool isDarkMode;
|
final bool isDarkMode;
|
||||||
@@ -32,7 +31,7 @@ class ExpectationInputPageState extends State<ExpectationInputPage> {
|
|||||||
final TextEditingController _expectedFrequencyController =
|
final TextEditingController _expectedFrequencyController =
|
||||||
TextEditingController();
|
TextEditingController();
|
||||||
|
|
||||||
// Reintroduce FocusNodes
|
// FocusNodes for each field
|
||||||
final FocusNode _clearanceLimitFocusNode = FocusNode();
|
final FocusNode _clearanceLimitFocusNode = FocusNode();
|
||||||
final FocusNode _routeFocusNode = FocusNode();
|
final FocusNode _routeFocusNode = FocusNode();
|
||||||
final FocusNode _altitudeFocusNode = FocusNode();
|
final FocusNode _altitudeFocusNode = FocusNode();
|
||||||
@@ -93,7 +92,7 @@ class ExpectationInputPageState extends State<ExpectationInputPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updated method to build a text field with optional input
|
// Method to build a text field with optional input and standard keyboard
|
||||||
Widget buildTextField({
|
Widget buildTextField({
|
||||||
required String label,
|
required String label,
|
||||||
required TextEditingController controller,
|
required TextEditingController controller,
|
||||||
@@ -120,20 +119,15 @@ class ExpectationInputPageState extends State<ExpectationInputPage> {
|
|||||||
enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
|
enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
|
||||||
onFieldSubmitted: (_) {
|
onFieldSubmitted: (_) {
|
||||||
if (isLastField) {
|
if (isLastField) {
|
||||||
// If it's the last field, unfocus to close the keyboard
|
// If it's the last field, unfocus to close the keyboard and submit
|
||||||
currentFocus.unfocus();
|
currentFocus.unfocus();
|
||||||
// Optionally, you can trigger form submission here
|
_navigateToComparisonPage();
|
||||||
// _navigateToComparisonPage();
|
|
||||||
} else if (nextFocus != null) {
|
} else if (nextFocus != null) {
|
||||||
FocusScope.of(context).requestFocus(nextFocus);
|
FocusScope.of(context).requestFocus(nextFocus);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Updated validator to allow empty inputs
|
// Validator allows empty inputs but validates if input is present
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
// If you want to perform validation only when input is not empty,
|
|
||||||
// you can add conditional checks here.
|
|
||||||
|
|
||||||
// Example: Validate numerical fields if they are not empty
|
|
||||||
if (value != null && value.isNotEmpty) {
|
if (value != null && value.isNotEmpty) {
|
||||||
if (keyboardType == TextInputType.number ||
|
if (keyboardType == TextInputType.number ||
|
||||||
keyboardType == const TextInputType.numberWithOptions(decimal: true)) {
|
keyboardType == const TextInputType.numberWithOptions(decimal: true)) {
|
||||||
@@ -144,7 +138,7 @@ class ExpectationInputPageState extends State<ExpectationInputPage> {
|
|||||||
// Add more specific validations if needed
|
// Add more specific validations if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example: Validate Transponder (Squawk) field
|
// Validate Transponder (Squawk) field
|
||||||
if (label == 'Transponder (Squawk)') {
|
if (label == 'Transponder (Squawk)') {
|
||||||
if (!RegExp(r'^[0-7]{1,4}$').hasMatch(value)) {
|
if (!RegExp(r'^[0-7]{1,4}$').hasMatch(value)) {
|
||||||
return 'Squawk must be 1-4 digits between 0 and 7';
|
return 'Squawk must be 1-4 digits between 0 and 7';
|
||||||
@@ -165,105 +159,6 @@ class ExpectationInputPageState extends State<ExpectationInputPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure KeyboardActions
|
|
||||||
KeyboardActionsConfig _buildConfig(BuildContext context) {
|
|
||||||
return KeyboardActionsConfig(
|
|
||||||
keyboardSeparatorColor: Colors.grey, // Optional: Customize the separator color
|
|
||||||
nextFocus: true, // Automatically handle next focus
|
|
||||||
actions: [
|
|
||||||
// Define actions for each FocusNode
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _clearanceLimitFocusNode,
|
|
||||||
toolbarButtons: [
|
|
||||||
(node) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => node.nextFocus(),
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"Next",
|
|
||||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _routeFocusNode,
|
|
||||||
toolbarButtons: [
|
|
||||||
(node) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => node.nextFocus(),
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"Next",
|
|
||||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _altitudeFocusNode,
|
|
||||||
toolbarButtons: [
|
|
||||||
(node) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => node.nextFocus(),
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"Next",
|
|
||||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _squawkFocusNode,
|
|
||||||
toolbarButtons: [
|
|
||||||
(node) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => node.nextFocus(),
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"Next",
|
|
||||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _frequencyFocusNode,
|
|
||||||
toolbarButtons: [
|
|
||||||
(node) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
node.unfocus(); // Dismiss the keyboard
|
|
||||||
_navigateToComparisonPage(); // Optionally, submit the form
|
|
||||||
},
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"Done",
|
|
||||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -282,142 +177,151 @@ class ExpectationInputPageState extends State<ExpectationInputPage> {
|
|||||||
body: GestureDetector(
|
body: GestureDetector(
|
||||||
// Dismiss the keyboard when tapping outside
|
// Dismiss the keyboard when tapping outside
|
||||||
onTap: () => FocusScope.of(context).unfocus(),
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
child: KeyboardActions(
|
child: SingleChildScrollView(
|
||||||
config: _buildConfig(context), // Apply KeyboardActionsConfig
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: SingleChildScrollView(
|
child: Column(
|
||||||
padding: const EdgeInsets.all(16.0),
|
children: [
|
||||||
child: Column(
|
const Card(
|
||||||
children: [
|
elevation: 2,
|
||||||
const Card(
|
child: Padding(
|
||||||
elevation: 2,
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: EdgeInsets.all(16.0),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(
|
||||||
children: [
|
'Welcome to IFR Buddy!',
|
||||||
Text(
|
style: TextStyle(
|
||||||
'Welcome to IFR Buddy!',
|
fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
style: TextStyle(
|
textAlign: TextAlign.left,
|
||||||
fontSize: 18, fontWeight: FontWeight.bold),
|
),
|
||||||
textAlign: TextAlign.left,
|
SizedBox(height: 10),
|
||||||
),
|
Text(
|
||||||
SizedBox(height: 10),
|
'Just an easy tool for writing down IFR clearances without a pen.',
|
||||||
Text(
|
style: TextStyle(fontSize: 16),
|
||||||
'Just an easy tool for writing down IFR clearances without a pen.',
|
textAlign: TextAlign.left,
|
||||||
style: TextStyle(fontSize: 16),
|
),
|
||||||
textAlign: TextAlign.left,
|
],
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
),
|
||||||
Card(
|
const SizedBox(height: 16),
|
||||||
elevation: 2,
|
Card(
|
||||||
child: Padding(
|
elevation: 2,
|
||||||
padding: const EdgeInsets.all(16.0),
|
child: Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(16.0),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
const Text(
|
children: [
|
||||||
'Expected Clearance',
|
const Text(
|
||||||
style: TextStyle(
|
'Expected Clearance',
|
||||||
fontSize: 22, fontWeight: FontWeight.bold),
|
style: TextStyle(
|
||||||
|
fontSize: 22, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
const Text(
|
||||||
|
'Enter your expected clearance information below. '
|
||||||
|
'Use the listening page or skip to the readback page.',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
buildTextField(
|
||||||
|
label: 'Clearance Limit',
|
||||||
|
controller: _expectedClearanceLimitController,
|
||||||
|
currentFocus: _clearanceLimitFocusNode,
|
||||||
|
nextFocus: _routeFocusNode,
|
||||||
|
keyboardType: TextInputType.text, // Standard keyboard
|
||||||
|
inputFormatters: [
|
||||||
|
// Allow letters, numbers, spaces, and hyphens
|
||||||
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'[A-Za-z0-9\s\-]')),
|
||||||
|
],
|
||||||
|
enableAutocorrect: false, // Disable autocorrect
|
||||||
|
enableSuggestions: false, // Disable suggestions
|
||||||
|
enableIMEPersonalizedLearning:
|
||||||
|
false, // iOS-specific
|
||||||
|
),
|
||||||
|
buildTextField(
|
||||||
|
label: 'Route/Sid',
|
||||||
|
controller: _expectedRouteController,
|
||||||
|
currentFocus: _routeFocusNode,
|
||||||
|
nextFocus: _altitudeFocusNode,
|
||||||
|
keyboardType: TextInputType.text, // Standard keyboard
|
||||||
|
inputFormatters: [
|
||||||
|
// Allow letters, numbers, spaces, and hyphens
|
||||||
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'[A-Za-z0-9\s\-]')),
|
||||||
|
],
|
||||||
|
enableAutocorrect: false, // Disable autocorrect
|
||||||
|
enableSuggestions: false, // Disable suggestions
|
||||||
|
enableIMEPersonalizedLearning:
|
||||||
|
false, // iOS-specific
|
||||||
|
),
|
||||||
|
buildTextField(
|
||||||
|
label: 'Altitude',
|
||||||
|
controller: _expectedAltitudeController,
|
||||||
|
currentFocus: _altitudeFocusNode,
|
||||||
|
nextFocus: _squawkFocusNode,
|
||||||
|
keyboardType: TextInputType.text, // Standard keyboard
|
||||||
|
inputFormatters: null, // No restrictions
|
||||||
|
enableAutocorrect: false, // Disable autocorrect
|
||||||
|
enableSuggestions: false, // Disable suggestions
|
||||||
|
enableIMEPersonalizedLearning:
|
||||||
|
false, // iOS-specific
|
||||||
|
),
|
||||||
|
buildTextField(
|
||||||
|
label: 'Transponder (Squawk)',
|
||||||
|
controller: _expectedSquawkController,
|
||||||
|
currentFocus: _squawkFocusNode,
|
||||||
|
nextFocus: _frequencyFocusNode,
|
||||||
|
keyboardType: TextInputType.text, // Standard keyboard
|
||||||
|
inputFormatters: [
|
||||||
|
// Allow only digits 0-7 and limit to 4 characters
|
||||||
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'[0-7]')),
|
||||||
|
LengthLimitingTextInputFormatter(4),
|
||||||
|
],
|
||||||
|
enableAutocorrect: false, // Disable autocorrect
|
||||||
|
enableSuggestions: false, // Disable suggestions
|
||||||
|
enableIMEPersonalizedLearning:
|
||||||
|
false, // iOS-specific
|
||||||
|
),
|
||||||
|
buildTextField(
|
||||||
|
label: 'Departure Frequency',
|
||||||
|
controller: _expectedFrequencyController,
|
||||||
|
currentFocus: _frequencyFocusNode,
|
||||||
|
isLastField: true,
|
||||||
|
keyboardType: TextInputType.text, // Standard keyboard
|
||||||
|
inputFormatters: [
|
||||||
|
FrequencyInputFormatter(), // Handles decimal inputs
|
||||||
|
],
|
||||||
|
enableAutocorrect: false, // Disable autocorrect
|
||||||
|
enableSuggestions: false, // Disable suggestions
|
||||||
|
enableIMEPersonalizedLearning:
|
||||||
|
false, // iOS-specific
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _navigateToComparisonPage,
|
||||||
|
child: const Text('Next'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
TextButton(
|
||||||
|
onPressed: _skipReadback,
|
||||||
|
child: const Text('Skip to end'),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
),
|
||||||
const Text(
|
],
|
||||||
'Enter your expected clearance information below. '
|
|
||||||
'Use the listening page or skip to the readback page.',
|
|
||||||
style: TextStyle(fontSize: 16),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
buildTextField(
|
|
||||||
label: 'Clearance Limit',
|
|
||||||
controller: _expectedClearanceLimitController,
|
|
||||||
currentFocus: _clearanceLimitFocusNode,
|
|
||||||
nextFocus: _routeFocusNode,
|
|
||||||
enableAutocorrect: false, // Disable autocorrect
|
|
||||||
enableSuggestions: false, // Disable suggestions
|
|
||||||
enableIMEPersonalizedLearning:
|
|
||||||
false, // iOS-specific
|
|
||||||
),
|
|
||||||
buildTextField(
|
|
||||||
label: 'Route/Sid',
|
|
||||||
controller: _expectedRouteController,
|
|
||||||
currentFocus: _routeFocusNode,
|
|
||||||
nextFocus: _altitudeFocusNode,
|
|
||||||
enableAutocorrect: false, // Disable autocorrect
|
|
||||||
enableSuggestions: false, // Disable suggestions
|
|
||||||
enableIMEPersonalizedLearning:
|
|
||||||
false, // iOS-specific
|
|
||||||
),
|
|
||||||
buildTextField(
|
|
||||||
label: 'Altitude',
|
|
||||||
controller: _expectedAltitudeController,
|
|
||||||
currentFocus: _altitudeFocusNode,
|
|
||||||
nextFocus: _squawkFocusNode,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.digitsOnly
|
|
||||||
],
|
|
||||||
enableAutocorrect: false, // Disable autocorrect
|
|
||||||
enableSuggestions: false, // Disable suggestions
|
|
||||||
enableIMEPersonalizedLearning:
|
|
||||||
false, // iOS-specific
|
|
||||||
),
|
|
||||||
buildTextField(
|
|
||||||
label: 'Transponder (Squawk)',
|
|
||||||
controller: _expectedSquawkController,
|
|
||||||
currentFocus: _squawkFocusNode,
|
|
||||||
nextFocus: _frequencyFocusNode,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.allow(
|
|
||||||
RegExp(r'[0-7]')),
|
|
||||||
LengthLimitingTextInputFormatter(4),
|
|
||||||
],
|
|
||||||
enableAutocorrect: false, // Disable autocorrect
|
|
||||||
enableSuggestions: false, // Disable suggestions
|
|
||||||
enableIMEPersonalizedLearning:
|
|
||||||
false, // iOS-specific
|
|
||||||
),
|
|
||||||
buildTextField(
|
|
||||||
label: 'Departure Frequency',
|
|
||||||
controller: _expectedFrequencyController,
|
|
||||||
currentFocus: _frequencyFocusNode,
|
|
||||||
isLastField: true,
|
|
||||||
keyboardType:
|
|
||||||
const TextInputType.numberWithOptions(decimal: true),
|
|
||||||
inputFormatters: [FrequencyInputFormatter()],
|
|
||||||
enableAutocorrect: false, // Disable autocorrect
|
|
||||||
enableSuggestions: false, // Disable suggestions
|
|
||||||
enableIMEPersonalizedLearning:
|
|
||||||
false, // iOS-specific
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _skipReadback,
|
|
||||||
child: const Text('Skip to Readback'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _navigateToComparisonPage,
|
|
||||||
child: const Text('Navigate to Comparison Page'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'expectation_input_page.dart';
|
import 'expectation_input_page.dart';
|
||||||
import 'edit_clearance_page.dart'; // Import the edit clearance page
|
// Import the edit clearance page
|
||||||
import '../widgets/clearance_field.dart';
|
import '../widgets/clearance_field.dart';
|
||||||
|
|
||||||
class FinalClearanceDisplay extends StatefulWidget {
|
class FinalClearanceDisplay extends StatefulWidget {
|
||||||
@@ -44,33 +44,6 @@ class FinalClearanceDisplayState extends State<FinalClearanceDisplay> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Navigate to Edit Clearance Page and handle returned data
|
// Navigate to Edit Clearance Page and handle returned data
|
||||||
Future<void> _navigateToEditClearance() async {
|
|
||||||
final result = await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => EditClearancePage(
|
|
||||||
clearanceLimit: clearanceLimit,
|
|
||||||
route: route,
|
|
||||||
altitude: altitude,
|
|
||||||
squawk: squawk,
|
|
||||||
frequency: frequency,
|
|
||||||
isDarkMode: widget.isDarkMode,
|
|
||||||
toggleDarkMode: widget.toggleDarkMode,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the result contains updated data, update the state with the new values
|
|
||||||
if (result != null && mounted) {
|
|
||||||
setState(() {
|
|
||||||
clearanceLimit = result['clearanceLimit'];
|
|
||||||
route = result['route'];
|
|
||||||
altitude = result['altitude'];
|
|
||||||
squawk = result['squawk'];
|
|
||||||
frequency = result['frequency'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigate back to the first page (ExpectationInputPage)
|
// Navigate back to the first page (ExpectationInputPage)
|
||||||
void _navigateHome(BuildContext context) {
|
void _navigateHome(BuildContext context) {
|
||||||
@@ -97,18 +70,7 @@ class FinalClearanceDisplayState extends State<FinalClearanceDisplay> {
|
|||||||
_navigateHome(context);
|
_navigateHome(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
actions: [
|
actions: const [],
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.edit), // Edit button
|
|
||||||
onPressed: _navigateToEditClearance,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(widget.isDarkMode ? Icons.dark_mode : Icons.light_mode),
|
|
||||||
onPressed: () {
|
|
||||||
widget.toggleDarkMode();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
|||||||
@@ -1,68 +1,68 @@
|
|||||||
{
|
{
|
||||||
"info": {
|
"images" : [
|
||||||
"version": 1,
|
{
|
||||||
"author": "xcode"
|
"size" : "16x16",
|
||||||
|
"idiom" : "mac",
|
||||||
|
"filename" : "app_icon_16.png",
|
||||||
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
"images": [
|
{
|
||||||
{
|
"size" : "16x16",
|
||||||
"size": "16x16",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_32.png",
|
||||||
"filename": "app_icon_16.png",
|
"scale" : "2x"
|
||||||
"scale": "1x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "32x32",
|
||||||
"size": "16x16",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_32.png",
|
||||||
"filename": "app_icon_32.png",
|
"scale" : "1x"
|
||||||
"scale": "2x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "32x32",
|
||||||
"size": "32x32",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_64.png",
|
||||||
"filename": "app_icon_32.png",
|
"scale" : "2x"
|
||||||
"scale": "1x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "128x128",
|
||||||
"size": "32x32",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_128.png",
|
||||||
"filename": "app_icon_64.png",
|
"scale" : "1x"
|
||||||
"scale": "2x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "128x128",
|
||||||
"size": "128x128",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_256.png",
|
||||||
"filename": "app_icon_128.png",
|
"scale" : "2x"
|
||||||
"scale": "1x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "256x256",
|
||||||
"size": "128x128",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_256.png",
|
||||||
"filename": "app_icon_256.png",
|
"scale" : "1x"
|
||||||
"scale": "2x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "256x256",
|
||||||
"size": "256x256",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_512.png",
|
||||||
"filename": "app_icon_256.png",
|
"scale" : "2x"
|
||||||
"scale": "1x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "512x512",
|
||||||
"size": "256x256",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_512.png",
|
||||||
"filename": "app_icon_512.png",
|
"scale" : "1x"
|
||||||
"scale": "2x"
|
},
|
||||||
},
|
{
|
||||||
{
|
"size" : "512x512",
|
||||||
"size": "512x512",
|
"idiom" : "mac",
|
||||||
"idiom": "mac",
|
"filename" : "app_icon_1024.png",
|
||||||
"filename": "app_icon_512.png",
|
"scale" : "2x"
|
||||||
"scale": "1x"
|
}
|
||||||
},
|
],
|
||||||
{
|
"info" : {
|
||||||
"size": "512x512",
|
"version" : 1,
|
||||||
"idiom": "mac",
|
"author" : "xcode"
|
||||||
"filename": "app_icon_1024.png",
|
}
|
||||||
"scale": "2x"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
120
pubspec.lock
@@ -1,22 +1,6 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
archive:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: archive
|
|
||||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.6.1"
|
|
||||||
args:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: args
|
|
||||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.6.0"
|
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -41,22 +25,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
checked_yaml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: checked_yaml
|
|
||||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.3"
|
|
||||||
cli_util:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: cli_util
|
|
||||||
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.1"
|
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -73,14 +41,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
crypto:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: crypto
|
|
||||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.6"
|
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -118,14 +78,6 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_launcher_icons:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_launcher_icons
|
|
||||||
sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.14.1"
|
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -160,46 +112,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
image:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image
|
|
||||||
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.3.0"
|
|
||||||
json_annotation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: json_annotation
|
|
||||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.9.0"
|
|
||||||
keyboard_actions:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: keyboard_actions
|
|
||||||
sha256: "31e0ab2a706ac8f58887efa60efc1f19aecdf37d8ab0f665a0f156d1fbeab650"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.2.0"
|
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.4"
|
version: "10.0.5"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.5"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -228,18 +156,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.0"
|
version: "0.11.1"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.0"
|
version: "1.15.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -272,14 +200,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
petitparser:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: petitparser
|
|
||||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.0.2"
|
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -401,10 +321,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.0"
|
version: "0.7.2"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -425,10 +345,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.1"
|
version: "14.2.5"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -445,22 +365,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
xml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xml
|
|
||||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.5.0"
|
|
||||||
yaml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: yaml
|
|
||||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.2"
|
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.4.3 <4.0.0"
|
dart: ">=3.4.3 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.22.0"
|
||||||
|
|||||||
@@ -36,9 +36,6 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
http: ^1.2.2
|
http: ^1.2.2
|
||||||
shared_preferences: ^2.3.2
|
shared_preferences: ^2.3.2
|
||||||
keyboard_actions: ^4.2.0
|
|
||||||
flutter_launcher_icons: ^0.14.1
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
BIN
web/favicon.png
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 403 B |
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||||
<meta name="description" content="A new Flutter project.">
|
<meta name="description" content="Write down IFR Clearances easily">
|
||||||
|
|
||||||
<!-- No cache tag -->
|
<!-- No cache tag -->
|
||||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<!-- iOS meta tags & icons -->
|
<!-- iOS meta tags & icons -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="apple-mobile-web-app-title" content="ifrbuddy">
|
<meta name="apple-mobile-web-app-title" content="IFR Buddy">
|
||||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "ifrbuddy",
|
"name": "IFR Buddy",
|
||||||
"short_name": "ifrbuddy",
|
"short_name": "IFR Buddy",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#hexcode",
|
"background_color": "#0175C2",
|
||||||
"theme_color": "#hexcode",
|
"theme_color": "#0175C2",
|
||||||
"description": "A new Flutter project.",
|
"description": "Write down IFR Clearances easily",
|
||||||
"orientation": "portrait-primary",
|
"orientation": "portrait-primary",
|
||||||
"prefer_related_applications": false,
|
"prefer_related_applications": false,
|
||||||
"icons": [
|
"icons": [
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 33 KiB |