A value of type "Future<Null>" can't be assigned to a variable of type "Data" - flutter

I'm trying figured out how to work with future for fetching data over internet. I was trying to write a simple code where I convert Future into "average" data but it doesn't work and I can't get why that's happend.
Here's my code
class Fetch {
Data getData () {
Data data;
data = fetch().then((value) {data = value;}); //here's I'm getting error
}
Future<Data> fetch() async {
int number = await Future.delayed(Duration(seconds: 1), () => 3);
return(Data.fromIt(number));
}
}
class Data{
int date;
Data({this.date});
factory Data.fromIt(int num) {
return Data(
date: num,
);
}
}
After I corrected this part of code error has gone but now getData() returns null instead of value:
Data getData () {
Data data;
fetch().then((value) {data = value;});
return data; //null
}
Thanks

You can make getData an async function, and use the await keyword. It is much easier to understand if you are new to async programming and Futures
Future<Data> getData () async {
final data = await fetch();
// Whatever you want to do with data
return data; // Dont forget to return it back to the caller
}

The data is defined as a Data object, while fetch() returns a Future<Data>, causing the error.
Data data;
data = fetch().then((value) {
data = value;
});
You can not transform a Future object to a synchronous object without awaiting it. You can do this at the UI to get the Future value:
FutureBuilder<Data>(
future: Fetch().fetch(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
Data data = snapshot.data;
return Text(data);
},
)

Related

How to extract values from onCall firebase function and load them in future builder

i have a onCall cloud function which is returning
resp.status(200).send(JSON.stringify(entities));
In my flutter app, i have created this future to get values from it.
Future<void> dataDriven(String filename) async {
HttpsCallable callable =
FirebaseFunctions.instance.httpsCallable('fruitsType');
final results = await callable;
final datE = results.call(<String, dynamic>{
'filename': 'filename',
});
final dataF = await datE.then((value) => value.data);
print (dataF);
}
It is successfully printing the response which is as per expectation. but my snapshot is always returning null. It is not even reaching hasData stage. Please help.
Response;
[{"name":"banana","type":"fruit","count":0,"color":"yellow"},{{"name":"apple","type":"fruit","count":2,"color":"red"}]
FutureBuilder(
future: dataDriven('fruits.txt'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: Text('An error has occurred!'),
);
} else {
final data = snapshot.data;
return Text(data.toString());
}
It looks like there are some issues that need to be fixed (See comments in code).
// Set the correct return type (not void because you are returning data)
Future<String> dataDriven(String filename) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('fruitsType');
// You can just call the function here with await
final result = await callable.call({
// Remove the quotes on the filename value
'filename': filename,
});
// Don't forget to return the data
return result;
}
I suggest reading up on the documentation about calling cloud functions from a flutter app and basic dart syntax.

type 'Future<List<Appointment>>' is not a subtype of type 'List<Appointment>' in type cast

The error should be clear but I'm unsure how to go around it.
Basically I have a Stream builder I'm calling every second by getData() method to update my SfCalendar with new data.
Stream<DataSource> getData() async* {
await Future.delayed(const Duration(seconds: 1)); //Mock delay
List<Appointment> appointments = foo() as List<Appointment>;
List<CalendarResource> resources = bar() as List<CalendarResource>;
DataSource data = DataSource(appointments, resources);
print("Fetched Data");
yield data;
}
But my appointments method foo() is of type Future<List> and not List.
Future<List<Appointment>> foo() async {
var url0 = Uri.https(
"uri",
"/profiles.json");
List<Appointment> appointments = [];
try {
final response = await dio.get(url0.toString());
//final Random random = Random();
//_colorCollection[random.nextInt(9)];
response.data.forEach((key, value) {
appointments.add(
Appointment(
id: int.parse(
value["id"],
),
startTime: DateTime.parse(value["startTime"]),
endTime: DateTime.parse(value["endTime"]),
),
);
});
} catch (error) {
print(error);
}
return appointments;
}
That is what the error should be telling, yes?
I tried removing the Future cast from foo() appointments but then I can't use async.
I also tried returning Future.value(appointments) but same error.
This is where I call my Stream in initState():
#override
void initState() {
super.initState();
print("Creating a sample stream...");
Stream<DataSource> stream = getData();
print("Created the stream");
stream.listen((data) {
print("DataReceived");
}, onDone: () {
print("Task Done");
}, onError: (error) {
print(error);
});
print("code controller is here");
}
Thank you, please help when possible
Just like JavaScript, async functions always return a Future. That's why you can't use async when you remove Future from the return type.
Since you're not waiting for that Future to resolve, you're actually trying to cast a Future to a List, which isn't a valid cast. All you should need to do is wait for the function to finish so it resolves to a List:
List<Appointment> appointments = await foo() as List<Appointment>;
and, since your return type is Future<List<Appointment>>, you don't actually need to cast the result.
List<Appointment> appointments = await foo();

