import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; // Correct package import import 'comparison_page.dart'; import 'final_clearance_page.dart'; // Import final clearance page import '../utils/frequency_input_formatter.dart'; class ExpectationInputPage extends StatefulWidget { final bool isDarkMode; final Function toggleDarkMode; const ExpectationInputPage({ super.key, required this.isDarkMode, required this.toggleDarkMode, }); @override ExpectationInputPageState createState() => ExpectationInputPageState(); } class ExpectationInputPageState extends State { final _formKey = GlobalKey(); final TextEditingController _expectedClearanceLimitController = TextEditingController(); final TextEditingController _expectedRouteController = TextEditingController(); final TextEditingController _expectedAltitudeController = TextEditingController(); final TextEditingController _expectedSquawkController = TextEditingController(); final TextEditingController _expectedFrequencyController = TextEditingController(); // Reintroduce FocusNodes final FocusNode _clearanceLimitFocusNode = FocusNode(); final FocusNode _routeFocusNode = FocusNode(); final FocusNode _altitudeFocusNode = FocusNode(); final FocusNode _squawkFocusNode = FocusNode(); final FocusNode _frequencyFocusNode = FocusNode(); @override void dispose() { _expectedClearanceLimitController.dispose(); _expectedRouteController.dispose(); _expectedAltitudeController.dispose(); _expectedSquawkController.dispose(); _expectedFrequencyController.dispose(); _clearanceLimitFocusNode.dispose(); _routeFocusNode.dispose(); _altitudeFocusNode.dispose(); _squawkFocusNode.dispose(); _frequencyFocusNode.dispose(); super.dispose(); } void _navigateToComparisonPage() { if (_formKey.currentState?.validate() ?? false) { Navigator.push( context, MaterialPageRoute( builder: (context) => ComparisonPage( expectedClearanceLimit: _expectedClearanceLimitController.text, expectedRoute: _expectedRouteController.text, expectedAltitude: _expectedAltitudeController.text, expectedSquawk: _expectedSquawkController.text, expectedFrequency: _expectedFrequencyController.text, isDarkMode: widget.isDarkMode, toggleDarkMode: widget.toggleDarkMode, ), ), ); } } // Function to skip directly to final clearance page void _skipReadback() { if (_formKey.currentState?.validate() ?? false) { Navigator.push( context, MaterialPageRoute( builder: (context) => FinalClearanceDisplay( clearanceLimit: _expectedClearanceLimitController.text, route: _expectedRouteController.text, altitude: _expectedAltitudeController.text, squawk: _expectedSquawkController.text, frequency: _expectedFrequencyController.text, isDarkMode: widget.isDarkMode, toggleDarkMode: widget.toggleDarkMode, ), ), ); } } // Updated method to build a text field with optional input Widget buildTextField({ required String label, required TextEditingController controller, required FocusNode currentFocus, FocusNode? nextFocus, TextInputType keyboardType = TextInputType.text, List? inputFormatters, bool isLastField = false, bool enableAutocorrect = false, // To disable autocorrect bool enableSuggestions = false, // To disable suggestions bool enableIMEPersonalizedLearning = false, // iOS-specific }) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: TextFormField( controller: controller, focusNode: currentFocus, keyboardType: keyboardType, textInputAction: isLastField ? TextInputAction.done : TextInputAction.next, inputFormatters: inputFormatters, autocorrect: enableAutocorrect, enableSuggestions: enableSuggestions, enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, onFieldSubmitted: (_) { if (isLastField) { // If it's the last field, unfocus to close the keyboard currentFocus.unfocus(); // Optionally, you can trigger form submission here // _navigateToComparisonPage(); } else if (nextFocus != null) { FocusScope.of(context).requestFocus(nextFocus); } }, // Updated validator to allow empty inputs 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 (keyboardType == TextInputType.number || keyboardType == const TextInputType.numberWithOptions(decimal: true)) { final number = double.tryParse(value); if (number == null) { return 'Please enter a valid number'; } // Add more specific validations if needed } // Example: Validate Transponder (Squawk) field if (label == 'Transponder (Squawk)') { if (!RegExp(r'^[0-7]{1,4}$').hasMatch(value)) { return 'Squawk must be 1-4 digits between 0 and 7'; } } // Add other conditional validations as necessary } // If no issues, return null return null; }, decoration: InputDecoration( labelText: label, border: const OutlineInputBorder(), ), ), ); } // 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 Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('IFR Buddy'), actions: [ IconButton( icon: Icon( widget.isDarkMode ? Icons.dark_mode : Icons.light_mode), onPressed: () { widget.toggleDarkMode(); }, ), ], ), body: GestureDetector( // Dismiss the keyboard when tapping outside onTap: () => FocusScope.of(context).unfocus(), child: KeyboardActions( config: _buildConfig(context), // Apply KeyboardActionsConfig child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( children: [ const Card( elevation: 2, child: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Welcome to IFR Buddy!', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold), textAlign: TextAlign.left, ), SizedBox(height: 10), Text( 'Just an easy tool for writing down IFR clearances without a pen.', style: TextStyle(fontSize: 16), textAlign: TextAlign.left, ), ], ), ), ), const SizedBox(height: 16), Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Expected Clearance', 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, 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'), ), ], ), ), ], ), ), ), ], ), ), ), ), ); } }