Flutter how to search an object in complex json - json

My application is a simple calculator but I am a little lost in flutter...
I have a text field entered by the user which is a distance.
Depending on this distance, I need to look in my local values ​​of the application stored in a json for the closest value and retrieve the index.
Here is my json:
{
"charges" : [
{
"type" : 1,
"distance": 800,
"setting": 1122,
"bond" : 44,
"index" : 1
},
{
"type" : 1,
"distance": 850,
"setting": 1076,
"bond" : 50,
"index" : 2
},
{
"type" : 2,
"distance": 800,
"setting": 1336,
"bond" : 37,
"index" : 3
},
{
"type" : 2,
"distance": 900,
"setting": 1299,
"bond" : 39,
"index" : 4
}
]
}
If the user enters 836m for example, I have to compare the different ammunition (type 1 and 2) and look at the distance closest to the bottom.
Here the closest distance to the bottom is 800 for ammunition 1 and 2.
{
"type" : 1,
"distance": 800,
"setting": 1122,
"bond" : 44,
"index" : 1
},
{
"type" : 2,
"distance": 800,
"setting": 1336,
"bond" : 37,
"index" : 3
}
But I have to find only one charge at the end which corresponds to my need.
For that I have to look at the setting which must be closest to 1100 at the superior.
The setting of the charge 1 is the closest so I need to keep the index 1.
How to do that ? Is it the good method to use json file ? The structure of my json file is good fot do that ?
This is my Homepage.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
import 'dart:async';
class HomeController extends StatefulWidget {
HomeController({Key key, this.title}) : super(key: key);
final String title;
#override
_HomeControllerState createState() => _HomeControllerState();
}
class _HomeControllerState extends State<HomeController> {
String _distance;
final _formKey = GlobalKey<FormState>();
// Widget ************************************
Widget _buildDistance() {
return TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Distance',
hintText: "Enter a distance",
),
keyboardType: TextInputType.number,
validator: (String value){
int distance = int.tryParse(value);
if(distance == null){
return "Distance is needed";
}
if(distance <= 0){
return "Distance must be greater than zero";
}
},
onSaved: (String value){
_distance = value;
},
);
}
// End Widget ************************************
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (() => FocusScope.of(context).requestFocus(FocusNode())),
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future : DefaultAssetBundle.of(context).loadString('assets/jsons/charge.json'),
builder : (context, snapshot){
var myjson = json.decode(snapshot.data.toString());
return Container(
margin: EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_buildDistance(),
SizedBox(height: 100),
RaisedButton(
child: Text(
'Calculer',
style: TextStyle(color:Colors.blue, fontSize : 16),
),
onPressed: (){
if(!_formKey.currentState.validate()){
return;
}
_formKey.currentState.save();
print(_distance);
Map<String, dynamic> data = {
"distance" : _distance,
};
print('result : ${data['distance']}');
print(myjson);
},
),
],
),
),
);
},
),
),
);
}
}

You need to parse the JSON. I would personally parse it whenever the application starts and keep it in memory (unless this JSON is really long!!). For instance, you can store the information in an array in which each element is an object of class Charge that is a simple POJO class that stores the information needed for a charge (type, distance...).
Then when you're searching for the selected Charge, you can simply traverse the array and keep track of which charge has the closest distance to the user-specified
var minDistance = double.maxFinite;
var correctCharge = null;
for(var charge in charges){
double curDistance = abs(charge.distance - userSpecifiedDIstance);
if(curDistance < minDistance){
minDistance = curDistance;
correctCharge = charge;
}
}
If you want to optimise this so you don't traverse the whole array every time, you can use search methods like binary search (if you don't have a lot of charges, you don't need to optimize.)

Related

Can I display datewise JSON data (ListView) in flutter?

