Flutter : Showing nested data received from serve : Edited #3 - json

I have an app receive nested data from server in the page i print the data's and it is printed successfully :
class page :
final DateTime mDate;
final List<Games> games;
class DatedMatchs {
DatedMatchs(
this.mDate,
this.games,
);
}
class Games {
Games(
this.id,this.sId,this.wId,this.leagueName,this.homeTeam,this.awayTeam,this.homeGoals,this.awayGoals,this.mHour,this.homeEx,this.awayEx,
);
final String id;
final String sId;
final String wId;
final String leagueName;
final String homeTeam;
final String awayTeam;
final String homeGoals;
final String awayGoals;
final String mHour;
final String homeEx;
final String awayEx;
}
page i want to show data:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import '../models/dated_matchs.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
List matchs = [];
Future<List> getmatchs() async {
var url =
'xxx/api/controller/matchs/dated_matchs.php?s_id=1';
var response = await http.get(url);
var data = jsonDecode(response.body);
print(data);
}
return FutureBuilder(
future: getmatchs(),
builder: (ctx, snapshot) {
return Container();
});
}
}
Now i don't know how to add received data to a list then show it on list-view
I used this way inside future function but there is something wrong :
Future<List> getmatchs() async {
var url =
'xxx/api/controller/matchs/dated_matchs.php?s_id=1';
var response = await http.get(url);
var data = jsonDecode(response.body);
for (var x in data) {
for (var y in x['games']) {
cont1.add(TextEditingController());
cont2.add(TextEditingController());
Games newmatch = Games(
y['id'],
y['s_id'],
y['w_id'],
y['league_name'],
y['home_team'],
y['away_team'],
y['home_goals'],
y['away_goals'],
y['m_hour'],
y['home_ex'],
y['away_ex']);
matchs.add(newmatch);
}
DatedMatchs newdated = DatedMatchs(x['m_date'], x['matchs']);
datedmatchs.add(newdated);
}
return datedmatchs;
}
no thing print

Some of your data is coming back as a Map, rather than a List. You'll need to debug and see which data is a Map, then you can print it from the Map.
Also, I wouldn't call an api in your UI. It's best to use a state management library, such as Bloc, Provider, or RxDart.

I solved it and below the future method which get me list of data correctly :
List<Games> games = []; // I added type of List
List<DatedMatchs> datedmatchs = []; // I added type of List
Future<List> getmatchs() async {
var url =
'xxx/api/controller/matchs/dated_matchs.php?s_id=1';
var response = await http.get(url);
var data = await jsonDecode(response.body);
for (var x in data) {
for (var y in x['games']) {
cont1.add(TextEditingController());
cont2.add(TextEditingController());
Games newmatch = Games(
y['id'],
y['s_id'],
y['w_id'],
y['league_name'],
y['home_team'],
y['away_team'],
y['home_goals'],
y['away_goals'],
y['m_hour'],
y['home_ex'],
x['away_ex']);
games.add(newmatch);
}
DatedMatchs newdated = DatedMatchs(x['m_date'], games); // add name of list
datedmatchs.add(newdated);
}
return datedmatchs;
}

Related

Can anyone explain the function of .map()

I was trying to make a Covid Tracking application using flutter, and I came across this function getCountrySummary( ),
import 'package:covid_tracker/models/country_summary.dart';
import 'package:covid_tracker/models/global_summary.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class CovidService {
Future<GlobalSummaryModel> getGlobalSummary() async {
final data =
await http.get(Uri.parse('https://api.covid19api.com/summary'));
if (data.statusCode != 200) {
throw Exception();
}
GlobalSummaryModel summary =
GlobalSummaryModel.fromJson(json.decode(data.body));
return summary;
}
Future<List<CountrySummaryModel>> getCountrySummary(String slug) async {
String url = "https://api.covid19api.com/total/dayone/country/$slug";
final data = await http.get(Uri.parse(url));
if (data.statusCode != 200) {
throw Exception();
}
List<CountrySummaryModel> summaryList = (json.decode(data.body) as List)
.map((item) => CountrySummaryModel.fromJson(item))
.toList();
return summaryList;
}
}
So I know what the function getCountrySummary() is trying to do, but I don't understand what statement
List<CountrySummaryModel> summaryList = (json.decode(data.body) as List).map((item) => CountrySummaryModel.fromJson(item)).toList();
is trying to do, and CountrySummaryModel is an object.
class CountrySummaryModel {
final String country;
final int confirmed;
final int death;
final int recovered;
final int active;
final DateTime date;
CountrySummaryModel(this.country, this.active, this.confirmed, this.date,
this.death, this.recovered);
factory CountrySummaryModel.fromJson(Map<String, dynamic> json) {
return CountrySummaryModel(
json["country"],
json["active"],
json["confirmed"],
DateTime.parse(json["date"]),
json["death"],
json["recovered"],
);
}
}
When you call Map on a list, it means you want to reach each item in it, in your case you call map on your list to parse each item in it and at then call toList() to make a list of this items.
If I understand your code correctly:
First, you convert data to List.
Then, use CountrySummaryModel.fromJson() and .toList() to convert it to List<CountrySummaryModel>.

