I'm new to the Flutter world and mobile app development and struggling with how I should pass data throughout my app. This is my code, How can I pass data from future to another future on the same page? these are my two futures and my class and my HomePage with the futurebuilders
help, please.
This is my future that returns the Location. From her I need to pass the Location id to the next future.
Future<Location> Lastlocation() async {
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/location/getlastlocation?token=" + value;
http.Response response = await http.get(
myUrl,
headers: {
'Accept': 'application/json',
//'Authorization': 'token $value'
},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
return Location.fromJson(json.decode(response.body));
} else {
// then throw an exception.
throw Exception('Failed to load album');
}
}
**This is the second future that returns a List of weather that depends on location id from the first future *Lastlocation()
Future<List> Getweither(String ID) async {
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/dashbaord/Getweither/$ID?token=" + value;
http.Response response = await http.get(myUrl,
headers: {
'Accept': 'application/json',
});
print("myUrldevice :"+myUrl);
print("status :"+response.statusCode.toString());
return json.decode(response.body);
}
This is my class Location
// To parse this JSON data, do
//
// final location = locationFromJson(jsonString);
import 'dart:convert';
List<Location> locationFromJson(String str) => List<Location>.from(json.decode(str).map((x) => Location.fromJson(x)));
String locationToJson(List<Location> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Location {
Location({
this.automaticIrrigation,
this.coordinates,
this.createdDate,
this.sensorIds,
this.id,
this.siteName,
this.description,
this.v,
});
bool automaticIrrigation;
List<double> coordinates;
DateTime createdDate;
List<String> sensorIds;
String id;
String siteName;
String description;
int v;
factory Location.fromJson(Map<String, dynamic> json) => Location(
automaticIrrigation: json["AutomaticIrrigation"],
coordinates: List<double>.from(json["Coordinates"].map((x) => x.toDouble())),
createdDate: DateTime.parse(json["Created_date"]),
sensorIds: List<String>.from(json["Sensor_ids"].map((x) => x)),
id: json["_id"],
siteName: json["SiteName"],
description: json["Description"],
v: json["__v"],
);
Map<String, dynamic> toJson() => {
"AutomaticIrrigation": automaticIrrigation,
"Coordinates": List<dynamic>.from(coordinates.map((x) => x)),
"Created_date": createdDate.toIso8601String(),
"Sensor_ids": List<dynamic>.from(sensorIds.map((x) => x)),
"_id": id,
"SiteName": siteName,
"Description": description,
"__v": v,
};
}
and this is my homePage
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sidebar_animation/Services/DataHelpers.dart';
import 'package:sidebar_animation/sidebar/sidebar_layout.dart';
import '../bloc.navigation_bloc/navigation_bloc.dart';
import 'package:sidebar_animation/constants.dart';
import 'package:flutter/gestures.dart';
import 'package:sidebar_animation/bloc.navigation_bloc/navigation_bloc.dart';
class HomePage extends StatelessWidget with NavigationStates {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseHelper2 databaseHelper2 = new DatabaseHelper2();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: ListView(
FutureBuilder(
future: databaseHelper2.Getweither(location_id),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem !");
}
return snapshot.hasData
? ItemList(list: snapshot.data)
: Center(child: CircularProgressIndicator(
),
);
}
),
);
}
Widget _buildProgrammCard() {
return Container(
height: 90,
child:
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 4,
margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
child:
FutureBuilder(
future: databaseHelper2.Lastlocation(),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("mochkla lenaa *");
}
return snapshot.hasData
? Text("Location :" +snapshot.data.siteName)
: Center(child: CircularProgressIndicator(
),
);
}
),
),
);
}
class ItemList extends StatelessWidget{
List list;
ItemList({this.list});
ScrollController _controller = new ScrollController();
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list == null ? 0 : list.length,
scrollDirection: Axis.horizontal,
itemExtent: 190.0,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 0, 14),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
item.storyUrl,
),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black26,
BlendMode.darken,
),
),
borderRadius: BorderRadius.circular(10.0),
color: Colors.grey,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"temp :",
style: TextStyle(color: Colors.white),
),
),
Padding(
padding: EdgeInsets.only(left: 24),
child:
Text(
list[i]['Weither']['Temp'],
),
),
],
),
),
);
},
),
}
}
Finally, I need to get the location id from the first future that returns the Location to the second future Getweither(String ID) to get the weather of a specified location.
Just nest the two FutureBuilders, and pass the snapshot.data as a parameter of the second one.
Minimalistic example:
FutureBuilder(
future: databaseHelper2.Lastlocation(),
builder: (context, snapshot) {
if (snapshot.hasError) {
print(snapshot.error);
print("there is problem !");
}
return snapshot.hasData
? FutureBuilder(
future: databaseHelper2.Getweither(snapshot.data.id),
builder: (context, snapshot2) {...})
: Center(child: CircularProgressIndicator());
}
);
Related
I have this error:
_TypeError (type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List')
This is my HTTP method class:
List<Property>? areaview;
Future<void> fetcharea() async {
final response = await http.get(
Uri.parse('https://arz-e-nafees.nafeessolutions.com/public/api/view'));
if (response.statusCode == 200) {
var property = (json.decode(response.body));
Areaview viewarea = Areaview.fromJson(property);
areaview = viewarea.properties;
return property;
} else {
throw Exception('Unexpected error occured!');
}
}
This is my model class:
// To parse this JSON data, do
//
// final areaview = areaviewFromJson(jsonString);
import 'dart:convert';
Areaview areaviewFromJson(String str) => Areaview.fromJson(json.decode(str));
String areaviewToJson(Areaview data) => json.encode(data.toJson());
class Areaview {
Areaview({
required this.properties,
});
List<Property> properties;
factory Areaview.fromJson(Map<String, dynamic> json) => Areaview(
properties: List<Property>.from(
json["properties"].map((x) => Property.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"properties": List<dynamic>.from(properties.map((x) => x.toJson())),
};
}
class Property {
Property({
required this.propertyImage,
required this.propertyTitle,
required this.locationCity,
required this.locationArea,
required this.propertyDescription,
required this.propertyPrice,
});
String? propertyImage;
String propertyTitle;
String locationCity;
String locationArea;
String propertyDescription;
String propertyPrice;
factory Property.fromJson(Map<String, dynamic> json) => Property(
propertyImage:
json["property_image"] == null ? null : json["property_image"],
propertyTitle: json["property_title"],
locationCity: json["location_city"],
locationArea: json["location_area"],
propertyDescription: json["property_description"],
propertyPrice: json["property_price"],
);
Map<String, dynamic> toJson() => {
"property_image": propertyImage == null ? null : propertyImage,
"property_title": propertyTitle,
"location_city": locationCity,
"location_area": locationArea,
"property_description": propertyDescription,
"property_price": propertyPrice,
};
}
And this is my future builder:
FutureBuilder(
future: fetcharea(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return VerticalCards(snapshot.data);
}
},
),
Here is a screenshot of the exception
Data type I am getting after return from HTTP method:
The problem is I am return when I try to return Areaview type then I cannot return property in vertical cards and when I return property type then I cannot return Areaview.
Edit: Vertical Cards class
// ignore_for_file: prefer_const_constructors, must_be_immutable, use_key_in_widget_constructors, sized_box_for_whitespace
import 'package:arzenafees/Components/Constants.dart';
import 'package:arzenafees/model/areaview.dart';
import 'package:arzenafees/services/areaviewapi.dart';
import 'package:flutter/material.dart';
class VerticalCards extends StatelessWidget {
List<Areaview> areaData;
VerticalCards(this.areaData);
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Constants.colorSecond),
height: MediaQuery.of(context).size.height,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: areaData.length,
itemBuilder: (context, index) {
final String? image = areaData[index] as String;
return Container(
// width: MediaQuery.of(context).size.width * 0.6,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: SizedBox(
width: 300,
height: 180,
child: GestureDetector(
onTap: () {},
child: Stack(alignment: Alignment.bottomLeft, children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
image: DecorationImage(
image: NetworkImage(image!),
fit: BoxFit.fill,
),
),
),
]),
)),
),
));
},
),
);
}
}
Try it and tell me if have any problem
Future fetcharea() async {
final response = await http.get(
Uri.parse('https://arz-e-nafees.nafeessolutions.com/public/api/view'));
if (response.statusCode == 200) {
return areaviewFromJson(response.body).properties;
} else {
throw Exception('Unexpected error occured!');
}
}
FutureBuilder(
future: fetcharea(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
final data = snapshot.data
return VerticalCards(data);
}
},
),
I want to access data from the API below.
"https://api.categen.com/api.php/recent_activity/1"
and Want to print in text.
Please help me.
Moreover, there is 3 classes
Home . dart file.
DataService . dart file.
Model . dart file
I tried below code.
Home.dart .
import 'dart:convert';
import 'package:categen_api_test/data_service.dart';
import 'package:categen_api_test/model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _dataService = DataService();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Categen API"),
),
body: Center(
child: ElevatedButton(
child: Text("Click me"),
onPressed: () {
_getlist();
},
),
),
);
}
void _getlist() async {
final response = await _dataService.getData();
print(response.name);
}
}
DataService
import 'dart:convert';
import 'package:categen_api_test/model.dart';
import 'package:http/http.dart' as http;
class DataService {
Future<ModelData> getData() async {
final String url = "https://api.categen.com/api.php/recent_activity/1";
final uri = Uri.https('api.categen.com', '/api.php/recent_activity/1');
final response = await http.get(uri);
print(response.body);
final json = jsonDecode(response.body);
return ModelData.fromJson(json);
}
}
First create a model like this:
class Model {
final String name;
final String location;
final String action_value;
final String item;
Model(this.name, this.location, this.action_value, this.item);
List<Model> getList(json) {
List<Model> tempList = []
json['records'].forEach((model)=> tempList.add(
Model(
model["name"],
model["location"],
model["action_value"],
model["item"]
)
)
);
return tempList;
}
}
Then create a function to fetch the data:
Future<List<Model>> fetchData() async {
final response = await http.get('https://api.categen.com/api.php/recent_activity/1');
if (response.statusCode == 200) {
return Model.getList(response.body);
} else {
throw Exception('Unable to fetch products from the REST API');
}
}
call the fetch data function in the init state of the HomePage Widget
late Future<List<Model>> futureData;
void initState() {
super.initState();
futureData = fetchData();
}
what is left to do now is to get your data using a FutureBuilder Widget.
and display the list of your data
FutureBuilder<Model>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snaphot.map((e)=>Text(e.name)).toList()
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
)
if you want to reload the data on the click of a button, then call the fetch data whenever the button is clicked and then rebuild state of the Homepage widget like shown below
onPressed: (){
setState(
(){
futureData = fetchData();
}
);
}
Try below code hope its helpful to you. If you get data from API refer my answer here or here or here hope it's helpful to you
Create your home widget:
Center(
child: ElevatedButton(
child: Text('Pressed Me'),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Jobs(),
),
),
),
),
Create your List Widget.
Your API Call function:
Future<List<dynamic>> getJobsData() async {
String url = 'https://api.categen.com/api.php/recent_activity/1';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body)['records'];
}
Your Widget:
Column(
children: [
Expanded(
child: Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var name = snapshot.data![index]['name'];
var location = snapshot.data![index]['location'];
var item = snapshot.data![index]['item'];
var action = snapshot.data![index]['action_value'];
var date = snapshot.data![index]['created_timestamp'];
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.green.shade300),
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
leading: Text(
action.toString(),
),
title: Text(name),
subtitle: Text(
location + '\n' + date,
),
trailing: Text(item),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
),
],
),
Your all class:
class Jobs extends StatelessWidget {
Future<List<dynamic>> getJobsData() async {
String url = 'https://api.categen.com/api.php/recent_activity/1';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body)['records'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Jobs'),
),
body: Column(
children: [
Expanded(
child: Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var name = snapshot.data![index]['name'];
var location = snapshot.data![index]['location'];
var item = snapshot.data![index]['item'];
var action = snapshot.data![index]['action_value'];
var date = snapshot.data![index]['created_timestamp'];
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.green.shade300),
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
leading: Text(
action.toString(),
),
title: Text(name),
subtitle: Text(
location + '\n' + date,
),
trailing: Text(item),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
),
],
),
);
}
}
Your Home widget output screen->
Your List Widget output screen->
This is the homepage code of the app I'm currently developing and I have to get all data from API. Therefore I've come up _getLatest that gets the data from the URL which is a list of maps and the data goes into _latest. And I implemented the future builder based on Flutter: Execute a function on Startup - wait for HTTP response parser to extract image URL but things are not quite done.
First of all there are two blue underlines: 1. Future<String> _getLatest() async { says
This function has a return type of 'FutureOr', but doesn't end with a return statement. 2. builder: (BuildContext context, AsyncSnapshot<String> snapshot) { says This function has a return type of 'Widget', but doesn't end with a return statement.
And the main problem is the homepage is that snapshot.connectionState doesn't change to done state so it's loading data eternally. And I'm pretty sure it's because of the code not the URL, the API works fine.
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:convert';
import 'package:kzstats/common/AppBar.dart';
import 'package:kzstats/common/Drawer.dart';
class Homepage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
final String currentPage = 'KZStats';
var _latest = [];
Future<String> _getLatest() async {
var url =
'https://kztimerglobal.com/api/v2.0/records/top/recent?stage=0&tickrate=128&modes_list_string=kz_timer&limit=3';
var httpClient = new HttpClient();
var result;
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
var data = jsonDecode(json);
result = data;
} else {}
} catch (exception) {}
setState(() {
_latest = result;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HomepageAppBar(currentPage),
drawer: HomepageDrawer(),
body: FutureBuilder<String>(
future: _getLatest(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
return new Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
color: Colors.white,
margin: const EdgeInsets.all(20),
child: Text('${_latest[index]}'),
);
},
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Loading data from API...'),
)
],
),
);
}
},
),
floatingActionButton: Builder(builder: (builderContext) {
return FloatingActionButton(onPressed: () {
_getLatest();
});
}),
);
}
}
First of all, you have done some wrong things,
if you are using FutureBuilder you can use a snapshot inside it so no need for _latest variable.
you can also use http package for easily requesting data.
inside your _getLatest() function you didn't returned the value and also it was not String.
also you can use Model class for easily assessing data after fetching json.
For your Problem my solution is
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:convert';
import 'package:http/http.dart' as http;
class Homepage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
final String currentPage = 'KZStats';
Future<List<KzTimer>> _getLatest() async {
var url =
'https://kztimerglobal.com/api/v2.0/records/top/recent?stage=0&tickrate=128&modes_list_string=kz_timer&limit=3';
List<KzTimer> result;
try {
var response = await http.get(Uri.parse(url));
if (response.statusCode == HttpStatus.ok) {
result = kzTimerFromJson(response.body);
} else {
print('Something went wrong!');
}
} catch (exception) {}
return result;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<KzTimer>>(
future: _getLatest(),
builder: (BuildContext context, AsyncSnapshot<List<KzTimer>> snapshot) {
return snapshot.hasData ?
ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
return new Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
color: Colors.white,
margin: const EdgeInsets.all(20),
child: Text('${snapshot.data[index].playerName}'),
//_latest[index].playerName
);
},
) :
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Loading data from API...'),
)
],
),
) ;
},
),
floatingActionButton: Builder(builder: (builderContext) {
return FloatingActionButton(onPressed: () {
_getLatest();
});
}),
);
}
}
Method to convert the response data to your model class data
List<KzTimer> kzTimerFromJson(String str) => List<KzTimer>.from(json.decode(str).map((x) => KzTimer.fromJson(x)));
Model class
class KzTimer {
KzTimer({
this.id,
this.steamid64,
this.playerName,
this.steamId,
this.serverId,
this.mapId,
this.stage,
this.mode,
this.tickrate,
this.time,
this.teleports,
this.createdOn,
this.updatedOn,
this.updatedBy,
this.place,
this.top100,
this.top100Overall,
this.serverName,
this.mapName,
this.points,
this.recordFilterId,
this.replayId,
});
int id;
String steamid64;
String playerName;
String steamId;
int serverId;
int mapId;
int stage;
String mode;
int tickrate;
double time;
int teleports;
DateTime createdOn;
DateTime updatedOn;
int updatedBy;
int place;
int top100;
int top100Overall;
String serverName;
String mapName;
int points;
int recordFilterId;
int replayId;
factory KzTimer.fromJson(Map<String, dynamic> json) => KzTimer(
id: json["id"],
steamid64: json["steamid64"],
playerName: json["player_name"],
steamId: json["steam_id"],
serverId: json["server_id"],
mapId: json["map_id"],
stage: json["stage"],
mode: json["mode"],
tickrate: json["tickrate"],
time: json["time"].toDouble(),
teleports: json["teleports"],
createdOn: DateTime.parse(json["created_on"]),
updatedOn: DateTime.parse(json["updated_on"]),
updatedBy: json["updated_by"],
place: json["place"],
top100: json["top_100"],
top100Overall: json["top_100_overall"],
serverName: json["server_name"],
mapName: json["map_name"],
points: json["points"],
recordFilterId: json["record_filter_id"],
replayId: json["replay_id"],
);
}
Change return type by: Future<void>. You are returning nothing.
You have an if and an else if but you still need the default case when both conditions are false.
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:convert';
import 'package:kzstats/common/AppBar.dart';
import 'package:kzstats/common/Drawer.dart';
class Homepage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
final String currentPage = 'KZStats';
var _latest = [];
Future<String> _getLatest() async {
var url =
'https://kztimerglobal.com/api/v2.0/records/top/recent?stage=0&tickrate=128&modes_list_string=kz_timer&limit=3';
var httpClient = new HttpClient();
var result;
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
var data = jsonDecode(json);
result = data;
} else {}
} catch (exception) {}
setState(() {
_latest = result;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HomepageAppBar(currentPage),
drawer: HomepageDrawer(),
body: FutureBuilder<String>(
future: _getLatest(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
return new Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
color: Colors.white,
margin: const EdgeInsets.all(20),
child: Text('${_latest[index]}'),
);
},
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Loading data from API...'),
)
],
),
);
}
},
),
floatingActionButton: Builder(builder: (builderContext) {
return FloatingActionButton(onPressed: () =>
_getLatest;
}),
);
}
}
The Correct Version of Your Code
I'm new to the flutter world and mobile app development and struggling with how I should pass data throughout my app. This is my code, How can I pass snapshot data from futurebuilder to another futurebuilder on the same page? help, please
**Widget _buildProgrammCard()**
From this widget Card I need to pass the location id which is in the futurebuilder to another futurebuilder.
Widget _buildProgrammCard() {
return Container(
height: 90,
child:
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 4,
margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
child:
FutureBuilder(
future: databaseHelper2.Lastlocation(),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem");
}
return snapshot.hasData
? Text("Location :" +snapshot.data.id)
: Center(child: CircularProgressIndicator(
),
);
}
),
),
);
}
Widget build(BuildContext context)
And this is the second Widget that I need to pass the location id into it from another widget.
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: FutureBuilder(
future: databaseHelper2.Getweither(location_id),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem !");
}
return snapshot.hasData
? ItemList(list: snapshot.data)
: Center(child: CircularProgressIndicator(
),
);
}
),
);
}
Flutter rebuilds widgets often so FutureBuilder shouldn't call a future function directly. (A widget may call its build function up to 60 times a second.)
Instead a FutureBuilder should only receive a future value from an async function called elsewhere.
In a StatefulWidget, the most common place to initiate long-running operations is in its initState() method.
The location data, retrieved during the first Widget initState, can be passed to the second widget, just like a regular constructor argument.
You'll access it in the 2nd widget's State class with widget.locationId.
import 'package:flutter/material.dart';
class FirstFuturePage extends StatefulWidget {
#override
State<StatefulWidget> createState() => FirstFutureState();
}
class FirstFutureState extends State<FirstFuturePage> {
Future<int> locationId = Future.value(-1);
#override
void initState() {
// TODO: implement initState
super.initState();
someAsyncCall();
}
Future<void> someAsyncCall() async {
// just returns the number 0 after 2 seconds & assigns it to "locationId" var
locationId = Future.delayed(Duration(seconds: 2), () => 0);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: FutureBuilder<int>(
future: locationId,
builder: (context, snapshot) {
int _locationId = snapshot.data;
if (snapshot.hasData)
return SecondWidget(_locationId);
return Text('Looking up location...');
},
),
),
),
);
}
}
class SecondWidget extends StatefulWidget {
final int locationId;
SecondWidget(this.locationId);
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class _SecondWidgetState extends State<SecondWidget> {
Future<String> weatherData = Future.value('Unknown');
#override
void initState() {
super.initState();
loadWeather(widget.locationId); // Use the locationId passed into widget
}
/// Takes locationId from First widget and looks up weather data for location
Future<void> loadWeather(int locationId) async {
List<String> weatherDataStore = List<String>.from(['Rainy', 'Sunny']);
weatherData = Future.delayed(
Duration(seconds: 2), () => weatherDataStore[locationId]
);
}
#override
Widget build(BuildContext context) {
int _locId = widget.locationId;
return FutureBuilder<String>(
future: weatherData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Weather for location $_locId is: ${snapshot.data}');
}
return Text('Loading Weather...');
},
);
}
}
State Management Solutions
When you get tired of passing values around like in the above example, you can use a State Management package or create your own that suits your needs.
Here's a nice overview from Jeff Delaney about various options:
https://fireship.io/lessons/flutter-state-management-guide/
And also check out Get which isn't mentioned in the above:
https://pub.dev/packages/get
Some of the above State management solutions (e.g. Provider) help you use Flutter-native state functionality correctly (because its rather complicated), while others completely avoid that and provide a framework separate from the Widget lifecycle (e.g. Get).
Thanks Baker for your response but not exactly what i meant
these are my two futures and my class
This is my future that returns the Location from her i need to pass the location id to the another future
Future<Location> Lastlocation() async {
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/location/getlastlocation?token=" + value;
http.Response response = await http.get(
myUrl,
headers: {
'Accept': 'application/json',
//'Authorization': 'token $value'
},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
return Location.fromJson(json.decode(response.body));
} else {
// then throw an exception.
throw Exception('Failed to load album');
}
}
This is my future that returns List of weather that depends on location id
Future<List> Getweither(String ID) async {
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/sensors/getDeviceByid/$ID?token=" + value;
http.Response response = await http.get(myUrl,
headers: {
'Accept': 'application/json',
});
print("myUrldevice :"+myUrl);
print("status :"+response.statusCode.toString());
return json.decode(response.body);
}
This is my class Location
// To parse this JSON data, do
//
// final location = locationFromJson(jsonString);
import 'dart:convert';
List<Location> locationFromJson(String str) => List<Location>.from(json.decode(str).map((x) => Location.fromJson(x)));
String locationToJson(List<Location> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Location {
Location({
this.automaticIrrigation,
this.coordinates,
this.createdDate,
this.sensorIds,
this.id,
this.siteName,
this.description,
this.v,
});
bool automaticIrrigation;
List<double> coordinates;
DateTime createdDate;
List<String> sensorIds;
String id;
String siteName;
String description;
int v;
factory Location.fromJson(Map<String, dynamic> json) => Location(
automaticIrrigation: json["AutomaticIrrigation"],
coordinates: List<double>.from(json["Coordinates"].map((x) => x.toDouble())),
createdDate: DateTime.parse(json["Created_date"]),
sensorIds: List<String>.from(json["Sensor_ids"].map((x) => x)),
id: json["_id"],
siteName: json["SiteName"],
description: json["Description"],
v: json["__v"],
);
Map<String, dynamic> toJson() => {
"AutomaticIrrigation": automaticIrrigation,
"Coordinates": List<dynamic>.from(coordinates.map((x) => x)),
"Created_date": createdDate.toIso8601String(),
"Sensor_ids": List<dynamic>.from(sensorIds.map((x) => x)),
"_id": id,
"SiteName": siteName,
"Description": description,
"__v": v,
};
}
and this is my homePage
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sidebar_animation/Services/DataHelpers.dart';
import 'package:sidebar_animation/sidebar/sidebar_layout.dart';
import '../bloc.navigation_bloc/navigation_bloc.dart';
import 'package:sidebar_animation/constants.dart';
import 'package:flutter/gestures.dart';
import 'package:sidebar_animation/bloc.navigation_bloc/navigation_bloc.dart';
class HomePage extends StatelessWidget with NavigationStates {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseHelper2 databaseHelper2 = new DatabaseHelper2();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: ListView(
FutureBuilder(
future: databaseHelper2.Getweither(location_id),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem !");
}
return snapshot.hasData
? ItemList(list: snapshot.data)
: Center(child: CircularProgressIndicator(
),
);
}
),
);
}
Widget _buildProgrammCard() {
return Container(
height: 90,
child:
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 4,
margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
child:
FutureBuilder(
// future: databaseHelper.getData(),
future: databaseHelper2.Lastlocation(),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("mochkla lenaa *");
}
return snapshot.hasData
? Text("Location :" +snapshot.data.siteName)
: Center(child: CircularProgressIndicator(
),
);
}
),
),
);
}
class ItemList extends StatelessWidget{
List list;
ItemList({this.list});
ScrollController _controller = new ScrollController();
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list == null ? 0 : list.length,
scrollDirection: Axis.horizontal,
itemExtent: 190.0,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 0, 14),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
item.storyUrl,
),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black26,
BlendMode.darken,
),
),
borderRadius: BorderRadius.circular(10.0),
color: Colors.grey,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"temp :",
style: TextStyle(color: Colors.white),
),
),
Padding(
padding: EdgeInsets.only(left: 24),
child:
Text(
list[i]['Weither']['Temp'],
),
),
],
),
),
);
},
),
}
}
Finaly i need to get the location id from the first future that return Location to the second future Getweither(String ID) .
I'm trying to apply the function compute to get the data from responseJSON.
I used quicktype.io to generate the function for this purpose.
This is what i have done so far:
final subCategoriesFolder = await compute(subCategoriesFolderFromJson, responseJSON);
Error 1:
error: The argument type 'List<List<SubCategoriesFolder>> Function(String)' can't be assigned to the parameter type 'FutureOr<List<List<SubCategoriesFolder>>> Function(dynamic)'. (argument_type_not_assignable at lib\src\ui\pages\subcategories.dart:84)
Error 2:
error: Couldn't infer type parameter 'Q'.
Tried to infer 'dynamic' for 'Q' which doesn't work:
Parameter 'callback' declared as 'FutureOr<R> Function(Q)'
but argument is 'List<List<SubCategoriesFolder>> Function(String)'.
The type 'dynamic' was inferred from:
Parameter 'message' declared as 'Q'
but argument is 'dynamic'.
Consider passing explicit type argument(s) to the generic.
(could_not_infer at lib\src\ui\pages\subcategories.dart:84)
Source:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/foundation.dart';
// import 'dart:isolate';
// import 'package:flutter/foundation.dart';
// import 'catwidget.dart';
// To parse this JSON data, do
//
// final subCategoriesFolder = subCategoriesFolderFromJson(jsonString);
List<List<SubCategoriesFolder>> subCategoriesFolderFromJson(String str) => List<List<SubCategoriesFolder>>.from(json.decode(str).map((x) => List<SubCategoriesFolder>.from(x.map((x) => SubCategoriesFolder.fromJson(x)))));
String subCategoriesFolderToJson(List<List<SubCategoriesFolder>> data) => json.encode(List<dynamic>.from(data.map((x) => List<dynamic>.from(x.map((x) => x.toJson())))));
class SubCategoriesFolder {
SubCategoriesFolder({
this.children,
this.title,
});
List<Child> children;
String title;
factory SubCategoriesFolder.fromJson(Map<String, dynamic> json) => SubCategoriesFolder(
children: List<Child>.from(json["children"].map((x) => Child.fromJson(x))),
title: json["title"],
);
Map<String, dynamic> toJson() => {
"children": List<dynamic>.from(children.map((x) => x.toJson())),
"title": title,
};
}
class Child {
Child({
this.title,
this.uri,
this.iconuri,
this.charset,
});
String title;
String uri;
String iconuri;
String charset;
factory Child.fromJson(Map<String, dynamic> json) => Child(
title: json["title"],
uri: json["uri"],
iconuri: json["iconuri"] == null ? null : json["iconuri"],
charset: json["charset"] == null ? null : json["charset"],
);
Map<String, dynamic> toJson() => {
"title": title,
"uri": uri,
"iconuri": iconuri == null ? null : iconuri,
"charset": charset == null ? null : charset,
};
}
class Subfolder extends StatefulWidget {
#override
SubFolder createState() => SubFolder();
}
class SubFolder extends State<Subfolder> {
Future _fetchJSON() async {
var response = await http.get(
"http://10.0.2.2:5000/subcategories",
headers: {"Accept": "application/json"},
);
if (response.statusCode == 200) {
String responseBody = response.body;
//var responseJSON = json.decode(responseBody);
var responseJSON = await compute(jsonDecode, responseBody);
//final subCategoriesFolder = subCategoriesFolderFromJson(responseJSON);
final subCategoriesFolder = await compute(subCategoriesFolderFromJson, responseJSON);
print(subCategoriesFolder.runtimeType);//THis is not being set, error here
print(subCategoriesFolder);
// name = responseJSON['name'];
// avatar = responseJSON['avatar_url'];
}
return true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.darkBackgroundGray,
body: FutureBuilder(
future: _fetchJSON(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return Center(child: Text('No data'));
} else
return Container(
child: Text(snapshot.data),
);
/*return GridView(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
children: <Widget>[
for(var item in snapshot.data ) singleCategoryTemp(item)
],
); */
} else if (snapshot.connectionState == snapshot.error) {
return Center(
child: Text('Error: Please try again later')); // error
} else {
return Center(child: CircularProgressIndicator()); // loading
}
}),
);
}
}
You can copy paste run full code below
You can use compute(subCategoriesFolderFromJson, responseBody)
I use nested ListView to show data, you check full code
code snippet
Future<List<List<SubCategoriesFolder>>> _fetchJSON() async {
...
if (response.statusCode == 200) {
String responseBody = response.body;
return compute(subCategoriesFolderFromJson, responseBody);
}
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
List<List<SubCategoriesFolder>> subCategoriesFolderFromJson(String str) =>
List<List<SubCategoriesFolder>>.from(json.decode(str).map((x) =>
List<SubCategoriesFolder>.from(
x.map((x) => SubCategoriesFolder.fromJson(x)))));
String subCategoriesFolderToJson(List<List<SubCategoriesFolder>> data) =>
json.encode(List<dynamic>.from(
data.map((x) => List<dynamic>.from(x.map((x) => x.toJson())))));
class SubCategoriesFolder {
SubCategoriesFolder({
this.children,
this.title,
});
List<Child> children;
String title;
factory SubCategoriesFolder.fromJson(Map<String, dynamic> json) =>
SubCategoriesFolder(
children:
List<Child>.from(json["children"].map((x) => Child.fromJson(x))),
title: json["title"],
);
Map<String, dynamic> toJson() => {
"children": List<dynamic>.from(children.map((x) => x.toJson())),
"title": title,
};
}
class Child {
Child({
this.title,
this.uri,
this.iconuri,
this.charset,
});
String title;
String uri;
String iconuri;
String charset;
factory Child.fromJson(Map<String, dynamic> json) => Child(
title: json["title"],
uri: json["uri"],
iconuri: json["iconuri"] == null ? null : json["iconuri"],
charset: json["charset"] == null ? null : json["charset"],
);
Map<String, dynamic> toJson() => {
"title": title,
"uri": uri,
"iconuri": iconuri == null ? null : iconuri,
"charset": charset == null ? null : charset,
};
}
class Subfolder extends StatefulWidget {
#override
SubFolder createState() => SubFolder();
}
class SubFolder extends State<Subfolder> {
Future<List<List<SubCategoriesFolder>>> _fetchJSON() async {
/*var response = await http.get(
"http://10.0.2.2:5000/subcategories",
headers: {"Accept": "application/json"},
);*/
String jsonString = '''
[[
{
"title" : "1",
"children" : [{
"title":"abc1",
"uri" : "def1",
"iconuri" : "123",
"charset" : "456"
},
{
"title":"abc2",
"uri" : "def1",
"iconuri" : "123",
"charset" : "456"
}]
},
{
"title" : "2",
"children" : [{
"title":"abc2",
"uri" : "def2",
"iconuri" : "789",
"charset" : "321"
}]
}
]]
''';
http.Response response = http.Response(jsonString, 200);
if (response.statusCode == 200) {
String responseBody = response.body;
return compute(subCategoriesFolderFromJson, responseBody);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
//backgroundColor: CupertinoColors.darkBackgroundGray,
body: FutureBuilder(
future: _fetchJSON(),
builder: (BuildContext context,
AsyncSnapshot<List<List<SubCategoriesFolder>>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
/* Expanded(
flex: 1, child: Text(snapshot.data[index].toString())),*/
Expanded(
flex: 2,
child: Container(
height: 50,
child: ListView.separated(
separatorBuilder:
(BuildContext context,
int index) {
return SizedBox(
width: 10,
);
},
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount:
snapshot.data[index].length,
itemBuilder: (context, index1) {
return Row(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 120,
child: Column(
children: <Widget>[
Text(
snapshot
.data[index]
[index1]
.title,
style: TextStyle(
color:
Colors.red),
),
Expanded(
child: ListView
.separated(
separatorBuilder:
(BuildContext
context,
int
index) {
return SizedBox(
width:
2,
);
},
shrinkWrap:
true,
scrollDirection:
Axis
.horizontal,
itemCount: snapshot
.data[
index]
[
index1]
.children
.length,
itemBuilder:
(context,
index2) {
return Row(
mainAxisAlignment: MainAxisAlignment
.spaceEvenly,
children: <
Widget>[
Text(snapshot.data[index][index1].children[index2].title,
style: TextStyle(color: Colors.red)),
]);
}),
),
],
),
)
]);
}),
),
)
])
],
);
});
}
}
}));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Subfolder(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}