4

I have added validator in TextField but validator is not working while submitting data. Save button saves the data with null value.

import 'package:cloud_firestore/cloud_firestore.dart';
import '../../screens/orders/order_success.dart';
import '../../services/global_methods.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class PlaceOrder extends StatefulWidget {
  static const routeName = '/place-order';

  const PlaceOrder({Key? key}) : super(key: key);

  @override
  State<PlaceOrder> createState() => _PlaceOrderState();
}

class _PlaceOrderState extends State<PlaceOrder> {

  final _formKey = GlobalKey<FormState>();
  String? _name;
  int? _phoneNumber;
  String? _fullAddress;
  String? _area;
  String? _city;
  String? _addressType;
  final TextEditingController _addressController = TextEditingController();
  String? _addressValue;
  String? _deliverySlotValue;
  final FirebaseAuth _auth = FirebaseAuth.instance;
  late bool _isLoading = false;
  final GlobalMethods _globalMethods = GlobalMethods();
  final FocusNode _numberFocusNode = FocusNode();

  @override
  void initState() {
    setState(() {
      _isLoading = false;
    });
    super.initState();
  }

  @override
  void dispose() {
    _numberFocusNode.dispose();
    super.dispose();
  }

  void _submitData() async {
    final _isValid = _formKey.currentState!.validate();
    FocusScope.of(context).unfocus();
    if (_isValid) {
      _formKey.currentState!.save();
    }
    try {
      setState(() {
        _isLoading = true;
      });
      final User? user = _auth.currentUser;
      final _uid = user!.uid;
      final orderId = ModalRoute.of(context)!.settings.arguments as String;
      FirebaseFirestore.instance
          .collection('orders')
          .doc(orderId)
          .set({
        'userId': _uid,
        'name': _name,
        'phoneNumber': _phoneNumber,
        'addressType': _addressType,
        'address': _fullAddress,
        'area': _area,
        'city': _city,
        'deliverySlot': _deliverySlotValue,
      }, SetOptions(merge: true));

    } catch (error) {
      _globalMethods.authDialog(context, error.toString());
    } finally {
      setState(() {
        _isLoading = false;
      });
      Navigator.of(context).pushReplacementNamed(OrderSuccess.routeName);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Add new address'),
      ),
      body: Form(
        key: _formKey,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Row(
                  children: [
                    DropdownButton<String>(
                      items: const [
                        DropdownMenuItem<String>(
                          child: Text('Home'),
                          value: 'Home',
                        ),
                        DropdownMenuItem<String>(
                          child: Text('Office'),
                          value: 'Office',
                        ),
                        DropdownMenuItem<String>(
                          child: Text('Other'),
                          value: 'Other',
                        ),
                      ],
                      onChanged: (value) {
                        setState(() {
                          _addressValue = value.toString();
                          _addressController.text = value.toString();

                          print(_addressType);
                        });
                      },
                      hint: const Text('Select address type'),
                      value: _addressValue,
                    ),
                    const SizedBox(
                      width: 10,
                    ),
                    Expanded(
                      child: TextFormField(
                        key: const ValueKey('addressType'),
                        controller: _addressController,
                        validator: (val) {
                          if (val!.isEmpty) {
                            return 'Please select address type';
                          }
                          return null;
                        },
                        decoration: const InputDecoration(
                          labelText: 'Select your address type',
                        ),
                        onSaved: (val) {
                          _addressType = val.toString();
                        },
                      ),
                    ),
                  ],
                ),
                TextFormField(
                  onSaved: (value) {
                    _name = value!;
                  },
                  textInputAction: TextInputAction.next,
                  key: const ValueKey('name'),
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please enter your name';
                    }
                    return null;
                  },
                  decoration: InputDecoration(
                    labelText: 'Name',
                    filled: true,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                    ),
                    prefixIcon: const Icon(Icons.location_city),
                  ),
                ),
                const SizedBox(height: 8),
                TextFormField(
                  onSaved: (value) {
                    _fullAddress = value!;
                  },
                  textInputAction: TextInputAction.next,
                  key: const ValueKey('streetAddress'),
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please enter your address';
                    }
                    return null;
                  },
                  decoration: InputDecoration(
                    labelText: 'Address',
                    filled: true,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                    ),
                    prefixIcon: const Icon(Icons.location_city),
                  ),
                ),
                const SizedBox(height: 8),
                TextFormField(
                  onSaved: (value) {
                    _area = value!;
                  },
                  textInputAction: TextInputAction.next,
                  key: const ValueKey('area'),
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please enter your area';
                    }
                    return null;
                  },
                  decoration: InputDecoration(
                    labelText: 'Area',
                    filled: true,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                    ),
                    prefixIcon: const Icon(Icons.location_city),
                  ),
                ),
                const SizedBox(height: 8),
                TextFormField(
                  onSaved: (value) {
                    _city = value!;
                  },
                  textInputAction: TextInputAction.next,
                  key: const ValueKey('city'),
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please enter your city';
                    }
                    return null;
                  },
                  decoration: InputDecoration(
                    labelText: 'City',
                    filled: true,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                    ),
                    prefixIcon: const Icon(Icons.location_city),
                  ),
                ),
                const SizedBox(height: 8),
                TextFormField(
                  onSaved: (value) {
                    _phoneNumber = int.parse(value!);
                  },
                  textInputAction: TextInputAction.next,
                  // keyboardType: TextInputType.emailAddress,
                  onEditingComplete: () =>
                      FocusScope.of(context).requestFocus(_numberFocusNode),
                  key: const ValueKey('number'),
                  validator: (value) {
                    if (value!.length < 10) {
                      return 'Phone number must be 10 units';
                    }
                    return null;
                  },
                  decoration: InputDecoration(
                    labelText: 'Phone Number',
                    filled: true,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                    ),
                    prefixIcon: const Icon(Icons.phone),
                  ),
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    DropdownButton<String>(
                      items: const [
                        DropdownMenuItem<String>(
                          child: Text('11:00am to 1:00pm'),
                          value: '11:00am to 1:00pm',
                        ),
                        DropdownMenuItem<String>(
                          child: Text('1:00pm to 3:00pm'),
                          value: '1:00pm to 3:00pm',
                        ),
                        DropdownMenuItem<String>(
                          child: Text('3:00pm to 5:00pm'),
                          value: '3:00pm to 5:pm',
                        ),
                        DropdownMenuItem<String>(
                          child: Text('5:00pm to 7:00pm'),
                          value: '5:00pm to 7:00pm',
                        ),
                        DropdownMenuItem<String>(
                          child: Text('7:00pm to 9:pm'),
                          value: '7:00pm to 9:pm',
                        ),
                      ],
                      onChanged: (value) {
                        setState(() {
                          _deliverySlotValue = value.toString();
                        });
                      },
                      hint: const Text('Select Delivery Slot'),
                      value: _deliverySlotValue,
                    ),
                  ],
                ),
                Padding(
                  padding: const EdgeInsets.all(48.0),
                  child: SizedBox(
                    child: ElevatedButton(
                      onPressed: _submitData,
                      child: _isLoading
                          ? const CircularProgressIndicator()
                          : const Text(
                        'S U B M I T',
                        style: TextStyle(color: Colors.white),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
 }
Tushar Rai
  • 2,371
  • 4
  • 28
  • 57

3 Answers3

7

It appears that the problem is a simple mistake with your control flow in the following code (see // comment):

 void _submitData() async {
    final _isValid = _formKey.currentState!.validate();
    FocusScope.of(context).unfocus();
    if (_isValid) {
      _formKey.currentState!.save();
    } // <----- this should not be here!
    try {
      setState(() {
        _isLoading = true;
      });
      final User? user = _auth.currentUser;
      final _uid = user!.uid;
      final orderId = ModalRoute.of(context)!.settings.arguments as String;
      FirebaseFirestore.instance
          .collection('orders')
          .doc(orderId)
          .set({
        'userId': _uid,
        'name': _name,
        'phoneNumber': _phoneNumber,
        'addressType': _addressType,
        'address': _fullAddress,
        'area': _area,
        'city': _city,
        'deliverySlot': _deliverySlotValue,
      }, SetOptions(merge: true));

    } catch (error) {
      _globalMethods.authDialog(context, error.toString());
    } finally {
      setState(() {
        _isLoading = false;
      });
      Navigator.of(context).pushReplacementNamed(OrderSuccess.routeName);
    }
  }

As you can see, even if the user input is not valid, you still continue to upload the results to Firebase anyway.

Try correcting like this:

void _submitData() async {
    final _isValid = _formKey.currentState!.validate();
    FocusScope.of(context).unfocus();
    if (_isValid) {
      _formKey.currentState!.save();
      try {
        setState(() {
          _isLoading = true;
        });
        final User? user = _auth.currentUser;
        final _uid = user!.uid;
        final orderId = ModalRoute.of(context)!.settings.arguments as String;
        FirebaseFirestore.instance.collection('orders').doc(orderId).set({
          'userId': _uid,
          'name': _name,
          'phoneNumber': _phoneNumber,
          'addressType': _addressType,
          'address': _fullAddress,
          'area': _area,
          'city': _city,
          'deliverySlot': _deliverySlotValue,
        }, SetOptions(merge: true));
      } catch (error) {
        _globalMethods.authDialog(context, error.toString());
      } finally {
        setState(() {
          _isLoading = false;
        });
        Navigator.of(context).pushReplacementNamed(OrderSuccess.routeName);
      }
    } else {
      // do nothing
    }
  }
Nerdy Bunz
  • 6,040
  • 10
  • 41
  • 100
0

The only possible reason is because of _addressType it's initial value is null since it's a String? variable so if the user didn't select a "Delivery Slot" the result of it after saving would be null,

what you need to do, it to check the value of _addressType if it's null or not then proceed to saving the form because your form doesn't check it if it's null or not.

tareq albeesh
  • 1,701
  • 2
  • 10
  • 13
-1

You can add the form validator to the submit button itself. The below code works when you click on the submit button.

Padding(
   padding: const EdgeInsets.all(48.0),
   child: SizedBox(
   child: ElevatedButton(
     onPressed: (){
       if (_formKey.currentState!.validate()){
         _submitData;
     }
   },
   child: _isLoading
   ? const CircularProgressIndicator()
   : const Text(
   'S U B M I T',
    style: TextStyle(color: Colors.white),
   ),
  ),
 ),
),
Code With Bishal
  • 129
  • 2
  • 16