Pass Map data from API to multiple screens in flutter?

How can i pass the Map data from that list to others screens being StatefulWidget or StatelessWidget, and why it donĀ“t work like the one screen example?
The Api Part here:
Future pokeinfo(int position) async {
var dio = Dio();
Response response;
response =
await dio.get('https://pokeapi.co/api/v2/pokemon/${position.toString()}');
Map json = jsonDecode(response.toString());
return json;
}
The function part here:
bool widgetVisible = false;
List<PokeList> elements = [];
void showWidget() {
createList();
setState(() {
widgetVisible = !widgetVisible;
});
}
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
void createList() async {
List<PokeList> _elements = [];
for (int i = 1; i < 50; i++) {
Map currentData = await pokeinfo(i);
_elements.add(PokeList(currentData));
}
setState(() {
elements = _elements;
});
How it works in one screen:
Map data;
PokeList(Map this.data);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PokemonPage()),
);
you need to use one of state management packages, However google recommend provider package.
with provider you can make request in provider class
class PokeProvider extends ChangeNotifier {
Map json;
Future pokeinfo(int position) async {
var dio = Dio();
Response response;
response = await dio
.get('https://pokeapi.co/api/v2/pokemon/${position.toString()}');
Map json = jsonDecode(response.toString());
return json;
}
}
then store it's data inside a variable that every screen will listen on change of it, like json
then expose that provider to whole app
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => PokeProvider(),
child: const MyApp(),
),
);
}
then read it from anywhere inside application with
PokeProvider pokeProvider = Provider.of<PokeProvider>(context, listen: false);
pokeProvider.json; // now you got your map
be carful of listen: false if your data will change ui directly so make listen: true
for more information about provider package check the link blew contain everything you need
ChangeNotifierProvider example
Thank you all who tried to help me, I got the problem and how to solve it. I will put the solution here to someone who has the same problem. A simple thing to change actually:
Remove the specific screen from List.
bool widgetVisible = false;
List elements = [];
void showWidget() {
createList();
setState(() {
widgetVisible = !widgetVisible;
});
}
Inside the function you just need to change List, repeating what was shown previously.
void createList() async {
List _elements = [];
for (int i = 1; i < 50; i++) {
Map currentData = await pokeinfo(i);
_elements.add(PokeList(currentData));
}
And finally, you only have to put inside the screen class. Like that:
Class ScreenNameHere extends StatelessWidget{
Map data;
ScreenNameHere(Map this.data);
/*
#override
Widget build(BuildContext context) {
return ScreenBody();
}
*/
}

Json from file not loading the first time

I'm trying to get a online JSON, save it locally for offline use. The issue is that when I try to get the data from the saved JSON file, the first time I open the new "window" no data is loading, because my "goggo1" variable does not receive the new value from reading the JSON file. The funny thing is if I go back and the open it again, then "goggo1" is getting the value required to show the JSON contents.
class HomePagerstare extends StatefulWidget {
Contact contact = new Contact();
String title, content;
#override
_HomePageState createState() => _HomePageState();
}
Future<String> get getFilePath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get getFile async {
final path = await getFilePath;
return File('$path/myfile.json');
}
Future<File> saveToFile(String datar) async {
final file = await getFile;
return file.writeAsString(datar);
}
Future<String> readFromFile() async {
try {
final file = await getFile;
final String fileContents = await file.readAsString();
return fileContents;
} catch (e) {
return "aici e eroare";
}
}
class _HomePageState extends State<HomePagerstare> implements HomeContract {
List<Note> data1 = List<Note>();
List<Note> data2 = List<Note>();
var goggo1 = "";
Future<List<Note>> loadJsonData() async {
var data = List<Note>();
this.setState(() {
print(goggo1);
var datas = jsonDecode(goggo1);
for (var noteJson in datas) {
data.add(Note.fromJson(noteJson));
Note note =
Note(noteJson["id"], noteJson["title"], noteJson["content"]);
}
});
return data;
}
#override
void initState() {
print("1Nu Suntem Online");
super.initState();
readFromFile().then((fileContents) {
setState(() {
goggo1 = fileContents;
});
});
print("1Nu Suntem Online");
this.loadJsonData().then((value) {
setState(() {
data1.addAll(value);
data2 = data1;
print("2Nu Suntem Online");
});
});
}
class Note {
String title;
String content;
int id;
the note.dart
Note(this.id, this.title, this.content);
Note.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
content = json['content'];
}
}
Please help!
I think I could give you an idea. Try something like this,
readFromFile().then((fileContents) {
setState(() {
goggo1 = fileContents;
});
this.loadJsonData().then((value) {
setState(() {
data1.addAll(value);
data2 = data1;
print("2Nu Suntem Online");
});
});
});
Hope that works!

Flutter/Dart - How to pass variables between classes?

I am very new to programming and I am trying to pass a variable (jsonData) from a future to another future in a different class, so the data from the variable can be stored in a database. Dont be confused, I am using an API for getting the neccessary data, this works perfectly fine. Any tips how I can access the variable jsonData to insert the data?
//first class gets the json data
class getJSONData {
Future<void> makeRequest() async {
var url = "some url";
Map inputData = {
"Password": example,
"SQL": sql query,
"db_server": "server",
"db_table": "table",
};
var body = json.encode(inputData);
var putRequest = await http.put(
Uri.encodeFull(url), headers: {"Content-Type": "application/json"},
body: body);
//this variable has to be stored
var jsonData = json.decode(putRequest.body);
}
}
//data needs to be inserted here
class Database {
Future<void> insertJSON() async {
db = await openDatabase(
join(await getDatabasesPath(), tableName),
onCreate: (db, version) {
return db.execute('''
INSERT INTO $tableName(column1, column2, etc) VALUES (${jsonData["Records].data1}, ${jsonData["Records].data2}, etc)
''');
}
);
}
}
below i am showing you how to pass data between two screens :
Screen 1 :
class HomeScreenTopContainer extends StatefulWidget {
#override
_HomeScreenTopContainerState createState() => _HomeScreenTopContainerState();
}
class _HomeScreenTopContainerState extends State<HomeScreenTopContainer> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Inkwell(onTap:(){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ParentLanding(parameter:"pass your value here")),
);
},child:Text("Tap Here and pass data")),
],
);
}
}
Screen 2 :
class ParentLanding extends StatefulWidget {
String parameter;
ParentLanding({Key key,String parameter}) : super(key: key,parameter:parameter);
#override
_ParentLandingState createState() => _ParentLandingState();
}
class _ParentLandingState extends State<ParentLanding> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Inkwell(onTap:(){
// you can use this parameter like this.
},child:Text(**Widget.parameter**)),
],
);
}
}
You have many options.
I'll suggest you one.
Change the signatures of both methods.
Example
Your HttpClient class
Future<Map<String, dynamic>> makeRequest() async {
// ... your code
var jsonData = json.decode(putRequest.body);
return jsonData;
}
Your DB Class
Future<void> insertJSON(Map<String, dynamic> jsonData) async {
db = await openDatabase(
join(await getDatabasesPath(), tableName),
onCreate: (db, version) {
return db.execute('''
INSERT INTO $tableName(column1, column2, etc) VALUES
(${jsonData["Records].data1}, ${jsonData["Records].data2}, etc)
''');
}
);
}
Use a third Class/object/method that make the call to the api, take the result and pass it to the db class:
main() async {
HttClient http = HttpClient();
Database db = Database();
final json = await http.makeRequest();
await db.insertJSON(json);
}
Easiest way to pass any value is using an argument.
Value can be send as:
Keys.navKey.currentState.pushNamed(
Routes.gameViewScreen, arguments: jsonData );
In another class it can be retrieved as :
var jsonData = ModalRoute.of(context).settings.arguments;

