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",
),
),
);
}
Related
What I'm trying to do is that if someone opens part 1 (JSON), then their respective article should be visible on the next screen. I was able to list out all the parts but how to show their respective articles on the next screen. For example, the first part contains 3 articles and the second part would contain 3
basically, every part would contain a different number of articles.
JSON
[
[ // Article Names
{
"ArtNo": "0",
"Name": "PREAMBLE",
},
{
"ArtNo": "1",
"Name": "Name and territory of the Union.",
},
{
"ArtNo": "2",
"Name": "Admission or establishment of new States.",
},
{
"ArtNo": "3",
"Name": "Formation of new States and alteration of areas, boundaries or names of existing States.",
},
{
"ArtNo": "4",
"Name": "Laws made under articles 2 and 3 to provide for the amendment of the First and the Fourth Schedules and supplemental, incidental and consequential matters.",
},
{
"ArtNo": "5",
"Name": "Citizenship at the commencement of the Constitution."
}
],
[ // Article Parts
{
"PartNo": "I",
"Name": "THE UNION AND ITS TERRITORY",
"Articles": ["1", "2", "3"]
},
{
"PartNo": "II",
"Name": "CITIZENSHIP",
"Articles": ["4", "5"]
}
]
]
Flutter (list out all parts)
class _ConstitutionPartsState extends State<ConstitutionParts> {
Future parts() async {
final data = await rootBundle.loadString('assets/json/file.json');
final jsonResult = jsonDecode(data);
final parts = jsonResult[1];
return parts;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: parts(), builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return const CircularProgressIndicator(color: Colors.deepOrangeAccent);
}
return Container(
margin: const EdgeInsets.all(25.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: const EdgeInsets.only(bottom: 20.0),
child: ListTile(
title: Text(snapshot.data[index]['Name']),
),
);
},
),
);
},
),
);
}
}
any help would be appreciated! thank you
You can use where condition for filteration,
more info
Here's the Full Code
class _ConstitutionPartsState extends State<ConstitutionParts> {
Future parts() async {
final data = await rootBundle.loadString('assets/json/file.json');
final jsonResult = jsonDecode(data);
return jsonResult; // return whole json
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: parts(), builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return const CircularProgressIndicator(color: Colors.deepOrangeAccent);
}
return Container(
margin: const EdgeInsets.all(25.0),
child: ListView.builder(
itemCount: snapshot.data[1].length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: const EdgeInsets.only(bottom: 20.0),
child: ListTile(
title: Text(snapshot.data[index]['Name']),
onTap():{
//navigate to name page with
NewPage(articePartIndex : index, json: snapshot.data)
}
),
);
},
),
);
},
),
);
}
}
NewPage {
final List<List<dynamic>> json;
final int index;
#override
Widget build(BuildContext context) {
var articles = json[1][index]['Articles'];
var filteredArticleList = json[0].where((a) => articles.contains(a['ArtNo'])).toList();
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(filteredArticleList[index]['Name']),
),
});
}
}
I am having trouble trying to iterate over a JSON array of objects from a remote URL using Flutter's FutureBuilder.
My goal is to:
Fetch JSON data from an API
Output the data into a 2 column gridview layout
The JSON data is an array of objects(or a List of Maps in dart), the objects have simple string data.
I know that I need to build a future to fetch the data from the API and decode the JSON, then I need to create a FutureBuilder to output the List data into my Gridview Builder. That is what I have tried to do in my code below.
import 'dart:convert';
import 'dart:async';
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class HomeSection extends StatefulWidget {
#override
_HomeSectionState createState() => _HomeSectionState();
}
class _HomeSectionState extends State<HomeSection> {
#override
void initState() {
super.initState();
}
Future<List<dynamic>> fetchSectionData() async {
String dataUrl =
'https://www.thisisthejsonapilink.co.uk/fetch-data';
var response = await http.get(Uri.parse(dataUrl));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to get the data');
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: fetchSectionData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return GridView.builder(
itemCount: 12,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300,
crossAxisSpacing: 16.0,
mainAxisSpacing: 18.0,
childAspectRatio: MediaQuery.of(context).size.height / 930,
),
itemBuilder: (BuildContext context, int index) => GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => OtherScreen()),
);
},
child: Column(
children: [
Container(
margin: const EdgeInsets.only(bottom: 12.0),
height: 144,
),
Text(
snapshot.data[index].title,
),
Text(
snapshot.data[index].duration + ' - ' + snapshot.data[index].type,
),
],
),
),
);
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.black,
),
);
}
},
);
}
}
My JSON that returns from the API link is structured like this:
[
{
"Title": "Audio 1",
"Duration": "5 min",
"type": "audio",
"bodyText": "lorem ipsum blah blah audio",
"url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
},
{
"Title": "Video 1",
"Duration": "5 min",
"type": "video",
"bodyText": "lorem ipsum blah blah video",
"url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
},
{
"Title": "Audio 2",
"Duration": "5 min",
"type": "audio",
"bodyText": "lorem ipsum blah blah audio",
"url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
},
{
"Title": "Video 2",
"Duration": "5 min",
"type": "video",
"bodyText": "lorem ipsum blah blah video",
"url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
},
{
"Title": "Audio 3",
"Duration": "5 min",
"type": "audio",
"bodyText": "lorem ipsum blah blah audio",
"url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
},
{
"Title": "Video 3",
"Duration": "5 min",
"type": "video",
"bodyText": "lorem ipsum blah blah video",
"url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
},
]
This is the error I am getting in my VS code debug console:
The argument type 'Future<List<dynamic>> Function()' can't be assigned to the parameter type 'Future<Object?>?'.
The red line appears right where I try to define my future in the FutureBuilder here under fetchSectionData:
return FutureBuilder(
future: fetchSectionData,
builder: (context, snapshot) {
I am not sure what this error means. Could somebody please explain it? The Future is definitely returning a <List>, but how do I get this list into the futurebuilder so that I can iterate over it and output the data into the gridview?
I am fairly new to flutter and come from a javascript web background, usually in javascript you can just loop over an array and output it that way. I'm tempted to do it that way here but I know that wouldn't be right.
When I looked up the Flutter documentation on how to fetch data from the internet it mentioned that I have to convert the response into a custom dart object, but nothing I try seems to work.
Appreciate your help!
Try this !
return FutureBuilder<List<dynamic>>(
future: fetchSectionData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return GridView.builder(
itemCount: 12,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300,
crossAxisSpacing: 16.0,
mainAxisSpacing: 18.0,
childAspectRatio: MediaQuery.of(context).size.height / 930,
),
itemBuilder: (BuildContext context, int index) => GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => OtherScreen()),
);
},
child: Column(
children: [
Container(
margin: const EdgeInsets.only(bottom: 12.0),
height: 144,
),
Text(
'${snapshot.data[index]['Title']}',
),
Text(
'${snapshot.data[index]['Duration'] + ' - ' + snapshot.data[index]['type']}',
),
],
),
),
);
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.black,
),
);
}
},
);
You have to pass the function like this
future: fetchSectionData(),
in your GridView it's better to use
...
itemCount: snapshot.data.length
...
List<dynamic> equals to List it self
Future<List> fetchSectionData() async{
...
it's a good practice to determine type of FutureBuilder
FutureBuilder<List>(
...
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.)
I have a JSON list of products of two companies, Apple and Windows. The below is my JSON data structure:
[
{
"category": "Apple",
"name": "Macbook Air"
},
{
"category": "Apple",
"name": "Macbook Pro"
},
{
"category": "Microsoft",
"name": "Surface"
},
{
"category": "Apple",
"name": "iPad"
},
{
"category": "Microsoft",
"name": "Windows"
},
{
"category": "Apple",
"name": "Siri"
},
{
"category": "Microsoft",
"name": "Office"
}
]
I am trying to create a listview of either Apple or Microsoft category. But in my current listview, it shows all the products from the JSON file:
List data;
Future<String> loadJsonData() async{
var jsonText = await rootBundle.loadString('assets/json/products.json');
setState(() => data = json.decode(jsonText));
}
#override
void initState() {
this.loadJsonData();
super.initState();
}
The above code loads all the products of both companies. But how can I load only the products of Apple or Microsoft?
This is my ListView
Container(
child: data == null ? Container() : ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: ListTile(
title: Text(
data[index]['name'],
),
),
);
}
),
),
You can do something like this:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xfff5f5f5),
appBar: AppBar(
title: Text('Demo'),
),
body: Container(
child: data == null
? Container()
: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: ((data[index]['category'] == "Apple")
? ListTile(title: Text(data[index]['name']))
: Container()));
}),
));
}
}
You can remove items except for Apple or Microsoft:
data.removeWhere((item) {
item['category'] != 'Apple' || item['category'] != 'Microsoft'
});
See also List Dartdoc
this json has two data first only name and second is inside there also name which is service name. ex 'Travel and Stay' and 'Religious' is main name which has to be displayed in expansion tile name and The 'Church/ Baptism' and 'Hindu Temples' is a subitem which is displayed inside checkbox list tile. Hope you understand :slightly_smiling_face: Please Help me
class _MyHomePageState extends State<MyHomePage> {
var lovCountryServices = [
{
"services": [
{
"service_group_id": 22,
"service_category": "B",
"name": "Air Tickets",
"id": 228
},
{
"service_group_id": 22,
"service_category": "W",
"name": "Boys Hostel",
"id": 229
},
],
"name": "Travel and Stay",
"is_active": true,
"id": 22
},
{
"services": [
{
"service_group_id": 19,
"service_category": "B",
"name": "Church/ Baptism",
"id": 193
},
{
"service_group_id": 19,
"service_category": "B",
"name": "Hindu Temples",
"id": 194
}
],
"name": "Religious",
"is_active": true,
"id": 19
}
];
List<_Result> _results = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: lovCountryServices.length,
itemBuilder: (BuildContext context, int index) {
var item = lovCountryServices[index];
var items= lovCountryServices[index]['services'];
return ExpansionTile(
title: Text(item['name']),
children: <Widget>[
CheckboxListTile(
title: Text("temp"),
value: item['is_active'],
onChanged: (val) {
setState(() {
item['is_active'] = val;
});
},
),
],
);
},
),
RaisedButton(
onPressed: () => print("sending to backend"),
child: Text("SEND"),
)
],
)),
);
}
}
I want thw data in checkbox list tile right there is dummy data called TEMP and i want the data from json right now in json there is 'Boys Hostel' this data needs to comes inside the checkbox listtile. Hope you undestand please help me
Working Code: You can use a Map variable to store boolean value.
Map<String, bool> _selection = {};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dummy'),
),
body: Center(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: lovCountryServices.length,
itemBuilder: (BuildContext context, int index) {
var item =
lovCountryServices[index]; // should be outside build function
List items = item['services']; // should be outside build function
return ExpansionTile(
title: Text(item['name']),
children: List.generate(items.length, (i) {
_selection[items[i]['name']] =
_selection[items[i]['name']] ?? item['is_active'];
return CheckboxListTile(
title: Text(items[i]['name']),
value: _selection[items[i]['name']],
onChanged: (val) {
setState(() {
_selection[items[i]['name']] = val;
});
},
);
}),
);
},
),
RaisedButton(
onPressed: () => print("sending to backend"),
child: Text("SEND"),
)
],
)),
);
}