Flutter中三TextFormField仅填两项的表单验证与取值实现
Hey there! Let's get your Low Pass Filter form working exactly as you need it—only two fields editable at a time, real-time updates, and the button enabled only when the condition is met. Here's a polished implementation with explanations:
Key Improvements Made:
- Real-time Field State Updates: Added listeners to each
TextEditingControllerto trigger UI updates whenever any field's content changes. - Field Disable Logic: Calculates how many fields are filled, then disables the third one once two are non-empty.
- Button Enable/Disable: The deprecated
RaisedButtonis replaced withElevatedButton, and it's only enabled when exactly two fields are filled. - Deprecated API Fix: Swapped outdated
WhitelistingTextInputFormatterwithFilteringTextInputFormatter.digitsOnly. - Proper Cleanup: Added listener removal in
disposeto prevent memory leaks.
Updated Code:
class LPFilterCalculator extends StatefulWidget { @override State<StatefulWidget> createState() => _LPFilterCalculatorState(); } class _LPFilterCalculatorState extends State<LPFilterCalculator> { @override Widget build(BuildContext context) { return Scaffold( appBar: PersistentAppBar("Low Pass Filter").build(context), drawer: DrawerMenu(), body: Column( children: const <Widget>[ LowPassInputForm(), ], ), ); } } /// Inputform class for Calculators class LowPassInputForm extends StatefulWidget { @override State<StatefulWidget> createState() => _LowPassInputFormState(); } class _LowPassInputFormState extends State<LowPassInputForm> { final _formKey = GlobalKey<FormState>(); final resistanceTextController = TextEditingController(); final capacitorTextController = TextEditingController(); final frequencyTextController = TextEditingController(); @override void initState() { super.initState(); // Add listeners to trigger UI updates on text change resistanceTextController.addListener(_updateFormState); capacitorTextController.addListener(_updateFormState); frequencyTextController.addListener(_updateFormState); } void _updateFormState() { setState(() {}); } // Count how many fields have non-empty values int get _filledFieldCount { int count = 0; if (resistanceTextController.text.isNotEmpty) count++; if (capacitorTextController.text.isNotEmpty) count++; if (frequencyTextController.text.isNotEmpty) count++; return count; } @override Widget build(BuildContext context) { final filledCount = _filledFieldCount; return Form( key: _formKey, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: <Widget>[ TextFormField( controller: resistanceTextController, inputFormatters: const <TextInputFormatter>[ FilteringTextInputFormatter.digitsOnly, ], keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Enter resistance value'), // Disable if two other fields are already filled enabled: filledCount < 2 || (filledCount == 2 && (capacitorTextController.text.isEmpty || frequencyTextController.text.isEmpty)), ), const SizedBox(height: 8), TextFormField( controller: capacitorTextController, inputFormatters: const <TextInputFormatter>[ FilteringTextInputFormatter.digitsOnly, ], keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Enter capacitor value'), enabled: filledCount < 2 || (filledCount == 2 && (resistanceTextController.text.isEmpty || frequencyTextController.text.isEmpty)), ), const SizedBox(height: 8), TextFormField( controller: frequencyTextController, inputFormatters: const <TextInputFormatter>[ FilteringTextInputFormatter.digitsOnly, ], keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: 'Enter desired cutoff frequency'), enabled: filledCount < 2 || (filledCount == 2 && (resistanceTextController.text.isEmpty || capacitorTextController.text.isEmpty)), ), const SizedBox(height: 16), ElevatedButton( elevation: 5.0, // Enable only when exactly two fields are filled onPressed: filledCount == 2 ? calculateLowPass : null, child: const Text('Calculate'), ), ], ), ), ); } void calculateLowPass() { final resistance = double.tryParse(resistanceTextController.text); final capacitor = double.tryParse(capacitorTextController.text); final frequency = double.tryParse(frequencyTextController.text); // Handle each combination of filled fields to compute the missing value if (resistance != null && capacitor != null) { final computedFreq = 1 / (2 * 3.14159 * resistance * capacitor); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Cutoff Frequency: ${computedFreq.toStringAsFixed(2)} Hz')), ); } else if (resistance != null && frequency != null) { final computedCap = 1 / (2 * 3.14159 * resistance * frequency); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Capacitor Value: ${computedCap.toStringAsFixed(6)} F')), ); } else if (capacitor != null && frequency != null) { final computedRes = 1 / (2 * 3.14159 * capacitor * frequency); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Resistance Value: ${computedRes.toStringAsFixed(2)} Ω')), ); } } @override void dispose() { // Clean up listeners and controllers to avoid memory leaks resistanceTextController.removeListener(_updateFormState); capacitorTextController.removeListener(_updateFormState); frequencyTextController.removeListener(_updateFormState); resistanceTextController.dispose(); capacitorTextController.dispose(); frequencyTextController.dispose(); super.dispose(); } }
Breakdown of Key Logic:
- Controller Listeners: In
initState, we attach listeners to each text controller. Whenever the user types or deletes text,_updateFormStatecallssetStateto refresh the UI immediately. - Filled Field Count: The
_filledFieldCountgetter tracks how many fields have content, which drives both the field disable logic and button state. - Field Enable Rules: Each field stays enabled unless exactly two other fields are filled. This ensures users can only edit two fields at a time.
- Button State: The calculate button is only clickable when exactly two fields are filled—setting
onPressedtonullautomatically disables the button. - Calculation Logic: The
calculateLowPassmethod safely parses input values and computes the missing parameter based on which two fields are filled, then shows the result via a snackbar (you can replace this with your own logic).
内容的提问来源于stack exchange,提问作者samlo
相关产品推荐
相关产品推荐