HTTP request doesn't get complete data in flutter

I have this class :
class Weeks {
final int index;
final int udigree;
final int d_id;
final String activity_a;
final String activity_k;
final String title_a;
final String title_k;
Weeks(this.index, this.udigree, this.d_id, this.activity_a, this.activity_k,
this.title_a, this.title_k);
}
I used future function to get data from server :
import 'package:flutter/material.dart';
import 'package:jiyanUquraan/classes/weekly.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Weekly extends StatefulWidget {
#override
_WeeklyState createState() => _WeeklyState();
}
class _WeeklyState extends State<Weekly> {
#override
Widget build(BuildContext context) {
var widthView = MediaQuery.of(context).size.width;
var heightView = MediaQuery.of(context).size.height;
List weekly = [];
Map rData = ModalRoute.of(context).settings.arguments;
var cm_id = rData['current_m_id'];
var u_id = rData['u_id'];
var d_id = rData['d_id'];
var w_id = rData['w_id'];
// Futher Function for Get Data
Future<List> getWeeks() async {
print(cm_id);
print(u_id);
print(w_id);
var url =
'http://10.0.2.2/jiyan/test/api/digrees/weekly_report.php?m_id=$cm_id&u_id=$u_id&w_id=$w_id';
var response = await http.get(url);
var data = jsonDecode(response.body);
print(data);
print(data.length);
for (var x in data) {
Weeks _weeklyReport = Weeks(x['index'], x['udigree'], x['activity_a'],
x['activity_k'], x['title_a'], x['title_k'], x['d_id']);
weekly.add(_weeklyReport);
}
return weekly;
}
// End of Get Data
// Create Future Builder
return FutureBuilder(
future: getWeeks(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Center(
child: Text('Loading'),
);
} else {
return Center(child: Text('data'));
}
});
// End of Futur Builder
}
}
As I print the data and its length, the length is correct but the data is not complete as shown:
As it doesn't get the data correctly the snapshot.data gets null, How can I fix this?
Does the error occur because the data is Kurdish?
the Mistake was in the class .
the variables d_id and udigree must be String
class Weeks {
final int index;
final String udigree;
final Stringd_id;
final String activity_a;
final String activity_k;
final String title_a;
final String title_k;
Weeks(this.index, this.udigree, this.d_id, this.activity_a, this.activity_k,
this.title_a, this.title_k);
}