I want to display JSON data on the date posted, there is a key value of date in the JSON string, Right Now I am printing the entire posted entries and it is really ugly?
I am currently using a flutter calendar weekly view which also disappears off the screen upon the day clicked. (runtime)
This is my Code where I am calling data
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:flutter_calendar_week/flutter_calendar_week.dart';
class WeeklyData extends StatefulWidget {
_WeeklyDataState createState() => _WeeklyDataState();
}
var dt;
List data = [];
class _WeeklyDataState extends State<WeeklyData> {
void fetchData() async {
final uri = Uri.parse('My URL');
final header = {'Content-Type': 'application/json'};
final response = await http.get(uri, headers: header);
if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
});
}
}
#override
void initState() {
super.initState();
fetchData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
SizedBox(
height: 20,
),
CalendarWeek(
controller: CalendarWeekController(),
showMonth: true,
height: 100,
minDate: DateTime.now().add(Duration(days: -365)),
maxDate: DateTime.now().add(Duration(days: 365)),
onDatePressed: (DateTime datetime) {
dt = datetime;
},
),
SizedBox(
height: 20,
),
Text("$data"),
],
),
),
);
}
}
The data gets printed fine on the screen but it's too much I want to filter only the entry according to the day clicked on the calendar
[
{
"url": "http://157.90.55.200:8040/lover/1/",
"uuid": "bf47ddad-9362-4a12-af74-f8ad62883c60",
"raddress": "",
"pubkey": "",
"active": true,
"created": "2021-08-15T17:29:42.797000Z",
"updated": "2021-08-15T17:29:42.798000Z",
"day": 0,
"dslp": 0,
"dnpe": 0,
"cm": "string",
"love": 0,
"pain": "string",
"mood": "string",
"diet": "string",
"eln": "string",
"sreq": "string"
},
{
"url": "http://my url/lover/2/",
"uuid": "25254a18-e9c2-4489-b2f6-ff5174d07ee3",
"raddress": null,
"pubkey": null,
........
check and used grouplist package
GroupedListView<ModelClass, String>(
//physics: ScrollPhysics(),
shrinkWrap: true,
elements: listData,
groupBy: (ModelClass e) => '${e.created.subString(0,9)}',
groupSeparatorBuilder: (String groupByValue) => groupByValue!="null"? Container(
padding: EdgeInsets.only(left:30),
width: double.infinity,
color: Colors.grey.withOpacity(0.5),
child: Text(groupByValue)):Container(),
itemBuilder: (context, element) {
return Center(child: Text(element?.mode ?? ""));
},
order: GroupedListOrder.DESC,
)

JSON.DECODE returning null value, Flutter

I am trying to create a quizapp in which i create cards, like when user click on specific card then it will be redirect to that quizpage.
here is the where i am trying to get json file data which are quiz questions
class getjson extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future:DefaultAssetBundle.of(context).loadString("assets/questions/generalQ.json"),
builder: (context, snapshot) {
List mydata = json.decode(snapshot.data.toString());
print(mydata);
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return quizpage();
}
},
);
}
}
class quizpage extends StatefulWidget {
#override
_quizpageState createState() => _quizpageState();
}
class _quizpageState extends State<quizpage> {
#override
Widget build(BuildContext context) {
return Container(
);
}
}
and here i created cards widgets
Widget customcard(String langname, String image, String des){
return Padding(
padding: EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 30.0,
),
child: InkWell(
onTap: (){
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=>getjson(),));
},
i am calling getjson class on the onTap.
when i run the app click on card it shows me "loading", it is returning null on "mydata" of getjson class.
how to fix it? why it's returning null on mydata? Please help!
JSON FILE:
[
{
"1": "What is the world's most populated country?",
"2": "Each year World Red Cross and Red Crescent Day is celebrated on",
"3": "The ozone layer restricts"
},
{
"1": {
"a": "Russia",
"b": "China",
"c": "USA"
},
"2": {
"a": "May 8",
"b": "May 18",
"c": "April 28"
},
"3": {
"a": "Visible light",
"b": "Ultraviolet radiation",
"c": "Infrared radiation"
}
}
]
Actually it's pretty simple, you say that your list = a Future . When you do json.decode(snapshot.data) the Future of your futureBuilder is not yet finished, so mydata at that time is snapshot.data which is null. You should write:
if(snapshot.ConnectionState == ConnectionState.done){
List mydata = json.decode(snapshot.data.toString());
return quizpage();
}else{
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
}

How do I search through data in flutter app