When I am using the provider package in Flutter to load data from an API into a list it repeatedly calls the API, how do I fix it?

I am trying to lode data from an api call that retrieves a map, I am able to get the map from the api to display how I want it to, however it repeatedly calls the api meaning the list keeps on refreshing. Even though I have tried setting the listener to false, it works but I have to manually refresh the app for it to work?
Additional Info: Assigning and Retrieving Data
import 'package:http/http.dart' as http;
class Stores with ChangeNotifier {
var s_length;
Future<List<Store>> getStores(String storeCatName) async {
final queryParameters = {
"store_category_name": storeCatName,
};
try {
//TODO this is the issue - must fix.
final uri = Uri.http("url", 'url', queryParameters);
//print(uri);
final response = await http.get(uri);
//print(response.statusCode);
//print(response.body);
if (response.statusCode == 200) {
final List<Store> stores = storeFromJson(response.body);
_stores = stores;
//print(_stores);
print("lenght: ${_stores.length}");
Store store;
for(store in _stores) {
store.products = Products().products(store.storeId);
}
//check if this is correct
notifyListeners();
//return stores;
} else {
print("error1");
return List<Store>();
}
} catch (e) {
print(e.toString());
return List<Store>();
}
//notifyListeners();
print(_stores);
}
List<Store> get favoriteItems {
//return _stores.where((storeItem) => storeItem.isFavorite).toList();
}
bool isNotFull(){
if (_stores.isEmpty){
return true;
} else {
return false;
}
}
int get numberOfStores{
return s_length;
}
List<Store> _stores = [];
List<Store> stores (String storeCatName){
getStores(storeCatName);
//print("cpp; + $s_length");
//notifyListeners();
return _stores;
}
}
final storesProvider = Provider.of<Stores>(
context, listen: false
);
storesProvider.getStores(categoryName);
final providerStoreList = storesProvider.stores(category.storeCategoryName);
Additional Info: Builder for List:
child: ListView.builder(
itemCount: providerStoreList.length,
itemBuilder: (context, index) => ChangeNotifierProvider.value(
value: providerStoreList[index],
child: StoreItem(),
)));
If any additional information is required just let me know. Any help would be greatly appreciated.
Thanks
Use
listen: false;
var ourClient = Provider.of<CartBlock>(context, listen: false);
Setting the listener to false means that your widget won't build again when notifyListeners() is called.
So, that might not be the issue.
The only reason I can think of is calling the API again from the build method,
which might happen if you are using a ListView builder.
So, every time you might be scrolling the ListView your API would call again.

How do I load and parse a Json in isolate?

