Pass Map data from API to multiple screens in flutter? - json

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();
}
*/
}

Related

How to update/setState bool value through a method's input in flutter?

This one probably has a very easy answer, but I've been blocked trying to solve this for a while and can't really get it to work.
**problem:**when I call checkBeans() with the input got1Beans on the initState, I still get the initial value (false) for got1Beans. What I need is to be able to update got1Beans = true. Anyone able to sort this beany situation? What am I missing here?
import 'package:flutter/material.dart';
class BeansExample extends StatefulWidget {
#override
_BeansExampleState createState() => _BeansExampleState();
}
class _BeansExampleState extends State<BeansExample> {
bool got1beans = false;
bool got2beans = false;
bool got3beans = false;
bool got4beans = false;
bool got5beans = false;
checkBeans(bool getBeans) {
setState(() {
getBeans = true; //shouldn't it update the input value???
});
}
#override
void initState() {
super.initState();
checkBeans(got1beans); //here I call got1Beans
print(got1beans); //This prints false, need to get true here
}
#override
Widget build(BuildContext context) {
return Container();
}
}
checkBeans(value) doesn't mean this.gotXbeans = true/false. It means value = true/false. The variable "value" should be local or method variable.
You can update your code.
List<bool> beans = [false, false, false, false, false];
checkBeans(int index, bool value) {
setState(() {
beans[index] = value;
});
}
Or you can create methods like this.
checkBeans1, checkBeans2, checkBeans3, checkBeans4, checkBeans5...
checkBeans1(bool value) {
setState(() {
get1beans = value;
});
}
Please check this question if you want to pass a reference.

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;

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

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;
}

What's the best way to serialize data from Firebase into Dart object for Flutter?

What is the best way to serialize a list of data from Firebase? Firebase provides an object with a list of properties for the list which makes it more challenging to come up with a good conversion technique.
How would you serialize this data from Firebase:
{
"-KiRg_F-qC59xxlfZ6ej": {
"first":"Brandon",
"last":"Donnelson"
},
"-KiRgmsISBsJSWfXhrdD": {
"first":"Danny",
"last":"Kirk"
}
}
What I came up with — see _loadData()) —:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Text(
'click',
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _test,
tooltip: 'Increment',
child: new Icon(Icons.add),
),);
}
void _test() {
_loadData();
}
_loadData() async {
String url = 'https://dev-xxxxxxx.firebaseio.com/names.json';
var httpClient = createHttpClient();
var response = await httpClient.read(url);
print('response=' + response);
// response = {
// "-KiRg_F-qC59xxlfZ6ej":{"first":"Brandon","last":"Donnelson"},
// "-KiRgmsISBsJSWfXhrdD":{"first":"Danny","last":"Kirk"}
// }
NamesData namesData = new NamesData(JSON.decode(response));
print("names.len=" + namesData.names.length.toString());
}
}
class NamesData {
final List<NameData> names = new List();
NamesData(Map data) {
data.values.forEach((Map map) => names.add(new NameData.fromJson(map)));
}
}
class NameData {
String first;
String last;
NameData.fromJson(Map map) {
first = map['first'];
last = map['last'];
}
}
I found the JSON decoder has a better method for instantiating classes with the reviver function. This feels much better, but I think I can do better.
_loadData() async {
String url = 'https://dev-xxxxxxx.firebaseio.com/names.json';
var httpClient = createHttpClient();
var response = await httpClient.read(url);
print('response=' + response);
// response = {
// "-KiRg_F-qC59xxlfZ6ej":{"first":"Brandon","last":"Donnelson"},
// "-KiRgmsISBsJSWfXhrdD":{"first":"Danny","last":"Kirk"}
// }
var extendedJson = new JsonCodec(reviver: _reviver);
var o = extendedJson.decode(response);
print('end');
}
// https://github.com/dart-lang/sdk/blob/master/tests/lib/convert
// /json_toEncodable_reviver_test.dart
_reviver(key, value) {
if (value != null && value is Map && key.toString().contains("-")) {
return new NameData2(value);
}
return value;
}
}
class NameData2 {
String first;
String last;
NameData2(Map map) {
first = map['first'];
last = map['last'];
}
}
I personally like writing a tiny Codec sometimes:
DartPad example
import 'dart:convert';
void main() {
final decoder = const FirebaseNamesDecoder();
print(decoder.convert(exampleFirebaseData));
}
class NamedData {
final String id;
final String firstName;
final String lastName;
const NamedData(this.id, this.firstName, this.lastName);
#override
String toString() => '$NamedData {$id: $firstName $lastName}';
}
class FirebaseNamesDecoder extends Converter<Map, Iterable<NamedData>> {
const FirebaseNamesDecoder();
#override
Iterable<NamedData> convert(Map<String, Map> raw) {
return raw.keys.map((id) => new NamedData(id, raw[id]['first'], raw[id]['last']));
}
}
final exampleFirebaseData = {
"-KiRg_F-qC59xxlfZ6ej": {
"first":"Brandon",
"last":"Donnelson"
},
"-KiRgmsISBsJSWfXhrdD": {
"first":"Danny",
"last":"Kirk"
}
};
Results in:
(
NamedData {-KiRg_F-qC59xxlfZ6ej: Brandon Donnelson},
NamedData {-KiRgmsISBsJSWfXhrdD: Danny Kirk}
)
Dart 2 needs modification to the overridden method:
Iterable<NamedData> convert(Map<dynamic,dynamic> raw) {
return raw.keys
.map((id) => new NamedData(id, raw[id]['first'], raw[id]['last']));
}
Serializing JSON manually using dart:convert
Basic JSON serialization in Flutter is very simple. Flutter has a built-in dart:convert library which includes a straightforward JSON encoder and decoder.
The following sample JSON implements a simple user model.
{"name":"John Smith","email":"john#example.com"}
With dart:convert, you can serialize this JSON model in two ways.
1) Serializing JSON inline
   
Map<String, dynamic> user = jsonDecode(jsonString);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
2) Serializing JSON inside model classes
class User
{
final Stringname;
final Stringemail;
User(this.name,this.email);
User.fromJson(Map<String,dynamic>json):name=json['name'],email=json['email'];
Map<String,dynamic>toJson()=>
{
'name':name,
'email':email,
};
}
The responsibility of the decoding logic is now moved inside the model itself. With this new approach, you can decode a user easily.
Map userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
I would recommend using json_serializable it is developed by google developers and it can handle the boilerplate code easily.