I am new to flutter and I was following a tutorial that shows how to search through data. I cannot recreate that using my own example. I would like to know how to search through data in a ListView from this json data.
{
"success": "true",
"data": [
{
"id": 1,
"name": "football"
},
{
"id": 2,
"name": "rugby"
},
{
"id": 3,
"name": "basketball"
},
{
"id": 4,
"name": "hockey"
},
{
"id": 5,
"name": "tennis"
},
{
"id": 6,
"name": "golf"
}
]
}
Displayed using this code
if (snapshot.hasData) {
return ListView.builder(
itemCount: sports.games.length,
itemBuilder: (context, index) {
if (sports.games.length > 0) {
final x = sports.games[index];
final y = sports.games[index].id.toString();
return ListTile(
title: Text(y),
subtitle: Text(x.name),
);
}
},
);
}
},
First your JSON file will return as a Map, according to this answer at
Unhandled Exception: InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>
for your problem, here is the solution. First you need create a model like this, can be place at separate file or can be place at same file too
class Sports {
int id;
String name;
Sports({
this.id,
this.name,
});
factory Sports.fromJson(Map<String, dynamic> json) {
return Sports(id: json['id'], name: json['name']);
}
}
than here it's the main.dart
import 'package:aberdeen/model.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyApp> {
TextEditingController controller = new TextEditingController();
List<Sports> array = [];
List<Sports> _filtered = [];
List<Sports> _null_filtered = [];
String jsonTest =
'{"success": "true","data": [{"id": 1,"name": "football"},{"id": 2,"name": "rugby"},{"id": 3,"name": "basketball"},{"id": 4,"name": "hockey"},{"id": 5,"name": "tennis"},{"id": 6,"name": "golf"}]}';
void initState() {
super.initState();
test();
}
void _alterfilter(String query) {
List<Sports> dummySearchList = [];
dummySearchList.addAll(_filtered);
if (query.isNotEmpty) {
List<Sports> dummyListData = [];
dummySearchList.forEach((item) {
if (item.name.contains(query)) { //if you want to search it order by id you can change item.name.contains to item.id.contains
dummyListData.add(item);
}
});
setState(() {
_filtered.clear();
_filtered.addAll(dummyListData); //dummyListData will place all the data that match at your search bar
});
return;
} else {
setState(() {
_filtered.clear();
_filtered.addAll(_null_filtered); //_null_filtered will place all the data if search bar was empty after enter a words
});
}
}
Future<Sports> test() async {
Map<String, dynamic> tagObjsJson = json.decode(jsonTest);
List<dynamic> data = tagObjsJson["data"];
setState(() {
for (Map Data in data) {
array.add(Sports.fromJson(Data));
}
_filtered.addAll(array);
_null_filtered.addAll(array);
for (int a = 0; a < _filtered.length; a++) {
print(_filtered[a].name);
}
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
alignment: Alignment.center,
child: Container(
margin: const EdgeInsets.only(top: 50),
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(
width: 1,
color: Color.fromRGBO(11, 189, 180, 1),
style: BorderStyle.solid)),
child: TextField(
decoration: InputDecoration(
hintText: 'Search your data',
contentPadding: EdgeInsets.all(15),
border: InputBorder.none),
controller: controller,
onChanged: (value) {
_alterfilter(value);
},
),
),
),
],
),
Expanded(
child: Container(
margin: const EdgeInsets.all(20),
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _filtered.length,
itemBuilder: (context, index) {
if (array.length > 0) {
final x = _filtered[index];
final y = _filtered[index].id.toString();
return ListTile(
title: Text(y),
subtitle: Text(x.name),
);
}
},
),
))
],
),
),
));
}
}
Sorry mate if my english was very bad but tell me if you got confused

In flutter, how would I save the TextBox input from generated textboxes?

Below is my full code, the only thing you need to do to run it is adding "http: any" to dependencies in pubspec.yaml.
What the code does is grab JSON input from source, and for each entry in the json feed create a card. Now for each question, I want the user to provide an answer, so when I add the button to the bottom, it brings up a popup box showing the answers (which I'll later post back to my server).
It would look something like:
Q1: "three"
Q2: "thirty"
Q3: "Some movie character"
Q4: "Walter White"
(I'll do the validation to my answers later)
My json is posted below the code.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<Question>> fetchQuestions(http.Client client) async {
final response =
await client.get('https://jsonblob.com/api/jsonBlob/a5885973-7f02-11ea-b97d-097037d3b153');
// Use the compute function to run parsePhotos in a separate isolate
return compute(parseQuestion, response.body);
}
// A function that will convert a response body into a List<Photo>
List<Question> parseQuestion(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Question>((json) => Question.fromJson(json)).toList();
}
class Question {
final int id;
final String question;
final String answer;
Question({this.id, this.question, this.answer});
factory Question.fromJson(Map<String, dynamic> json) {
return Question(
id: json['id'] as int,
question: json['question'] as String,
answer: json['answer'] as String
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Isolate Demo';
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Padding(
child: FutureBuilder<List<Question>>(
future: fetchQuestions(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? QuestionList(questions: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
padding: EdgeInsets.fromLTRB(1.0, 10.0, 1.0, 10.0),
),
);
}
}
class QuestionList extends StatelessWidget {
final List<Question> questions;
QuestionList({Key key, this.questions}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: questions.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
Container(
constraints: BoxConstraints.expand(
height: Theme.of(context).textTheme.display1.fontSize * 1.1 +
200.0,
),
color: Colors.blueGrey,
alignment: Alignment.center,
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text(questions[index].id.toString()),
subtitle: Text(questions[index].question),
),
new TextFormField(
decoration: new InputDecoration(
labelText: "Answer Question",
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(
),
),
),
validator: (val) {
if(val.length==0) {
return "Type your answer here";
}else{
return null;
}
},
keyboardType: TextInputType.text,
style: new TextStyle(
fontFamily: "Poppins",
),
),
/*
// make buttons use the appropriate styles for cards
ButtonBar(
children: <Widget>[
RaisedButton(
color: Colors.lightBlue,
hoverColor: Colors.blue,
child: const Text('Open'),
onPressed: () {/* ... */},
),
],
),
*/
],
),
)),
],
);
},
);
}
}
[
{
"id" : 1,
"question" : "what is one plus two",
"answer": "three"
},
{
"id" : 2,
"question" : "what is five times 6",
"answer": "thirty"
},
{
"id" : 3,
"question" : "Who said show me the money",
"answer": "Cuba Gooding Jnr"
},
{
"id" : 4,
"question" : "who said I am the danger",
"answer": "Walter White"
}
]
Consider attaching a TextEditingController to the TextFormField through the controller property. You can access the text currently in the TextField using the controller's text field.