I'm trying to load my json and parse it in a new isolate. If I understood correctly this allows better performance of the application.
I charge my json and parse it after my future builder.
I would like to parse it before in an isolate and return them all but I can't display my json.
Is this the right method or it's useless ?
Future<Map<String, dynamic>> loadJson() async {
/*
* I load my Json without other isolate and I parse it in the future builder
*/
final myJson = await rootBundle.loadString('assets/jsons/myJson.json');
return {
'myJson' : myJson
};
}
Future<Map<String, dynamic>> loadJsonParsed() async {
/*
* I load my Json and parse it in other isolate
*/
final myJson = await rootBundle.loadString('assets/jsons/myJson.json');
var myJsonParsed = compute(_parseJson, myJson);
return {
'myJson' : myJsonParsed
};
}
Map<String, dynamic>_parseJson(String responseBody) {
final parsed = jsonDecode(responseBody);
return parsed;
}
// Some code ...
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (() => FocusScope.of(context).requestFocus(FocusNode())),
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: Future.wait([loadJson(), loadJsonParsed()]),
builder: (context, snapshot) {
if(snapshot.hasError) print(snapshot.error);
if (!snapshot.hasData) {
return LinearProgressIndicator();
}
print('BEGIN ================================');
var myJson = json.decode(snapshot.data[0]['myJson']);
print(myJson); // I see my json !
print(myJson.runtimeType); // _InternalLinkedHashMap<String, dynamic>
print('SEPARATION ================================');
var myJsonParsed = snapshot.data[1]['myJson'];
print(myJsonParsed); // Instance of 'Future<Map<String,dynamic>>' How to do ?
print(myJsonParsed.runtimeType); // Future<Map<String,dynamic>>
print('END ================================');
If the json is small, probably it's not worth to move the parsing to a different isolate as this operation also has a cost. Small / big is something subjetive but using common sense we could likely apply the correct approach.
If the json is big, definitively you're doing the correct thing as otherwise dart would execute the parsing on the main thread, what could cause a visible jank.
In relation to why compute is working wrong for you, this function returns a Future so instead of doing this
var myJsonParsed = compute(_parseJson, myJson);
You need to await the call to get the result, otherwise you'll be storing a future here 'myJson' : myJsonParsed and not the json map
var myJsonParsed = await compute(_parseJson, myJson);

Is it possible to filter a List with a function that returns Future?

I have a list List<Item> list and a function Future<bool> myFilter(Item).
Is there a way to filter my list using the Future returning function myFilter()?
The idea is to be able to do something like this:
final result = list.where((item) => myFilter(item)).toList();
But this is not possible since where expects bool and not Future<bool>
Since the iteration involves async operation, you need to use a Future to perform the iteration.
final result = <Item>[];
await Future.forEach(list, (Item item) async {
if (await myFilter(item)) {
result.add(item);
}
});
You can iterate over your collection and asynchronously map your value to the nullable version of itself. In asyncMap method of Stream class you can call async methods and get an unwrapped Future value downstream.
final filteredList = await Stream.fromIterable(list).asyncMap((item) async {
if (await myFilter(item)) {
return item;
} else {
return null;
}
}).where((item) => item != null).toList()
You can try bellow:
1, Convert List => Stream:
example:
Stream.fromIterable([12, 23, 45, 40])
2, Create Future List with this function
Future<List<int>> whereAsync(Stream<int> stream) async {
List<int> results = [];
await for (var data in stream) {
bool valid = await myFilter(data);
if (valid) {
results.add(data);
}
}
return results;
}
Here's a complete solution to create a whereAsync() extension function using ideas from the accepted answer above. No need to convert to streams.
extension IterableExtension<E> on Iterable<E> {
Future<Iterable<E>> whereAsync(Future<bool> Function(E element) test) async {
final result = <E>[];
await Future.forEach(this, (E item) async {
if (await test(item)) {
result.add(item);
}
});
return result;
}
}
You can now use it in fluent-style on any iterable type. (Assume the function validate() is an async function defined elsewhere):
final validItems = await [1, 2, 3]
.map((i) => 'Test $i')
.whereAsync((s) async => await validate(s));
Try this:
final result = turnOffTime.map((item) {
if(myFilter(item)) {
return item;
}
}).toList();