Flutter - dynamic forms - json

I would like to create a dynamic form with TextEditingController built dynamically with information from a json file.
For example, in my json file i have 2 fields
[
{
focusNode: nftNameFocusNode,
controller: nftNameController,
textInputAction: TextInputAction.next,
labelText: 'name',
autocorrect: false,
keyboardType: TextInputType.text
},
{
focusNode: nftDescFocusNode,
controller: nftDescController,
textInputAction: TextInputAction.next,
labelText: 'description',
autocorrect: false,
keyboardType: TextInputType.text
}
]
and i would like to generate 2 fields in my form
Thx

Related

how to use Json list in dropdown button in flutter

I have this code which works and print the list but when i use it in a dropdown button, it throws the null red screen error while running the app.
Future<void> readJson() async {
final response = await rootBundle.loadString('assets/json/units.json');
final data = await json.decode(response);
setState(() {
List jsonList = data["length"] as List;
print(jsonList);
});
}
here is how the Json file structure looks like
{
"length" : [
{
"name": "Meter",
"conversion": 1.0,
"base_unit": true
},
{
"name": "Millimeter",
"conversion": 1000.0
},
{
"name": "Centimeter",
"conversion": 100.0
}
]
}
and here is how the dropdown button looks like,
Widget customJsonDropDown(String value, void onChange(val)) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(9),
color: Colors.white,
),
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 5),
child: SizedBox(
//width: 80,
height: 50,
child: DropdownButton<String>(
value: value,
onChanged: (val) {
onChange(val);
},
items: jsonList?.map((item) {
return DropdownMenuItem(
child: Text(item['name']),
value: item['conversion'].toString(),
);
}).toList() ??
[],
underline: Container(),
isExpanded: true,
))));
}
any help is highly appreciated.
What are you sending in as the value parameter to customJsonDropDown?
The error message is saying either none of the value properties of any of the items you have passed to the DropdownButton match the value set the DropdownButton itself, or that more than one do. Can you confirm what item['conversion'].toString() evaluates to, and if exactly one of those values matches the DropdownButton's value?
EDIT:
Let me try to clarify a bit:
Widget customJsonDropDown(String value, void onChange(val)) {
return ...
child: DropdownButton<String>(
value: value, // <-- this needs exactly one corresponding DropdownMenuItem, or be null
onChanged: (val) {
onChange(val); // <-- this needs to update the value above to a value that also matches exactly one of the items you set below, or null
},
items: jsonList?.map((item) {
return DropdownMenuItem(
child: Text(item['name']),
value: item['conversion'].toString(), // <-- exactly one of these has to match the DropdownButton's value above, if not null
);
}).toList() ??
[],
// ...

Flutter: How to stop state from changing UI

I have some local JSON data that I've parsed into my app. The data contains a few arrays and math questions (building a quiz app), so based on the math there are chapters which I've displayed in a ListView builder, when clicking on one of those it opens a new screen and passes on specific data for that chapter based on the index of the chapter.
In my quiz screen math questions in an array are brought from the JSON data depending on what chapter it is, I have the questions shuffled so that they appear in a random order, issue is when a user tries to answer a question in a provided TextField the keyboard opens as intended but the state of the UI changes which also changes the currently displayed question. How can I stop this?
To see the problem I have in the app here's a video link: https://youtu.be/n_VeT26I8NE
The JSON data has been parsed from a different screen and set in these variables:
List quiz = widget.newData["quiz"];
//Shuffle questions
var randomQuiz = (quiz..shuffle()).first;
If you prefer to read the Dart code from GitHub: https://github.com/BotsheloRamela/classio/blob/main/quiz_screen.dart
Some of the JSON data:
[
{
"chapter": "Exponents & Surds",
"introduction": {
"description": "A number's exponent determines how many times to multiply it.
Exponents can also be called powers or indices. In mathematics, a surd is a value
that cannot be further simplified into a whole number or integer. They are
irrational numbers.",
"videoID": ["568dGLFTom8", "XZRQhkii0h0"]
},
"quiz": [
{
"question": "Simplify the follwoing.",
"sum": "2a^2.3a^2.b^0",
"answer": "6a^5",
"explaination": ""
},
{
"question": "Rewrite the following with prime bases, your answer should be a fraction.",
"sum": "16.8^{-4}",
"answer": "1/256",
"explaination": ""
},
{
"question": "Solve the following, your answer should be a fraction.",
"sum": "\\frac{(-3a^3 b)^2}{a^5 b^3}",
"answer": "9a/b",
"explaination": ""
},
{
"question": "Rewrite the following with prime bases and simplify, your answer should be a fraction.",
"sum": "(\\frac{2x^3}{8y^{-4}})^{-3}",
"answer": "\\frac{64}{x^9 y^12}",
"explaination": ""
}
]
},
{
"chapter": "Equations & Inequalities",
"introduction": {
"description": "Equations and inequalities are both mathematical sentences made up of two expressions related to one another. The symbol = indicates that two expressions are regarded as equal within an equation. While in an inequality, two terms are not necessarily equal, which is indicated by the following symbols: >, <, ≤ or ≥.",
"videoID": ["hJ-_OoCHTks"]
},
"quiz": [
{
"question": "Show that the following trinomial is a perfect square.",
"sum": "x^2+2x+1",
"answer": "(x+1)^2",
"explaination": ""
},
{
"question": "Show that the following trinomial is a perfect square.",
"sum": "x^2-6x+9",
"answer": "(x-3)^2",
"explaination": ""
},
{
"question": "Show that the following trinomial is a perfect square.",
"sum": "x^2+8x+16",
"answer": "(x+4)^2",
"explaination": ""
}
]
},
]
Dart code:
import 'package:classio_students/bottom_nav.dart';
import 'package:classio_students/colors.dart';
import 'package:classio_students/screens/learn/maths_formula_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_math_fork/flutter_math.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:page_transition/page_transition.dart';
// ignore: must_be_immutable
class Gr11MathQuiz extends StatefulWidget {
var newData;
Gr11MathQuiz({Key? key, required this.newData}) : super(key: key);
#override
_Gr11MathQuizState createState() => _Gr11MathQuizState();
}
class _Gr11MathQuizState extends State<Gr11MathQuiz> {
final TextEditingController _answerController = TextEditingController();
#override
void initState() {
super.initState();
//SystemChannels.textInput.invokeMethod('TextInput.show');
}
#override
Widget build(BuildContext context) {
//Creates a list from the JSON data & shuffles the data quiz in a random order everytime the state changes
List quiz = widget.newData["quiz"];
var randomQuiz = (quiz..shuffle()).first;
//Check answer function
checkAnswer() async {
if (_answerController.text.toString() !=
randomQuiz['answer'].toString()) {
Fluttertoast.showToast(
msg: 'Are you sure? Take a look at your answer.',
gravity: ToastGravity.TOP,
backgroundColor: Colors.orangeAccent,
textColor: Colors.white);
} else {
Fluttertoast.showToast(
msg: 'Nice!',
gravity: ToastGravity.TOP,
backgroundColor: Colors.green,
textColor: Colors.white);
Future.delayed(const Duration(seconds: 1), () {
setState(() {});
_answerController.clear();
});
}
}
final height = MediaQuery.of(context).size.height;
const shape = StadiumBorder();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('Gr11 Maths Quiz',
style: TextStyle(
color: titleColor,
letterSpacing: 1,
fontSize: 20,
fontWeight: FontWeight.bold)),
leading: IconButton(
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.arrow_back,
color: Colors.black,
size: 30,
)),
actions: [
IconButton(
onPressed: () => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const CustomNavBar())),
icon: const Icon(
Icons.home,
color: Colors.black,
size: 30,
))
],
backgroundColor: Colors.white,
centerTitle: true,
elevation: 0,
),
body: SizedBox(
height: height,
child: Flexible(
child: Padding(
padding: const EdgeInsets.all(13),
child: Column(
children: [
Text(
randomQuiz['question'].toString(),
style: const TextStyle(color: Colors.black, fontSize: 17),
),
const SizedBox(
height: 30,
),
//Sum
Center(
child: Math.tex(
randomQuiz['sum'].toString(),
mathStyle: MathStyle.display,
textStyle: const TextStyle(fontSize: 25),
),
),
const SizedBox(
height: 60,
),
//Answer text
const Align(
alignment: Alignment.centerLeft,
child: Text(
'Answer:',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 20),
),
),
const SizedBox(
height: 10,
),
//Answer textfield & check btn
Row(
children: [
//Textfield
Expanded(
child: TextField(
controller: _answerController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: 'Answer',
hintStyle: const TextStyle(color: Colors.grey),
labelStyle: const TextStyle(color: Colors.grey),
enabledBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey),
borderRadius: BorderRadius.circular(13)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(13),
),
focusedBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: submitBtn),
borderRadius: BorderRadius.circular(13))),
),
),
const SizedBox(
width: 15,
),
//Button
Container(
decoration: BoxDecoration(
color: titleColor,
borderRadius: BorderRadius.circular(10)),
child: ElevatedButton(
onPressed: () {
checkAnswer();
},
child: const Text(
"Check",
style: TextStyle(fontSize: 16),
),
style: ElevatedButton.styleFrom(
primary: Colors.transparent,
shadowColor: Colors.transparent,
padding: const EdgeInsets.symmetric(
vertical: 19, horizontal: 20),
//minimumSize: const Size(100.0, 5.0),
textStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 23)),
),
),
],
),
const SizedBox(
height: 20,
),
//View Math symbols dialog
Align(
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade400,
borderRadius: BorderRadius.circular(10)),
child: ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return const MathFormulaDialog();
});
},
child: const Text(
"How should I type my answer?",
style: TextStyle(fontSize: 16, color: Colors.black),
),
style: ElevatedButton.styleFrom(
primary: Colors.transparent,
shadowColor: Colors.transparent,
padding: const EdgeInsets.symmetric(
vertical: 19, horizontal: 20),
//minimumSize: const Size(100.0, 5.0),
textStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 23)),
),
),
),
],
),
),
),
));
}
}
try not to use code inside build method unless you want it to be refreshed anytime any little change occurred in widget tree.
basically the problem is here in the very beginning lines of build method:
...
#override
Widget build(BuildContext context) {
List quiz = widget.newData["quiz"];
var randomQuiz = (quiz..shuffle()).first;
...
you must use it outside of build and only use it whenever an action is triggered for example if user finished answering current question then refresh ui and show another random question.
Thanks to #Namini40, I moved the variables to initState() from the build function, the checkAnswer() function remained in the build method and I changed a couple things. If a user gets the answer right rather than using setState() I instead used Navigator.pushReplacement() to navigate to the very same page but will display a different question.
New code: https://github.com/BotsheloRamela/classio/blob/main/quiz_screen.dart

How to call POST function in Flutter

I'm making a POST request using HTTPClient and posting a form's data. The request is successful when I tried with only 3 fields, but now I want to add all the fields so I added the complete JSON object model in the parameter and body of the POST function.
I've used Postman to get the entire JSON data and used quicktype.io tool to parse it into a model class which I've passed as parameters to the function and also to the requests body.
I have a save button in the UI which when clicked should post all the form data, I'm having trouble building that function saveContact() and getting the fields from the Model and initializing it to the form field controllers.
API_Manager class:
Future<AddContactModel> addContact(AddContactModel contact) async {
var client = http.Client();
String addContactUrl =
"https://example.com/ma/api/contacts/new";
String basicAuth = 'Basic examplebasicauthkey';
var response = await client.post(addContactUrl,
headers: <String, String>{
'authorization': basicAuth,
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
},
body: contact.toJson()); //from the Model class
// print(response.statusCode);
developer.log(response.body);
if (response.statusCode == 201) {
final String responseString = response.body;
return addContactModelFromJson(responseString);
} else {
return null;
}
}
SAVE FUNCTION to call POST request's function:
Future saveContact() async { //Need to call function here, get Model fields
//and initialize it to the controllers.
//await API_Manager().addContact(contact);
//Something like this but sure of the full code
}
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Scaffold(
appBar: AppBar(
title: Text('Add Contact'),
actions: <Widget>[
FlatButton(
textColor: Colors.white,
onPressed: () async {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState.validate()) {
await saveContact();
}
},
child: Text(
'SAVE',
style: TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
shape:
CircleBorder(side: BorderSide(color: Colors.transparent)),
)
],
),
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.all(5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_contact == null
? Container()
:
//Text("The user ${_contact.contact.fields.all.firstname} is created successfully at time ${_contact.contact.lastActive.toIso8601String()}"),
TextFormField(
onSaved: null,
controller: _ipCountryCode,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'IP Country Code',
fillColor: Colors.white,
filled: true,
contentPadding: EdgeInsets.all(8)),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: DateTimeFormField(
decoration: InputDecoration(
labelText: 'Time First Seen',
fillColor: Colors.white,
filled: true,
contentPadding: EdgeInsets.all(8)),
onDateSelected: (DateTime value) {
setState(() {
timeFirstSeen = value;
});
},
),
),
],
),
TextFormField(
onSaved: null,
controller: _eventRevenue,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'Event Revenue',
fillColor: Colors.white,
filled: true,
contentPadding: EdgeInsets.all(8)),
),
//Not the full code
addContactModel class:
import 'dart:convert';
AddContactModel addContactModelFromJson(String str) => AddContactModel.fromJson(json.decode(str));
String addContactModelToJson(AddContactModel data) => json.encode(data.toJson());
class AddContactModel {
AddContactModel({
this.contact,
});
Contact contact;
factory AddContactModel.fromJson(Map<String, dynamic> json) => AddContactModel(
contact: Contact.fromJson(json["contact"]),
);
Map<String, dynamic> toJson() => {
"contact": contact.toJson(),
};
}
class Contact {
Contact({
this.isPublished,
this.dateAdded,
this.dateModified,
this.createdBy,
this.createdByUser,
this.modifiedBy,
this.modifiedByUser,
this.id,
this.points,
this.color,
this.fields,
this.lastActive,
this.owner,
this.ipAddresses,
this.tags,
this.utmtags,
this.stage,
this.dateIdentified,
this.preferredProfileImage,
this.doNotContact,
this.frequencyRules,
});
bool isPublished;
DateTime dateAdded;
dynamic dateModified;
int createdBy;
String createdByUser;
dynamic modifiedBy;
dynamic modifiedByUser;
int id;
int points;
dynamic color;
Fields fields;
dynamic lastActive;
dynamic owner;
List<dynamic> ipAddresses;
List<dynamic> tags;
dynamic utmtags;
dynamic stage;
dynamic dateIdentified;
dynamic preferredProfileImage;
List<dynamic> doNotContact;
List<dynamic> frequencyRules;
factory Contact.fromJson(Map<String, dynamic> json) => Contact(
isPublished: json["isPublished"],
dateAdded: DateTime.parse(json["dateAdded"]),
dateModified: json["dateModified"],
createdBy: json["createdBy"],
createdByUser: json["createdByUser"],
modifiedBy: json["modifiedBy"],
modifiedByUser: json["modifiedByUser"],
id: json["id"],
points: json["points"],
color: json["color"],
fields: Fields.fromJson(json["fields"]),
lastActive: json["lastActive"],
owner: json["owner"],
ipAddresses: List<dynamic>.from(json["ipAddresses"].map((x) => x)),
tags: List<dynamic>.from(json["tags"].map((x) => x)),
utmtags: json["utmtags"],
stage: json["stage"],
dateIdentified: json["dateIdentified"],
preferredProfileImage: json["preferredProfileImage"],
doNotContact: List<dynamic>.from(json["doNotContact"].map((x) => x)),
frequencyRules: List<dynamic>.from(json["frequencyRules"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"isPublished": isPublished,
"dateAdded": dateAdded.toIso8601String(),
"dateModified": dateModified,
"createdBy": createdBy,
"createdByUser": createdByUser,
"modifiedBy": modifiedBy,
"modifiedByUser": modifiedByUser,
"id": id,
"points": points,
"color": color,
"fields": fields.toJson(),
"lastActive": lastActive,
"owner": owner,
"ipAddresses": List<dynamic>.from(ipAddresses.map((x) => x)),
"tags": List<dynamic>.from(tags.map((x) => x)),
"utmtags": utmtags,
"stage": stage,
"dateIdentified": dateIdentified,
"preferredProfileImage": preferredProfileImage,
"doNotContact": List<dynamic>.from(doNotContact.map((x) => x)),
"frequencyRules": List<dynamic>.from(frequencyRules.map((x) => x)),
};
}
class Fields {
Fields({
this.core,
this.social,
this.personal,
this.professional,
this.all,
});
All core;
Social social;
List<dynamic> personal;
List<dynamic> professional;
All all;
//Not the full code
I have this post request you can change it to your desire
this is Post Function
Future<ResponseModel> HTTPPOST<T>(String url, List<QueryModel> query,
var body, HeaderEnum headerType, ResponseEnum responseType) async {
try {
var response = await http.post(
UrlGenerator(url, query),
headers: headerGetter(headerType),
body: body,
);
return responseGetter<T>(responseType, response);
} catch (e) {
return ResponseModel(
isSuccess: false,
statusCode: "500",
data: null,
message: "خطایی در عملیات رخ داده است");
}
}
this is the responseGetter function
responseGetter<T>(ResponseEnum typeEnum, http.Response response) {
try {
switch (typeEnum) {
case ResponseEnum.ResponseModelEnum:
String data = utf8.decode(response.bodyBytes);
if (data == null || data.isEmpty)
return ResponseModel(
statusCode: "555",
isSuccess: false,
data: null,
);
return ResponseModel().fromJson(
json.decode(data),
);
default:
return response.bodyBytes;
}
} catch (e) {
return ResponseModel(
isSuccess: false,
statusCode: "500",
data: null,
message: "خطایی در عملیات رخ داده است");
}
}
and this is using post function in an example
Future<ResponseModel<CartHeader>> AddProduct(int productId, int qty) async {
var map = {"productId": productId, "qty": qty};
var json = jsonEncode(map);
var response = await HTTPPOST(
RoutingCart.POST_AddProduct,
[],
json,
HeaderEnum.BearerHeaderEnum,
ResponseEnum.ResponseModelEnum,
);
return ResponseModel<CartHeader>(
isSuccess: response.isSuccess,
statusCode: response.statusCode,
data: response.data,
message: response.message,
); }

How to display selected month data from flutter dropdown selection using json

I have try to display Selected month data in dropdown selection in flutter , supposed I was select January then it display the January month record in json formate
String monthWiseGraph;
Future<String> getMonthlyGraphData() async {
String url = 'http://example.com/getAgentMonthGraph.php?month='+ monthWiseGraph ;
var response = await http.get(url);
return response.body;
}
Container(
height: 130,
width: 200,
padding: EdgeInsets.only(left: 30, top: 50),
child: Card(
// color: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
bottomLeft: Radius.circular(20)),
side: BorderSide(color: Colors.black)),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 10, right: 20, left: 20),
child: DropdownButtonHideUnderline(
child: DropdownButton(
hint: Text("Monthly",
style:
TextStyle(color: Colors.black, fontSize: 25)),
value: monthWiseGraph,
icon: Icon(Icons.arrow_drop_down),
iconSize: 30,
elevation: 16,
style: TextStyle(color: Colors.black),
onChanged: (String monthNewValue) {
setState(
() {
monthWiseGraph = monthNewValue;
print(monthWiseGraph);
_showMonthlyAlert();
},
);
},
items: <String>[
'January',
'February',
.
.
'December'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(
fontSize: 20,
),
),
);
}).toList(),
),
),
),
],
),
),
),
This is My January data
[
{
userId: "2",
name: "Manish",
total: "32"
},
{
userId: "3",
name: "Altaf",
total: "31"
}
]
This is My February data
[
{
userId: "4",
name: "Prathamesh",
total: "68"
},
{
userId: "7",
name: "Aniket",
total: "62"
},
]
Above image I display the selected month data When I select january from dropdown then it display january data from json like manish and altaf and when I select february it dipslay Prathamesh and Aniket data below is this is normal graph
I am new to flutter
This is my drop down
You have to put "jsonDecode(response.body)" either in a variable or just change the "return" keyword. And then you can access the JSON api because "JsonDecode" will change the JSON data into a Flutter language. Make sure to "import" the file needed.

Is it possible to use conditionals in JSON queries for flutter?

I have the Following json file and I have used it to generate a dropdown list. The goal is to store the price when a user searches for a given service. The list shows the fields which are then used for the search but the prices are the accumulated.
{
"services": [
{
"price": 3,
"service": "Caregiver"
},
{
"price": 5,
"service": "Driver"
},
{
"price": 0,
"service": "Tipper Driver"
}
],
"locations": [
{
"sub_county": "All of NY"
},
{
"sub_county": "Brkln"
}
]
}
Here is the drop down field code
DropdownButtonFormField<String>(
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 0),
filled: true,
fillColor: Theme.Colors.color_7,
hintText: 'Select a Profession',
hintStyle: TextStyle(fontWeight: FontWeight.bold)),
value: _listdownValue,
icon: Icon(Icons.arrow_drop_down),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.black),
onChanged: (String newValue) {
setState(() {
_listdownValue = newValue;
});
},
validator: (value) {
if (value == null) {
return "Select a Profession";
}
return null;
},
items: snapshot.data.services.map((item) {
return new DropdownMenuItem(
child: new Text(item.service),
value: item.service.toString(),
);
}).toList(),
),