How to test if json property is present during the build of a Flutter ListView

I'm beginner in Flutter and Dart.
I have a local JSON file, read it and I want to display the JSON Data in the ListView.
But in my JSON data I haven't always all the different properties.
So when I want to display a text, the value of the property is no existing because the property is not existing (In the case is the Property "description".
How could I solve it ?
Thank you in advance for your help
I tried ternary operator
I tried with the function containsKey
But maybe I did it wong ?
... json
[
{
"createdBy": "bddae0de-320c-41a9-a69b-75793758b7a7",
"description": "Fhjifgsdsdsd",
"endDateTime": "1477490400000",
"hasPicture": "false",
"isActive": "true",
"isAdminActive": "true",
"maxParticipants": "50",
"startDateTime": "1476799200000",
"title": "Song Church Story Telling",
"type": "Music"
},
{
"createdBy": "-KHzgxS6KBqu1rNmJzpT",
"endDateTime": "1476378000000",
"hasPicture": "false",
"isActive": "true",
"isAdminActive": "true",
"startDateTime":"1476205200000",
"title": "Test greg T",
"titleDate": "Tuesday, 11 Oct 8:00 PM",
"type": "Games"
}
]
...
... flutter
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Load local JSON file"),
),
body: new Container(
child: new Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: new FutureBuilder(
future: DefaultAssetBundle.of(context)
.loadString('assets/data/ckevents_data.json'),
builder: (context, snapshot) {
// Decode the JSON
var newData = json.decode(snapshot.data.toString());
return new ListView.builder(
// Build the ListView
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Text("Title: " + newData[index]['title'],
style: new TextStyle(
fontSize: 20.0, color: Colors.blue)),
new Text(
"Description: " +
((newData[index].containsKey('description')) ? ('') : (newData[index]['description'])),
style: new TextStyle(
fontSize: 10.0, color: Colors.pink)),
new Text("Categorie: " + newData[index]['type'],
style: new TextStyle(
fontSize: 15.0, color: Colors.red)),
new Text(
"Date: " +
DateTime.fromMillisecondsSinceEpoch(
newData[index]['startDateTime'])
.add(Duration(days: 700))
.toString(),
style: new TextStyle(
fontSize: 10.0, color: Colors.black))
],
),
);
},
itemCount: newData == null ? 0 : newData.length,
);
}),
),
));
}
}
...
You can use the null coalesce operator like so:
new Text("Description: " + newData[index]['description'] ?? ''),
This will print the description if it exists, and an empty string if it doesn't.
Try this way
1. make model class for your response json like this way..
class UserData {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
UserData({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory UserData.fromJson(Map<String, dynamic> json) {
return new UserData(
albumId: json['albumId'],
id: json['id'],
title: json['title'],
url: json['url'],
thumbnailUrl: json['thumbnailUrl']);
}
}
now make list object ..
List<UserData> list = List();
then add data..
list = (json.decode(response.body) as List)
.map((data) => UserData.fromJson(data))
.toList();
read json file like this way..
class _ApiCallScreenState extends State<ApiCallScreen> {
#override
void initState() {
String data = await DefaultAssetBundle.of(context).loadString("assets/data.json");
list = (json.decode(data) as List)
.map((data) => UserData.fromJson(data))
.toList();
super.initState();
}