I'm trying to get data from APIs, The data is being successfully fetched from the server but the issue is that when the data is provided to Listview it cant be shown. How can I show the data on Listview in a flutter/dart?
Following is the code for fetching data from API's
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
Map mapResponse = {};
Map dataResponse = {};
List listResponse = {} as List;
class teamapilab extends StatefulWidget {
#override
State<teamapilab> createState() => _teamapilab();
}
// ignore: camel_case_types
class _teamapilab extends State<teamapilab> {
Future team() async {
http.Response response;
response = await http
.get(Uri.parse("https://www.archmage.lk/api/v1/webapi/get-teams"));
if (response.statusCode == 200) {
setState(() {
//stringResponse = response.body;
mapResponse = json.decode(response.body);
listResponse = mapResponse['data'];
});
}
}
#override
void initState() {
team();
super.initState();
}
#override
Widget build(BuildContext context) {
// ignore: avoid_unnecessary_containers
return ListView.builder(
itemBuilder: (context, index) {
return Container(
child: Column(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
backgroundImage:
NetworkImage(listResponse[index]['member_image']),
),
),
Text(listResponse[index]['name'].toString()),
Text(listResponse[index]['status'].toString()),
]),
);
},
// ignore: unnecessary_null_comparison
itemCount: listResponse == null ? 0 : listResponse.length,
);
}
}
It is Not good to use async in initState instead using FutureBuilder. Try this:
class Teamapilab extends StatefulWidget {
#override
State<Teamapilab> createState() => _Teamapilab();
}
class _Teamapilab extends State<Teamapilab> {
Future<List> team() async {
http.Response response;
response = await http
.get(Uri.parse("https://www.archmage.lk/api/v1/webapi/get-teams"));
if (response.statusCode == 200) {
Map mapResponse = json.decode(response.body);
return mapResponse['data'] as List;
} else {
return [];
}
}
#override
void initState() {
// team();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List?>(
future: team(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List data = snapshot.data ?? [];
return ListView.builder(
itemBuilder: (context, index) {
return Column(children: [
Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
),
height: 50,
width: 50,
child: Image(
errorBuilder: (context, object, trace) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
),
);
},
image: NetworkImage(
data[index]['member_image'] ?? '',
)),
),
Text('${data[index]['name']}'),
Text('${data[index]['status']}'),
]);
},
itemCount: data.length,
);
}
}
}),
),
);
}
}
List listResponse = {} as List; should be replaced with List listResponse = [] as List;
In dart {} is empty map and [] is empty list.
You can use below code.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Map mapResponse = {};
Map dataResponse = {};
List listResponse = {} as List;
class teamapilab extends StatefulWidget {
#override
State<teamapilab> createState() => _teamapilab();
}
// ignore: camel_case_types
class _teamapilab extends State<teamapilab> {
bool dataLoaded = false;
Future team() async {
http.Response response;
response = await http
.get(Uri.parse("https://www.archmage.lk/api/v1/webapi/get-teams"));
if (response.statusCode == 200) {
setState(() {
//stringResponse = response.body;
mapResponse = json.decode(response.body);
print("DAata " + mapResponse.toString());
listResponse = mapResponse['data'];
print("DAata " + listResponse.toString());
dataLoaded = true;
setState(() {});
});
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) async{
await team();
});
}
#override
Widget build(BuildContext context) {
// ignore: avoid_unnecessary_containers
return Scaffold(
body: dataLoaded
? ListView.builder(
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Column(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
backgroundImage:
NetworkImage(listResponse[index]['member_image']),
),
),
Text(listResponse[index]['name'].toString()),
Text(listResponse[index]['status'].toString()),
]);
},
// ignore: unnecessary_null_comparison
itemCount: listResponse == null ? 0 : listResponse.length,
)
: const CircularProgressIndicator(),
);
}
}
Related
I have two widgets that both use the same Future to display CircularProgressIndicators and then show the widget once the future completes. But the CircularProgressIndicators do not animate, so they are just small squares. They animate the very first time after a full compile, but they do not animate ever again, not even after an app refresh. I have tried with other animating widgets to confirm, and they are indeed static:
#override
Widget build(BuildContext context) {
_mapLoadedNotifier = ref.read(mapLoadedProvider.notifier);
_mapLoaded = ref.watch(mapLoadedProvider);
theVm = ref.watch(searchPageVmProvider);
return Row(children: [ _mapOverlay(),_mapWidget(),]);
}
Widget _mapOverlay() {
return theVm.when(
data: (store) {
vm = store;
if (!vm!.hasSearched) {
vm!.isMapLoading = true;
}
vm!.ref = ref;
return SearchFormBuilder(
initState: vm!.initState,
model: Search(search: SearchByTermSuggestion('')),
builder: (context, formModel, child) {
return Container(
padding: PAD_M_TOP,
alignment: Alignment.topLeft,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: PAD_M),
width: sizingInfo.maxWidthS,
color: Colors.transparent,
child: VpCombinationAutocompleteField(
formControl: formModel.searchControl,
labelText: '',
hintText: 'Search',
widgetRef: vm!.ref!,
widgetOnSuggestionSelected:
(Suggestion suggestion) =>
suggestion.onSelected())));
});
},
error: (error, s) => Text(error.toString()),
loading: () {
return const Center(
child: SizedBox(
height: 30,
width: 30,
child: Center(child: CircularProgressIndicator())),
);
});
}
Widget _mapWidget() {
return FutureBuilder<SearchPageVm>(
future: ref.watch(searchPageVmProvider.future),
builder: ((context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: SizedBox(
height: 30,
width: 30,
child: Center(child: CircularProgressIndicator())),
);
}
vm = snapshot.data;
return StreamBuilder<dynamic>(
stream: vm!.map$,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
vm!.vpMap = snapshot.data;
if (!vm!.hasSearched) {
vm!.isMapLoading = true;
}
vm!.ref = ref;
}
return _googleMap(vm!);
});
}));
}
When I remove the StreamBuilder, they both animate correctly. This does not appear to be a riverPod issue, since I have tried plain Flutter FutureBuilders and it has the same issue. I've tried so many alternatives. The StreamBuilder stops the FutureBuilders CircularProgressIndicators from animating. Why?
It is the same issue as here:
Flutter CircularProgressIndicator() animation is not working inside Riverpod`s ref.watch().when
The provider:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:vepo/src/_common/extensions/vegan_item_establishments.dart';
import '../../../_common/constants/presentation/colors.dart';
import '../../../_common/enums/vegan_item_type.dart';
import '../../../_common/services/theme_mode.dart';
import '../../../_common/services/user.dart';
import '../../../application/local/form_capabilities.dart';
import '../../../application/local/map_data/map.dart';
import '../../../application/local/suggestion/search_by_term/search_by_term_suggestion.dart';
import '../../../application/search_params/vegan_item/vegan_item_search_params.dart';
import '../../../domain/vegan_item_establishment/_common/vegan_item_establishment.dart';
import 'search.dart';
import 'search_results_provider.dart';
final searchPageVmProvider = FutureProvider<SearchPageVm>((ref) async {
final userWithLocation = await ref.watch(userWithLocationProvider.future);
final vm = SearchPageVm(
userWithLocation: userWithLocation,
);
vm.search();
return vm;
});
class SearchPageVm {
bool hasSearched = false;
late GoogleMap? googleMapWidget;
bool isMapLoading = true;
VpMap? vpMap;
WidgetRef? ref;
ThemeMode? themeMode;
Color? statusBarColor;
String? searchText;
Stream<VpMap>? map$;
late final StreamController<VpMap> mapController =
StreamController.broadcast();
late UserWithLocation _userWithLocation;
Set<Marker> markers = <Marker>{};
Stream<String?>? searchTerm;
GoogleMapController? googleMapController;
SearchPageVm({required UserWithLocation userWithLocation}) {
map$ = mapController.stream;
_userWithLocation = userWithLocation;
}
#override
Search get model => Search(search: SearchByTermSuggestion(''));
Position get userPosition => _userWithLocation.userPosition;
/// Add an array of search results to the map
void addResults(Iterable<VgnItmEst> searchResults) {
mapController.add(VpMap(
position: _userWithLocation.userPosition,
markers: searchResults.markers));
_animateCamera(searchResults);
}
#override
void initState(BuildContext context, SearchForm formModel, [dynamic args]) {
if (!hasSearched) {
search();
hasSearched = true;
}
themeMode = ref?.read(themeModeServiceProvider);
statusBarColor = themeMode == ThemeMode.light
? BACKGROUND_COLOR
: DARK_THEME_BACKGROUND_COLOR_LIGHT;
}
/// Search for results and add them to the map
Future<List<VgnItmEst>?> search(
{String searchTerm = '', VgnItmType? category}) async {
final searchResults =
await _search(searchTerm: searchTerm, category: category);
addResults(searchResults ?? []);
return searchResults;
}
void _animateCamera(Iterable<VgnItmEst> searchResults) {
googleMapController?.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
searchResults.first.establishment!.location!.latitude,
searchResults.first.establishment!.location!.longitude),
zoom: 13)));
}
Future<List<VgnItmEst>?> _search(
{required String searchTerm, VgnItmType? category}) async {
final result = await ref?.watch(searchResultsProvider(VgnItmSearchParams(
searchTerm: searchTerm,
page: 1,
pageSize: 10,
vgnItmDiscriminators: category != null ? [category.name] : null))
.future);
return result;
}
}
final searchResultsProvider =
FutureProvider.family<List<VgnItmEst>, VgnItmSearchParams>(
(ref, searchParams) async {
List<VgnItmEst>? searchResults;
final allVgnItmsGooglePlacesRepo = ref.read(allVgnItmEstsRepoProvider);
searchResults = (await allVgnItmsGooglePlacesRepo.search(searchParams))?.data;
return searchResults!;
});
Just Make your own progress dialog for just do single change and change will reflect whole code.
class ProgressBar extends StatelessWidget {
final Color color;
ProgressBar({this.color = Colors.amber});
Widget build(BuildContext context) {
return Center(
child: SizedBox(
height: 40,
width: 40,
child: CircularProgressIndicator(color:color))
);}
The way you handle different state of your FutureBuilder is wrong, also when you use FutureBuilder, you should use ref.read, you need to change your _mapWidget to this:
Widget _mapWidget() {
return FutureBuilder<SearchPageVm>(
future: ref.read(searchPageVmProvider.future), // <=== change this
builder: (context, snapshot) { // <=== change this
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: SizedBox(
height: 30,
width: 30,
child: Center(child: CircularProgressIndicator())),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
vm = snapshot.data;
return StreamBuilder<dynamic>(
stream: vm!.map$,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
vm!.vpMap = snapshot.data;
if (!vm!.hasSearched) {
vm!.isMapLoading = true;
}
vm!.ref = ref;
}
return _googleMap(vm!);
});
}
}
});
}
I am trying to show data from api and while loading data , there should be shown a circularprogressindicator,
but when I start app..it directly showing data instead of circularprogressindicator
class _HomeScreenState extends State<HomeScreen> {
bool isloading = false;
var maplist ;
Future<void> fetchdata() async {
setState(() {
isloading=true;
});
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
setState(() {
isloading = false;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchdata();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
));
}
MyBody() {
return isloading==true ? Center(child: CircularProgressIndicator()) : ListView.builder(
itemCount: maplist.length,
itemBuilder: (context,index){
return Container(
padding: EdgeInsets.all(8.0),
child: Text(maplist[index]['title']));
});
}
}
It's actually working perfectly fine, it shows too fast because it is receiving data quickly(+ could be cache case).
If you like to have more delay you can add, future.delay which is unnecessary
Future<void> fetchdata() async {
setState(() {
isloading = true;
});
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
// get more delay
await Future.delayed(Duration(seconds: 2));
setState(() {
isloading = false;
});
}
A better of way of handling future method with FutureBuilder
Try the following code:
class _HomeScreenState extends State<HomeScreen> {
var maplist;
Future<void> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
setState(() {
maplist = json.decode(resp.body);
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
));
}
MyBody() {
return FutureBuilder(
future: fetchdata(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return ListView.builder(
itemCount: maplist.length,
itemBuilder: (context,index){
return Container(
padding: EdgeInsets.all(8.0),
child: Text(maplist[index]['title']));
});
}
}
}
You need to use FutureBuilder, it is not good to use async function in initState, try this:
FutureBuilder<List<Map<String,dynamic>>>(
future: fetchdata(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Map<String,dynamic>> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Text(data[index]['title']));
});
}
}
},
),
also you need to change your fetchdata to this:
Future<List<Map<String,dynamic>>> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
return json.decode(resp.body);
}
Try this one,set isloading default true
class _HomeScreenState extends State<HomeScreen> {
bool isloading = true;
var maplist ;
Future<void> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
setState(() {
isloading = false;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchdata();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
));
}
MyBody() {
return isloading ? Center(child: CircularProgressIndicator()) : ListView.builder(
itemCount: maplist.length,
itemBuilder: (context,index){
return Container(
padding: EdgeInsets.all(8.0),
child: Text(maplist[index]['title']));
});
}
}
You can use like that
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
bool isloading = false;
var maplist;
Future<void> fetchdata() async {
setState(() {
isloading = true;
});
var resp = await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
setState(() {
isloading = false;
});
}
#override
void initState() {
super.initState();
fetchdata();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: isloading ? const CircularProgressIndicator() : const MyBody(),
);
}
}
class MyBody extends StatelessWidget {
const MyBody({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
//Write your code here
);
}
}
i'm trying to set the login cookies to the rest of the get requests.
so i use http pachakge and store the login cookies with sharedPreferences and use it in the get request by adding an update function
but i have a problem that when i go to the page i get 400 response just refreshing the page and i get my data and response 200
is there any other solution for setting cookies in the others get requests headers ?
or is there a solution for my bug ?
codes images : [https://ibb.co/kD3dDc9]
[https://ibb.co/25p5fZr]
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:valomnia_reports/Screens/Superviseur%20Screens/SideBar.dart';
import 'user_model.dart';
class SellersPage extends StatefulWidget {
const SellersPage({Key? key}) : super(key: key);
#override
_SellersPage createState() => _SellersPage();
}
class _SellersPage extends State<SellersPage> {
String? finalEmail;
Future? _futureData;
String? rawCookie;
// ignore: must_call_super
void initState() {
getValidationData();
super.initState();
_futureData = getUserApi();
}
Future getValidationData() async {
final SharedPreferences sharedPreferences2 =
await SharedPreferences.getInstance();
var obtainedEmail2 = sharedPreferences2.getString("rawCookie");
setState(() {
rawCookie = obtainedEmail2;
print(rawCookie);
});
}
List<UserModel> userList = [];
Map<String, String> headers = {};
Future<List<UserModel>> getUserApi() async {
http.Response response = await http.get(
Uri.parse('https://valomnia.herokuapp.com/superviseur/getAllVendeurs'),
headers: headers);
response.headers['set-cookie'] = rawCookie!;
updateCookie(response);
var data = jsonDecode(response.body.toString());
String? cookies = response.headers['set-cookie'];
if (response.statusCode == 200) {
for (Map i in data) {
userList.add(UserModel.fromJson(i));
}
print("Cookie : $cookies");
print("200");
return userList;
} else {
print("400");
print(rawCookie);
print(cookies);
return userList;
}
}
void updateCookie(http.Response response) {
String? rawCookie2 = response.headers['set-cookie'];
if (rawCookie2 != null) {
int index = rawCookie2.indexOf(';');
headers['cookie'] =
(index == -1) ? rawCookie2 : rawCookie2.substring(0, index);
}
}
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
Future<Null> _refresh() {
return getUserApi().then((userList) {
setState(() => userList = userList);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: NavigationDrawerWidget(),
appBar: AppBar(
title: Text(
'Sellers list',
),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Column(
children: [
Expanded(
child: FutureBuilder(
future: _futureData,
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _refresh,
child: ListView.builder(
itemCount: userList.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
ReusbaleRow(
title: 'Id',
value:
snapshot.data![index].id.toString()),
ReusbaleRow(
title: 'Name',
value: snapshot.data![index].name
.toString()),
ReusbaleRow(
title: 'Username',
value: snapshot.data![index].username
.toString()),
ReusbaleRow(
title: 'DateCreated',
value: snapshot.data![index].email
.toString()),
],
),
),
);
}),
);
}
},
),
)
],
),
);
}
}
// ignore: must_be_immutable
class ReusbaleRow extends StatelessWidget {
String title, value;
ReusbaleRow({Key? key, required this.title, required this.value})
: super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title),
Text(value),
],
),
);
}
} ```
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) .