Hi,
Please check the code. I'm getting an error with the date picker. After setting the date in text field it's not validating or getting the value after submit the form.
bloc.dart file
`
import 'package:frideos_core/frideos_core.dart';
class DynamicFieldsBloc {
DynamicFieldsBloc() {
print('-------DynamicFields BLOC--------');
// Adding the initial three pairs of fields to the screen
nameFields.addAll([
StreamedValue<String>(initialData: 'Name AA'),
StreamedValue<String>(initialData: 'Name BB'),
StreamedValue<String>(initialData: 'Name CC')
]);
dateFields.addAll([
StreamedValue<String>(initialData: '11-02-2018'),
StreamedValue<String>(initialData: '22-03-2018'),
StreamedValue<String>(initialData: '13-04-2018')
]);
// Set the method to call every time the stream emits a new event
for (var item in nameFields.value) {
item.onChange(checkForm);
}
for (var item in dateFields.value) {
item.onChange(checkForm);
}
}
// A StreamedList holds a list of StreamedValue of type String so
// it is possibile to add more items.
final nameFields = StreamedList<StreamedValue>(initialData: []);
final dateFields = StreamedList<StreamedValue>(initialData: []);
// This StreamedValue is used to handle the current validation state
// of the form.
final isFormValid = StreamedValue();
// Every time the user clicks on the "New fields" button, this method
// adds two new fields and sets the checkForm method to be called
// every time these new fields change.
void newFields() {
nameFields.addElement(StreamedValue());
dateFields.addElement(StreamedValue());
nameFields.value.last.onChange(checkForm);
dateFields.value.last.onChange(checkForm);
// This is used to force the checking of the form so that, adding
// the new fields, it can reveal them as empty and sets the form
// to not valid.
checkForm(null);
}
void checkForm(String _) {
// These two boolean flags will be used to determine whether each group of fields
// (name or age) is valid or not.
bool isValidFieldsTypeName = true;
bool isValidFieldsTypeDate = true;
// Checking each name fields: if an item is empty an error will be
// sent to stream and the `isValidFieldsTypeName` set to false. Instead,
// if the item is null (e.g when new fields are added),
// it only sets the `isValidFieldsTypeName` to null in order to disable
// the submit button.
for (var item in nameFields.value) {
if (item.value != null) {
if (item.value.isEmpty) {
item.stream.sink.addError('The text must not be empty.');
isValidFieldsTypeName = false;
}
} else {
isValidFieldsTypeName = false;
}
}
// Similarly to the previous check, for the date fields is checked
for (var item in dateFields.value) {
if (item.value != null) {
if (item.value.isEmpty) {
item.stream.sink.addError('Enter a valid date.');
isValidFieldsTypeName = false;
}
} else {
isValidFieldsTypeName = false;
}
}
// If both of each groups are valid it is given a true value
// to isFormValid (StreamedValue<bool>) sending the true event
// to the stream triggering the StreamBuilder to rebuild and
// enable the submit button. If the condition is not met, then
// a null value is sent to the stream and the button will be
// disabled.
if (isValidFieldsTypeName && isValidFieldsTypeDate) {
isFormValid.value = true;
} else {
isFormValid.value = null;
}
}
void submit() {
for(var i=0; i<dateFields.length;i++){
print('dates: '+dateFields.value[i].value.toString());
}
}
void removeFields(int index) {
nameFields.removeAt(index);
dateFields.removeAt(index);
}
void dispose() {
print('-------DynamicFields BLOC DISPOSE--------');
for (var item in nameFields.value) {
item.dispose();
}
nameFields.dispose();
for (var item in dateFields.value) {
item.dispose();
}
dateFields.dispose();
isFormValid.dispose();
}
}
final bloc = DynamicFieldsBloc();
`
dynamicFields.dart file
`
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:frideos_core/frideos_core.dart';
import 'package:frideos/frideos.dart';
import 'package:date_format/date_format.dart';
import '../bloc/bloc.dart';
const TextStyle buttonText = TextStyle(color: Colors.white);
class DynamicFieldsPage extends StatefulWidget {
@OverRide
_DynamicFieldsPageState createState() => _DynamicFieldsPageState();
}
class _DynamicFieldsPageState extends State {
@OverRide
void dispose() {
bloc.dispose();
super.dispose();
}
@OverRide
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: const Text('Dynamic fields validation'),
),
body: DynamicFieldsWidget(),
),
);
}
}
class DynamicFieldsWidget extends StatefulWidget {
@OverRide
_DynamicFieldsWidgetState createState() => _DynamicFieldsWidgetState();
}
class _DynamicFieldsWidgetState extends State {
final nameFieldsController = List();
var dateFieldsController = List();
@OverRide
Widget build(BuildContext context) {
List _buildFields(int length) {
// Clear the TextEditingControllers lists
nameFieldsController.clear();
dateFieldsController.clear();
for (int i = 0; i < length; i++) {
final name = bloc.nameFields.value[i].value;
final date = bloc.dateFields.value[i].value;
nameFieldsController.add(TextEditingController(text: name));
dateFieldsController.add(TextEditingController(text: date));
}
return List<Widget>.generate(
length,
(i) => FieldsWidget(
index: i,
nameController: nameFieldsController[i],
dateController: dateFieldsController[i],
),
);
}
return ListView(
children: <Widget>[
Container(
height: 16.0,
),
ValueBuilder<List<StreamedValue<String>>>(
streamed: bloc.nameFields,
builder: (context, snapshot) {
return Column(
children: _buildFields(snapshot.data.length),
);
},
noDataChild: const Text('NO DATA'),
),
const SizedBox(
height: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
StreamBuilder<bool>(
stream: bloc.isFormValid.outStream,
builder: (context, snapshot) {
return RaisedButton(
padding: EdgeInsets.fromLTRB(50, 15, 50, 15),
elevation: 1,
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(50.0)),
color: Color(0xFF0BBD72),
child: snapshot.hasData
? snapshot.data
? const Text('Submit', style: buttonText)
: const Text('Form not valid', style: buttonText)
: const Text('Form not valid', style: buttonText),
onPressed: snapshot.hasData ? bloc.submit : null,
);
}),
OutlineButton(
padding: EdgeInsets.fromLTRB(30, 15, 30, 15),
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(50.0)),
color: Color(0xFF626775),
child: new Text(
'Add more',
style: new TextStyle(fontSize: 16.0, color: Color(0xFF626775))
),
onPressed: bloc.newFields
),
],
),
const SizedBox(
height: 10.0,
),
],
);
}
}
class FieldsWidget extends StatefulWidget {
const FieldsWidget({
this.index,
this.nameController,
this.dateController,
});
final int index;
final TextEditingController nameController;
final TextEditingController dateController;
@OverRide
_FieldsWidgetState createState() => _FieldsWidgetState();
}
class _FieldsWidgetState extends State {
dynamic selectedDate = '';
_selectDate(BuildContext context) async {
final DateTime pickedStartDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2015, 8),
lastDate: DateTime(2022));
if (pickedStartDate != null)
setState(() {
selectedDate = formatDate(pickedStartDate, [dd, ' ', M, ' ', yyyy]).toString();
widget.dateController.text = selectedDate;
});
}
@OverRide
void initState() {
super.initState();
}
@OverRide
Widget build(BuildContext context) {
return Stack(
children: [
_buildFormFields(context),
],
);
}
Widget _buildFormFields(context){
return Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text('${widget.index + 1}:')),
Expanded(
child: Column(
children: [
StreamBuilder(
initialData: ' ',
stream: bloc.nameFields.value[widget.index].outStream,
builder: (context, snapshot) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: TextField(
controller: widget.nameController,
style: const TextStyle(
fontSize: 14,
color: Colors.black,
),
decoration: InputDecoration(
labelText: 'Name:',
hintText: 'Insert a name...',
errorText: snapshot.error,
),
onChanged: bloc.nameFields.value[widget.index].inStream,
),
),
],
);
}),
StreamBuilder(
initialData: ' ',
stream: bloc.dateFields.value[widget.index].outStream,
builder: (context, snapshot) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: TextField(
onTap: ()=> _selectDate(context),
controller: widget.dateController,
style: const TextStyle(
fontSize: 14,
color: Colors.black,
),
decoration: InputDecoration(
labelText: 'Date:',
hintText: 'Add Date',
errorText: snapshot.error,
),
keyboardType: TextInputType.datetime,
onChanged: bloc.dateFields.value[widget.index].inStream,
),
),
],
);
}),
const SizedBox(
height: 22.0,
),
],
),
),
IconButton(
icon: const Icon(Icons.delete),
color: Colors.red,
onPressed: () => bloc.removeFields(widget.index),
),
],
);
}
}
`
main.dart file
`
import 'package:flutter/material.dart';
import '../ui/dynamic_fields.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@OverRide
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DynamicFieldsPage(),
);
}
}
`
Hi,
Please check the code. I'm getting an error with the date picker. After setting the date in text field it's not validating or getting the value after submit the form.
bloc.dart file
`
import 'package:frideos_core/frideos_core.dart';
class DynamicFieldsBloc {
DynamicFieldsBloc() {
print('-------DynamicFields BLOC--------');
}
// A StreamedList holds a list of StreamedValue of type String so
// it is possibile to add more items.
final nameFields = StreamedList<StreamedValue>(initialData: []);
final dateFields = StreamedList<StreamedValue>(initialData: []);
// This StreamedValue is used to handle the current validation state
// of the form.
final isFormValid = StreamedValue();
// Every time the user clicks on the "New fields" button, this method
// adds two new fields and sets the checkForm method to be called
// every time these new fields change.
void newFields() {
nameFields.addElement(StreamedValue());
dateFields.addElement(StreamedValue());
}
void checkForm(String _) {
// These two boolean flags will be used to determine whether each group of fields
// (name or age) is valid or not.
bool isValidFieldsTypeName = true;
bool isValidFieldsTypeDate = true;
}
void submit() {
for(var i=0; i<dateFields.length;i++){
print('dates: '+dateFields.value[i].value.toString());
}
}
void removeFields(int index) {
nameFields.removeAt(index);
dateFields.removeAt(index);
}
void dispose() {
print('-------DynamicFields BLOC DISPOSE--------');
}
}
final bloc = DynamicFieldsBloc();
`
dynamicFields.dart file
`
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:frideos_core/frideos_core.dart';
import 'package:frideos/frideos.dart';
import 'package:date_format/date_format.dart';
import '../bloc/bloc.dart';
const TextStyle buttonText = TextStyle(color: Colors.white);
class DynamicFieldsPage extends StatefulWidget {
@OverRide
_DynamicFieldsPageState createState() => _DynamicFieldsPageState();
}
class _DynamicFieldsPageState extends State {
@OverRide
void dispose() {
bloc.dispose();
super.dispose();
}
@OverRide
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: const Text('Dynamic fields validation'),
),
body: DynamicFieldsWidget(),
),
);
}
}
class DynamicFieldsWidget extends StatefulWidget {
@OverRide
_DynamicFieldsWidgetState createState() => _DynamicFieldsWidgetState();
}
class _DynamicFieldsWidgetState extends State {
final nameFieldsController = List();
var dateFieldsController = List();
@OverRide
Widget build(BuildContext context) {
List _buildFields(int length) {
// Clear the TextEditingControllers lists
nameFieldsController.clear();
dateFieldsController.clear();
}
}
class FieldsWidget extends StatefulWidget {
const FieldsWidget({
this.index,
this.nameController,
this.dateController,
});
final int index;
final TextEditingController nameController;
final TextEditingController dateController;
@OverRide
_FieldsWidgetState createState() => _FieldsWidgetState();
}
class _FieldsWidgetState extends State {
dynamic selectedDate = '';
_selectDate(BuildContext context) async {
final DateTime pickedStartDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2015, 8),
lastDate: DateTime(2022));
if (pickedStartDate != null)
setState(() {
selectedDate = formatDate(pickedStartDate, [dd, ' ', M, ' ', yyyy]).toString();
widget.dateController.text = selectedDate;
});
}
@OverRide
void initState() {
super.initState();
}
@OverRide
Widget build(BuildContext context) {
return Stack(
children: [
_buildFormFields(context),
],
);
}
Widget _buildFormFields(context){
return Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text('${widget.index + 1}:')),
Expanded(
child: Column(
children: [
StreamBuilder(
initialData: ' ',
stream: bloc.nameFields.value[widget.index].outStream,
builder: (context, snapshot) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: TextField(
controller: widget.nameController,
style: const TextStyle(
fontSize: 14,
color: Colors.black,
),
decoration: InputDecoration(
labelText: 'Name:',
hintText: 'Insert a name...',
errorText: snapshot.error,
),
onChanged: bloc.nameFields.value[widget.index].inStream,
),
),
],
);
}),
StreamBuilder(
initialData: ' ',
stream: bloc.dateFields.value[widget.index].outStream,
builder: (context, snapshot) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: TextField(
onTap: ()=> _selectDate(context),
controller: widget.dateController,
style: const TextStyle(
fontSize: 14,
color: Colors.black,
),
decoration: InputDecoration(
labelText: 'Date:',
hintText: 'Add Date',
errorText: snapshot.error,
),
keyboardType: TextInputType.datetime,
onChanged: bloc.dateFields.value[widget.index].inStream,
),
),
],
);
}),
const SizedBox(
height: 22.0,
),
],
),
),
IconButton(
icon: const Icon(Icons.delete),
color: Colors.red,
onPressed: () => bloc.removeFields(widget.index),
),
],
);
}
}
`
main.dart file
`
import 'package:flutter/material.dart';
import '../ui/dynamic_fields.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@OverRide
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DynamicFieldsPage(),
);
}
}
`