TextFormField with bloc pattern is Subbmiting null
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?
Comments
-
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.