Flutter

Rendering Forms from JSON in Flutter

By 3 November 2020 January 7th, 2021 3 Comments

Rendering Forms from JSON in Flutter

Flutter 1.17 Features and Dart 2.8 Updates

Sharing is caring!

Form is so far the commonly used UI component used in a mobile application. Last week, we came across the requirement where we had to render different forms in our Flutter app based on JSON data fetched from the server. Today, I’m going to share my experience with the same.

Prepare JSON file

We will keep all our JSON data fetched from server in the .json file inside assets folder like:
So we request API for the JSON data, encode the response as a String, then write the output string to our json file. Making API requests and writing output to the json file is beyond the scope of this article. The main point to note here are:
  1. .json file which will hold the structure of our form to be rendered.
  2. JSON data needs to be in specific format with specific keys.

The Package: json_to_form

We will make use of json_to_form package for rendering form. It can be installed using pubspec.yaml file and ‘pub get’ command, but I don’t recommend this way for this package because I found some areas of improvement in it to make our JSON data more simple. The package is no more than a single file – json_schema.dart, which can be dropped in our project directly and extend it as per our requirement. Thats cool!
As mentioned earlier, JSON data must be in specific format for the package to accept it. I’m writing that data directly into our .json file for this article. Open up the sample_form.json file inside assets and write down the following lines into it.
[
  {
    "title": "Personal Information",
    "fields": [
      {
        "type":"Input",
        "key":"name",
        "placeholder":"Name",
        "value":"",
        "required":true
      },
      {
        "type":"Phone",
        "key":"phone",
        "placeholder":"Phone Number",
        "value":"",
        "required":true
      },
      {
        "type":"Email",
        "key":"email",
        "placeholder":"Email",
        "value":"",
        "required":true
      },
      {
        "type":"RadioButton",
        "key":"gender",
        "label":"Gender",
        "value": "Male",
        "items": ["Male", "Female"]
      },
      {
        "type":"Select",
        "key":"address_type",
        "label":"Address Type",
        "value": "Permanent",
        "items": ["Permanent", "Business", "Communication"]
      },
      {
        "type": "Switch",
        "key":"is_admin",
        "label": "Is Admin",
        "switchValue": false
      }
    ]
  }
]
Here we mentioned 6 types of fields: Input, Phone, Email, RadioButton, Select, and Switch. Well, we can have more according to what can be accept by json_schema.dart file. So if you need your own control to be render, first code it in json_schema.dart file, then specify the data accordingly into json file(or on server side if you’re fetching JSON from API). IMPORTANT: I’ve modified json_schema.dart file in order for JsonSchema widget to accept above json data and render a form accordingly. Just download the modified file below and replace the previously added one with this file.

JsonSchema in Action

So we have JsonSchema widget defined in json_schema.dart file, which is ready to go.  Read content from json file
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
...
String formString;
...
void loadJson() {
    rootBundle.loadString('assets/json/submission_form.json').then((jsonString){
      List<dynamic> jsonObj = json.decode(jsonString) ?? '';
      List<Map<String, dynamic>> rawJson = listOfDynamicToMap(jsonObj) ?? [];
      
    });
  }

  List<Map<String, dynamic>> listOfDynamicToMap(List<dynamic> list) {
    List<Map<String,dynamic>> listOfMap = [];
    list.forEach((element) {
      if(element is Map<String, dynamic>) {
        listOfMap.add(element);
      }
    });
    return listOfMap;
  }
  • Prepare the json string for JSonSchema. Just add setState() inside loadJson() as:
void loadJson() {
    rootBundle.loadString('assets/json/submission_form.json').then((jsonString){
      List<dynamic> jsonObj = json.decode(jsonString) ?? '';
      List<Map<String, dynamic>> rawJson = listOfDynamicToMap(jsonObj) ?? [];
      setState(() => formString = json.encode(rawJson.first));
    });
  }
  • Render the form
import 'package:flutterassignment/external/json_schema.dart';
...
...
JsonSchema(
        key: new GlobalKey<CoreFormState>(),
        form: formString,
        onChanged: (dynamic data){
          //Some values has been changed withing form
        },
        actionSave: (data){
          //Submit button is clicked, do something with the data
        },
        buttonSave:Container(
            height:40,
            alignment: Alignment.center,
            color: Theme.of(context).primaryColor,
            padding:EdgeInsets.symmetric(horizontal: 20.0, vertical: 8.0),
            child: Text( 'Submit', style: TextStyle(fontSize: 16.0, color: Colors.white))
        ),
    );

The complete code:

//main.dart
import 'package:flutterassignment/ui/form_render.dart';
void main() {
  runApp(FormRender());
}
//form_render.dart
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/material.dart';
import 'package:flutterassignment/external/json_schema.dart';

class FormRender extends StatefulWidget {
  @override
  FormRenderState createState() => FormRenderState();
}

class FormRenderState extends State<FormRender> {

  String formString;

  void loadJson() {
    rootBundle.loadString('assets/json/submission_form.json').then((jsonString){
      List<dynamic> jsonObj = json.decode(jsonString) ?? '';
      List<Map<String, dynamic>> rawJson = listOfDynamicToMap(jsonObj) ?? [];
      setState(() => formString = json.encode(rawJson.first));
    });
  }

  List<Map<String, dynamic>> listOfDynamicToMap(List<dynamic> list) {
    List<Map<String,dynamic>> listOfMap = [];
    list.forEach((element) {
      if(element is Map<String, dynamic>) {
        listOfMap.add(element);
      }
    });
    return listOfMap;
  }

  @override
  void initState() {
    super.initState();
    loadJson();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      appBar: AppBar(
        title: Text('Json To Form'),
        centerTitle: true,
      ),
      body: body(),
    );
  }

  Widget body() {

    if(formString == null) {
      return Center(child: Text('No Contents!'));
    }

    return JsonSchema(
        key: new GlobalKey<CoreFormState>(),
        form: formString,
        onChanged: (dynamic data){
          //Some values has been changed within form
        },
        actionSave: (data){
          //Submit button is clicked, do something with the data
        },
        buttonSave:Container(
            height:40,
            alignment: Alignment.center,
            color: Theme.of(context).primaryColor,
            padding:EdgeInsets.symmetric(horizontal: 20.0, vertical: 8.0),
            child: Text( 'Submit', style: TextStyle(fontSize: 16.0, color: Colors.white))
        ),
    );
  }

}

 Output: 

Thanks for reading. [/vc_column_text][/vc_column][/vc_row]

3 Comments

  • ramesh says:

    i am getting error at JsonSchema and CoreFormState.can you please tell me what the reason.

  • Tali says:

    The file download for the json_schema.dart doesn’t go anywhere, is there somewhere else where that can be accessed?

    • admin says:

      It is not a download, the intention of the statement is that you need to create a file named json_schema.dart in the specified folder structure.

Get started with Bloom