TextFormField with bloc pattern is Subbmiting null

954

Your code is pretty confusing, but as far as I can see you write to _formData['title'] in onSaved, yet you never call FormState.save().

Have you tried calling _formKey.currentState.save() in the onPressed of the save button?

Share:
954
key
Author by

key

Currently working as a dart, Flutter Developer.

Updated on December 10, 2022

Comments

  • key
    key 11 months

    When using TextFormField, I am not able to add or update data, the data submitted to the db is always null or I get a NoSuchMethodErrorerror

    I/flutter ( 6511): Another exception was thrown: NoSuchMethodError: The getter 'value' was called on null

    I have build the Bloc as so:

    class ProductsBloc {
    
      String id;
    //  Product _product;
    
    
      // ignore: close_sinks
    //  static final _productController = BehaviorSubject<Product>();
    //  Stream<Product> get productOut => _productController.stream;
    
      // ignore: close_sinks
      final _id = BehaviorSubject<int>();
      // ignore: close_sinks
      final _title = BehaviorSubject<String>();
      // ignore: close_sinks
      final _message = BehaviorSubject<String>();
      // ignore: close_sinks
      final _price = BehaviorSubject<String>();
    
      Observable<int> get idOut => _id.stream;
      Observable<String> get titleOut => _title.stream.transform(_validateTitle);
      Observable<String> get message => _message.stream;
      Observable<String> get price => _price.stream;
    
      Function(int) get changeId => _id.sink.add;
      Function(String) get changeTitle => _title.sink.add;
      Function(String) get changeMessage => _message.sink.add;
      Function(String) get changePrice => _price.sink.add;
    
      final _validateTitle = StreamTransformer<String, String>.fromHandlers(handleData: (title, sink){
        if(title.isNotEmpty){
          sink.add(title);
        } else {
          sink.addError('Add some text');
        }
      });
    
      Future<void> createProduct({title}) {
        return db.createProduct(DateTime.now().millisecondsSinceEpoch.toString(), title.value, _message.value, _price.value);
      }
    

    the ui like so:

    class _ProductEditPageState extends State<ProductEditPage> {
    
      final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
      final _titleFocusNode = FocusNode();
      final _descriptionFocusNode = FocusNode();
      final _priceFocusNode = FocusNode();
    
      final id;
      final title;
      final message;
      final price;
    
      _ProductEditPageState(this.id, this.title, this.message, this.price);
    
      final Map<String, dynamic> _formData = {
        'title': null,
        'message': null,
        'price': null,
        'image': 'assets/food.jpg'
      };
    
      Widget _buildTitleTextField(ProductsBloc productBloc) {
        return StreamBuilder(
          stream: productBloc.titleOut,
          builder: (context, snapshot) {
            return TextFormField(
              focusNode: _titleFocusNode,
              onSaved: (String value){ _formData['title'] = value;},
              initialValue: title,
              decoration: InputDecoration(labelText: 'Title', errorText: snapshot.error),
            );
          },
        );
      }
    

    and the submit like this. the submitted data as this catches the error NoSuchMethodFound if I change the submit to no argument or the argument it fills the db with null.

    child: Form(
                key: _formKey,
                child: ListView(
                  padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
                  children: <Widget>[
                    _buildTitleTextField(productBloc, ),
                    _buildDescriptionTextField(productBloc),
                    _buildPriceTextField(productBloc),
                    SizedBox(
                      height: 10.0,
                    ),
                    RaisedButton(
                      child: Text('Save'),
                      textColor: Colors.white,
                      onPressed: () {
                        if(id != null) {
                          productBloc.updateData(id);
                        }
                        else{
                          productBloc.createProduct(
                            title: _formData['title'],
                          );
                        }
                        Navigator.of(context).pop();
                      },
    

    here is also my model

    class Product {
    
      final db = Firestore.instance.collection('products');
    
      Future<void> createProduct(String docId, String title, String message, String price) async {
        await db.document(docId).setData({'id': docId, 'title': title, 'message': message, 'price': price});
      }
    
      void readData(String docId){
        db.document(docId).get();
      }
    
      Future<void> deleteData(String docId) async {
        await db.document(docId).delete();
      }
    
      Future<void> updateData(String docId, String title, String message, String price) async {
        await db.document(docId).updateData({'title': title, 'message': message, 'price': price});
      }
    
    }
    
    Product db = Product();
    

    I'm also using the provider package from flutter so I think the provider is right:

       return MultiProvider(
          providers: [
            Provider<ThemeBloc>(
              value: ThemeBloc(),
            ),
            Provider<UserBloc>(
              value: UserBloc(),
            ),
            Provider<ProductsBloc>(
              value: ProductsBloc(),
            ),
          ],
          child: StreamBuilder<ThemeData>(
    

    using textFields is working fine but I need to see the form filled with some initial data before editing, so obviously I need to have TextFormFields.