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
Related
I am trying to transfer my data from FireBase Collection to a map. Then, the map data will go into MultiSelectBottomSheetField.
Problems: I am getting "Instance of '_JsonQueryDocumentSnapshot'" instead of 'Home' for example.
I still not getting the list of context from collection into MultiSelectItem
Also, I have noticed that when I start the app, the MultiSelectionItem is empty. It is displaying "Instance of '_JsonQueryDocumentSnapshot'" when I display the main view a second time. I guess that a SetState is probably missing somewhere. But as I can not write set state in a constructor, I am puzzled.
This is driving me nuts as I do not find where the problem is coming from.
Many thanks for your help.
import 'package:flutter_swipe_action_cell/core/cell.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
import 'package:multi_select_flutter/bottom_sheet/multi_select_bottom_sheet_field.dart';
import 'package:multi_select_flutter/util/multi_select_item.dart';
String inboxTaskDisplayed='';
int nbRecord=0;
var taskSelectedID;
var taskDone;
//------TEST
class ContextExisting {
final int id;
final String name;
ContextExisting({
this.id,
this.name,
});
}
List<ContextExisting> _contexts = [];
List <ContextExisting>allMyContext=[];
TextEditingController passController = new TextEditingController();
//-----------------
var documentID;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
//_-----------------
class Inbox_Test_For_Chip_Trial extends StatefulWidget {
final String screenSelected;
final String titlePage;
Inbox_Test_For_Chip_Trial(Key key, {#required this.screenSelected, #required this.titlePage,}) : super(key: key);
#override
_Inbox_Test_For_Chip_TrialState createState() => _Inbox_Test_For_Chip_TrialState(screenSelected, titlePage);
}
class _Inbox_Test_For_Chip_TrialState extends State<Inbox_Test_For_Chip_Trial> {
GlobalKey<FormState> _inboxFormKey = GlobalKey<FormState>();
String screenSelected;
String titlePage;
_Inbox_Test_For_Chip_TrialState(this.screenSelected, this.titlePage,);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text(titlePage + ' ('+nbRecord.toString()+')'),
actions: <Widget>[
],
),
backgroundColor: Colors.white,
body: Container(
height: 250,
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
//FOR CONTEXT
Flexible(child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('contexts')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else {
var docs = snapshot.data.docs;
print('docs');
print (docs[2].id);
print(snapshot.data.docs.length);
int nbContext = snapshot.data.docs.length;
for (int i=0;i<nbContext; i++) {
_contexts.addAll([ContextExisting (id:i, name:snapshot.data.toString())]);
print(_contexts);
}
return Container(
height: MediaQuery.of(context).size.height * .78,
width: MediaQuery.of(context).size.width,
child: ListView(
children: snapshot.data.docs.map((document) {
return Wrap(
children: [Card(
child: SwipeActionCell(
key: ObjectKey(document['context_Name']),
trailingActions: <SwipeAction>[
],
child: ListTile(
trailing: IconButton(
icon: Icon(Icons.keyboard_arrow_right),
onPressed: () async {
taskSelectedID = FirebaseFirestore
.instance
.collection('Users')
.doc(
FirebaseAuth.instance.currentUser
.uid)
.collection('contexts')
.doc(document.id).toString();
}
),
leading: ConstrainedBox(
constraints: BoxConstraints(
minWidth: 30,
minHeight: 35,
maxWidth: 30,
maxHeight: 35,
),
//InkWell(child: Icon(Icons.check_box_outline_blank),
),
title: Text(
document['context_Name'],
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
),
]
);
}).toList(),
),
);
}
}),),
Column(children:[
TestWidgetContext(),
]), //MyHomePage())
],
),
),
//bottomNavigationBar: MyBottomAppBar(), //PersistentBottomNavBar(),
);
}
class TestWidgetContext extends StatefulWidget {
TestWidgetContext({Key key}) : super(key: key);
#override
_TestWidgetContextState createState() => _TestWidgetContextState();
}
class _TestWidgetContextState extends State<TestWidgetContext> {
List itemsContext;
List<ContextExisting> _selectedContext5 = [];
final _itemsContext = _contexts
.map((context) => MultiSelectItem<ContextExisting>(context, context.name))
.toList();
#override
void initState() {
_selectedContext5 = _contexts;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
MultiSelectBottomSheetField(
buttonText: Text("Contexts"),
onConfirm: (val2) {
},
items: _itemsContext,
// initialValue:
// _itemsContext,
),
],
);
}
}
Since the code has a lot of errors, I am writing down my findings.
allMyContext is a list and if you wish to add elements to it you should be wring it as
allMyContext.add(ContextExisting (id:i, name:doc.name));
instead of List<allMyContext> = ContextExisting (id:i, name:doc.name)
Insde the on pressed
taskSelectedID = (document.id).toString();
since you already have taken a document you don't have to query it again
final doc = FirebaseFirestore.instance.collection('users').doc('contexts').get();
and
FirebaseFirestore.instance.collection('Users').doc(FirebaseAuth.instance.currentUser.uid).collection('contexts').snapshots(),
These two are contradictory. Is contexts a collection or a document?
Problem solved.
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
//FOR CONTEXT
Flexible(child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('contexts')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
} else if (snapshot.hasData) {
// if connectionState is waiting
if (snapshot.connectionState == ConnectionState.waiting)
{
return Center(child: CircularProgressIndicator());
} else {
for (int i=0;i<snapshot.data.docs.length;i++){
DocumentSnapshot snap = snapshot.data.docs[i];
_contexts.add(snap['context_Name']);
}
}
}
// return widgets and use data
return Column(children:[
TestWidgetContext(),
]); //MyHomePage())
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->
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());
}
);
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) .
Here's my code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
//import 'package:location/location.dart';
class Home extends StatefulWidget {
Home({Key key, this.title}) : super(key: key);
final String title;
#override
_Home createState() => _Home();
}
class _Home extends State<Home> {
//declared var
String key = "ville";
String txt_ville = "PARIS";
List<String> villes = [];
GlobalKey<ScaffoldState> _drawerKey = GlobalKey();
//Location location;
//LocationData locationData;
//Stream<LocationData> stream;
#override
void initState() {
// TODO: implement initState
getData();
print(villes);
super.initState();
//location = new Location();
//getFirstLocation();
}
/*getFirstLocation() async {
try {
print("Position: ${locationData.latitude}/${locationData.longitude}");
}catch (e) {
print("Error when locating $e");
}
}*/
#override
Widget build(BuildContext context) {
return Scaffold(
key: _drawerKey,
drawer: Drawer(
child: new ListView.builder(
itemCount: villes.length,
itemBuilder: (BuildContext ctx, i) {
return new ListTile(title: new Text(villes[i]));
},
)
),
backgroundColor: Colors.amberAccent,
body: new Column(
children: <Widget>[
Expanded(
flex:4,
child: new Container(
width: double.infinity,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(bottomLeft: Radius.circular(120))
),
child: new Column(
children: <Widget>[
new Padding(
padding: EdgeInsets.only(top: 50),
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Padding(padding: EdgeInsets.only(left: 20),
child: new IconButton(icon: new Icon(Icons.dehaze, size: 30, color: Colors.black,), onPressed: () {
_drawerKey.currentState.openDrawer();
})),
new Padding(
padding: EdgeInsets.only(left: 30),
child: new Text("TheWeatherApp", style: new TextStyle(
fontSize: 40
),),
),
]),
)
],
),
)
),
Expanded(
flex: 1,
child: new Container(
),
)
],
)
);
}
Future<Null> addTown() async{
return showDialog(barrierDismissible: true, context: context, builder: (BuildContext buildcontext) {
return new SimpleDialog(
contentPadding: EdgeInsets.all(20.0),
title: Text("Add a town"),
children: <Widget>[
new RaisedButton(onPressed: () {
}, child: new Text("Auto locate me"),),
new TextField(
decoration: new InputDecoration(
labelText: "Ville"
),
onSubmitted: (s) {
setData(s);
Navigator.pop(context);
},
)
],
);
});
}
void getData() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
List<String> liste = await sharedPreferences.getStringList(key);
if (liste != null) {
setState(() {
villes = liste;
});
}
}
void setData(String str) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
villes.add(str);
await sharedPreferences.setStringList(key, villes);
getData();
}
void deleteData(String str) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
villes.remove(str);
await sharedPreferences.setStringList(key, villes);
}
}
I'm still a beginner on Flutter and I'm trying to understand why when I launch my application on the emulator and open my drawer with the iconbutton I get this error: Pastbinlink to error
If I deliberately create an error in the code like removing a parenthesis and do a hotreload, and I put the parenthesis back and do a hotreload again then my code works and the listview is displayed...
I have a theory that it's my getData function that initializes the variable villes that doesn't work...
I thank you in advance for any answer!
I can't comment, so I am writing it as an answer.
I haven't tried your code, and I am not sure if it will work, however,
You should try to use FutureBuilder
SharedPreferences.getInstance() is a future that will have value later on, and when you try to build your Drawer, it tries to access a value that doesn't exist.
Instead, if you try it like this. Your drawer will be created once your data is retrieved.
drawer: Drawer(
child: new FutureBuilder<List<String>>(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length == 0) {
return Center(child: Text("No data found."));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext ctx, i) {
return new ListTile(title: new Text(villes[i]));
},
)
}
} else if (snapshot.hasError) {
return checkError();
}
// By default, show a loading spinner.
return Center(child: CircularProgressIndicator());
})
),