0

In flutter, I'm trying to define a form field model. The form type is a enum, based on which different Field are to be returned.

My text field should look like this on toJson():

type : "text"
label : "..."
required : bool
minLength : int | null
maxLength : int | null

The model is defined like so, and uses json_serializable to auto generate the toJson and fromJson methods.

import 'package:json_annotation/json_annotation.dart';

part 'form_model.g.dart';

enum FormFieldType {
  text,
  number,
}


@JsonSerializable(createFactory: false)
abstract class FormFieldModel {
  final String label;
  final FormFieldType type;
  final bool required;

  FormFieldModel({required this.label, required this.type, this.required = true});

  static FormFieldModel fromJson(Map<String, dynamic> json) {
    final type = FormFieldType.values.firstWhere(
      (e) => e.toString() == 'FormFieldType.${json['type']}',
      orElse: () => throw Exception('Unknown form field type: ${json['type']}'),
    );

    switch (type) {
      case FormFieldType.text:
        return WizTextField.fromJson(json);
      case FormFieldType.number:
        return WizNumberField.fromJson(json);
    }
  }
}

@JsonSerializable()
class WizTextField extends FormFieldModel {
  final int? minLength;
  final int? maxLength;

  WizTextField({
    required String label,
    this.minLength,
    this.maxLength,
    bool required = true,
  }) : super(label: label, type: FormFieldType.text, required: required);

  factory WizTextField.fromJson(Map<String, dynamic> json) => _$WizTextFieldFromJson(json);
  @override
  Map<String, dynamic> toJson() => _$WizTextFieldToJson(this);
}

// Similarly for Number

However, when I run dart run build_runner build, to my surprise, I find the 'type' field to be missing.

Map<String, dynamic> _$WizTextFieldToJson(WizTextField instance) =>
    <String, dynamic>{
      'label': instance.label,
      'required': instance.required,
      'minLength': instance.minLength,
      'maxLength': instance.maxLength,
    };

Where am I going wrong?

EDIT 1

(Based comments from one of the User) I tried updating required field to isRequired but the problem persists. Putting complete code for reference (temp.dart)

import 'package:json_annotation/json_annotation.dart';

part 'temp.g.dart';

enum FormFieldType {
  text,
  number,
}


@JsonSerializable(createFactory: false)
abstract class FormFieldModel {
  final String label;
  final FormFieldType type;
  final bool isRequired;

  FormFieldModel({required this.label, required this.type, this.isRequired = true});

  static FormFieldModel fromJson(Map<String, dynamic> json) {
    final type = FormFieldType.values.firstWhere(
      (e) => e.toString() == 'FormFieldType.${json['type']}',
      orElse: () => throw Exception('Unknown form field type: ${json['type']}'),
    );

    switch (type) {
      case FormFieldType.text:
        return WizTextField.fromJson(json);
      case FormFieldType.number:
        return WizNumberField.fromJson(json);
      }
  }
}

@JsonSerializable()
class WizTextField extends FormFieldModel {
  final int? minLength;
  final int? maxLength;

  WizTextField({
    required String label,
    this.minLength,
    this.maxLength,
    bool isRequired = true,
  }) : super(label: label, type: FormFieldType.text, isRequired: isRequired);

  factory WizTextField.fromJson(Map<String, dynamic> json) => _$WizTextFieldFromJson(json);
  @override
  Map<String, dynamic> toJson() => _$WizTextFieldToJson(this);
}

@JsonSerializable()
class WizNumberField extends FormFieldModel {
  final int? min;
  final int? max;

  WizNumberField({
    required String label,
    this.min,
    this.max,
    bool isRequired = true,
  }) : super(label: label, type: FormFieldType.number, isRequired: isRequired);

  factory GWizNumberField.fromJson(Map<String, dynamic> json) => _$WizNumberFieldFromJson(json);
  @override
  Map<String, dynamic> toJson() => _$WizNumberFieldToJson(this);
}

This still gives same autogenerated toJson:

Map<String, dynamic> _$WizNumberFieldToJson(WizNumberField instance) =>
    <String, dynamic>{
      'label': instance.label,
      'isRequired': instance.isRequired,
      'min': instance.min,
      'max': instance.max,
    };

1 Answer 1

0

There's a confusion zone, actually required is a reserved keyword for Dart language, you should try renaming that boolean variable with another name like isRequired.

The reason for being missed in auto-serialization/deserialization generated code could be it's ignored and treated as a keyword rather than an actual variable.

btw, if you have named it required to be compatible with names in the returned json, you could use JsonKey annotation to search and extract the value of the name exist in the json:

@JsonSerializable(createFactory: false)
abstract class FormFieldModel {
  final String label;
  final FormFieldType type;

  @JsonKey(name:'required')
  final bool isRequired;

  ...
 }

EDIT

The above words should be applied, because you shouldn't declare a variable that has the same name of a keyword. reserved keywords are reserved keywords.

So, why type variable doesn't appear in toJson auto-generated code. that's because You have need to tell json_serializable how to serialize and deserialize the enum field. in your problem tell it how to serialize the enumerated type field.

  @JsonKey(fromJson: typeFromJson, toJson: typeToJson)
  final FormFieldType type;

Define these functions as a Top-Level functions (not inside a class).

FormFieldType typeFromJson(String type) {
  switch (type) {
    case 'text':
      return FormFieldType.text;
    case 'number':
      return FormFieldType.number;
    
    default:
      throw ArgumentError('Unknown FormFieldType: $type');
  }
}

String typeToJson(FormFieldType type) {
  return type.toString().split('.').last;
}

Then, rerun the build_runner.

Sign up to request clarification or add additional context in comments.

7 Comments

I tried changing it to isRequired and still same behavior.
@Ouroboros, have you ran the build-runner again to regenerate the code?
Yes. I also added complete code for your reference, with the changes you suggested. Can you please check and let me know
@Ouroboros ok look at updates
It does not work. Request you to make the changes - and share code which works - cause the code you're shared does not work.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.