Skip to content
This repository has been archived by the owner on May 19, 2019. It is now read-only.

Getting error with date picker fields value not set. #1

Closed
debasiskoley opened this issue May 13, 2019 · 2 comments
Closed

Getting error with date picker fields value not set. #1

debasiskoley opened this issue May 13, 2019 · 2 comments
Labels
question Further information is requested

Comments

@debasiskoley
Copy link

debasiskoley commented May 13, 2019

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(),
);
}
}
`

@frideosapps
Copy link
Owner

Hello @debasiskoley, you missed to change the checkForm method accordingly:

    for (var item in dateFields.value) {
      if (item.value != null) {
        if (item.value.isEmpty) {
          item.stream.sink.addError('Enter a valid date.');
          isValidFieldsTypeDate = false; //This was isValidFieldsTypeName
        }
      } else {
        isValidFieldsTypeDate = false; // same here
      } 
    }

and in the FieldsWidget you don't need a stateful, but you have to send the date to stream (in this case you are using the onChanged of the textfield only to check if the value is null, e.g. if you delete the date):

class FieldsWidget extends StatelessWidget {
  FieldsWidget({
    this.index,
    this.nameController,
    this.dateController,
  });

  final int index;
  final TextEditingController nameController;
  final TextEditingController dateController;
  String 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) {
      selectedDate =
          formatDate(pickedStartDate, [dd, ' ', M, ' ', yyyy]).toString();
      dateController.text = selectedDate;
      bloc.dateFields.value[index].value = selectedDate;  // Add this to send to stream the selected date
    }
  }

Also you need to specify the types in the StreamedValues and StreamBuilders. Avoid to use the dynamic type is not necessary.

  final nameFields = StreamedList<StreamedValue<String>>(initialData: []); // StreamedValue<String>
  final dateFields = StreamedList<StreamedValue<String>>(initialData: []);

 StreamBuilder<bool>
 StreamBuilder<String>

Finally, you have to specify the widget on the State class:

class _DynamicFieldsWidgetState extends State<DynamicFieldsWidget> {

@debasiskoley
Copy link
Author

Hi,
It's working now. Thank you.

@frideosapps frideosapps added the question Further information is requested label May 13, 2019
@frideosapps frideosapps pinned this issue May 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants