Flutter

Rendering Forms from JSON in Flutter

By 3 November 2020 No Comments

Rendering Forms from JSON in Flutter

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 we 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 using 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 accepted by json_schema.dart file. So if you need your own control to be rendered, first code it in json_schema.dart file, then specify the data accordingly into the 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.

Leave a Reply

Get started with Bloom