When I use the .map method to return a List of type ARImage, I get the error "Expected a value of type 'List<ARImage>', but got one of type 'List<ARImage?>'. When I use .forEach, I am able to return the List with no problems.
Why does .map throw an error, but .forEach does not?
addARImages() async {
final List<MediaObjectEntity> mediaObjects = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => AssetSelectorAlt(),
),
);
if (mediaObjects.isNotEmpty) {
var tempList = <ARImage>[];
mediaObjects.forEach((mediaObject) {
tempList.add(ARImage(
imgRef: TourAsset(mediaObject.storageRef),
));
});
setState(() {
_arImages.addAll(tempList);
});
}
The following code throws the error: Expected a value of type 'List<ARImage>', but got one of type 'List<ARImage?>'
if (mediaObjects.isNotEmpty) {
final List<ARImage> arImages = mediaObjects.map((mediaObject) {
return ARImage(
imgRef: TourAsset(mediaObject.storageRef),
);
}).toList();
setState(() {
_arImages.addAll(arImages);
});
}
Related
Describe the bug
when executing the provider with ref.read or ref.watch the result is the same , it is stuck on the loading block , while testing the api in postman works fine , the funny thing is that the api call gets executed and whenever i print something inside it it appears in the console
To Reproduce
in presentation layer
onpressed:()=>ref
.read(getPatientProvider(
r.api_token))
.when(data: (data) {
data.fold(
(l) => print(
"something wrong happened"),
(r) async {
print(r.id);
print("hello");
patient.value = patient.value
.copyWith(
name: r.name,
aliid: r.id,
appointments: r
.patient_appointments,
fcmtoken: token);
ref.read(docexist(r.id)).when(
loading: () =>
print("loading"),
error: (error, _) =>
print(error),
data: (data) async {
print("heloo");
if (data.isEmpty) {
print(
"data is not empty");
} else {
return print(
"logged in normally");
}
});
});
}, error: (error, _) {
print(error);
}, loading: () {
print("object");
})
Provider with riverpod generator
#riverpod
Future<Either<ApiFailures, dynamic>> getPatient(
GetPatientRef ref, String token) async {
final patientProvider = ref.watch(patientRepositoryProvider);
return patientProvider.getInfo(token);
}
infrastructure layer
#override
Future<Either<ApiFailures, dynamic>> getInfo(String token) {
var dio = Dio();
final result = TaskEither<ApiFailures, PatientModel>(() async {
try {
final response = await dio.get(
"https://xxxxxxxx/GetInfo?api_token=$token");
if (response.data == null) {
return const Left(ApiFailures.notfound());
} else {
PatientModel patientModel =
PatientModel.fromJson(response.data["User"]);
return Right(patientModel);
}
} catch (err, st) {
final message = 'error ${err.runtimeType}]';
if (kDebugMode) log(message, error: err, stackTrace: st);
if (err is DioError) {
return Left(ApiFailures.fromDioError(error: err));
}
return const Left(ApiFailures.internalError());
}
});
return result.map((r) => r).run();
}
Expected behavior
it should get the data as always
Calling when inside a click handler such as onPressed as you did does not make sense.
"when" does not wait for the future to complete. It executes immediately based on the current status of the future.
Considering that when you call it, you just triggered the future, then the future at that time will always be in a loading state.
What you want is something like async/await, where you can wait until the completion of your future.
You could do that with:
onPressed: () async {
final value = await ref.read(provider.future);
}
I want to browse through the documents with for on Firestore and query whether there is a value equal to the e-mail address I entered with the controller structure. For this I followed a code like below. But it gives me the error mentioned in the title. What can I do about it?
My Code:
masterEmailGet() {
var _usersDoc = await FirebaseFirestore.instance.collection('Master').get();
for (var eleman in _usersDoc.docs) {
if (eleman.data()['masterEmail'] == _emailController.text) {
return true;
}
}
}
Error:
type 'Future<dynamic>' is not a subtype of type 'bool'
Called:
InkWell(
onTap: () {
if (masterEmailGet()) {
_auth.signIn(_emailController.text, _passController.text).then((value) {
return Navigator.push(context, MaterialPageRoute(builder: ((context) => BottomNavBar())));
});
}
},
I've done a lot of research on the internet but haven't found a working solution.
Since masterEmailGet returns a Future, you need to await for its call in if. Also add async in the function body masterEmailGet() async { /* */ }.
onTap: () async {
if (await masterEmailGet()) {
// ...
}
}
I have a class called TotalLabel() that gets a double value from another function:
Future<double> getValue(String type) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
double total = prefs.getDouble(type) ?? 0;
return total;
}
This is how I call the function to get the result:
TotalLabel(
typeOf: 'Hidrats de carboni',
subtitle: 'Range',
onPressed: () {},
fillBar:
getValue('hidratsDeCarboni') //gets result from function
)
The problem is that when I call the function passing the ID of the variable that I want to get, I have this error: "The argument type 'Future can't be assigned to the parameter type double".
Thanks.
getValue returns a Future<double> so you either await for it in the initState or use a FutureBuilder
FutureBuilder<double>(
future: getValue('hidratsDeCarboni'),
builder: (context, snapshot) {
if(snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator();
} else if(!snapshot.hasError && snapshot.hasData) {
return TotalLabel(
typeOf: 'Hidrats de carboni',
subtitle: 'Range',
onPressed: () {},
fillBar: snapshot.data!,
);
} else {
return const Text('error');
}
}
)
you are returning the value from a future function you need to use await to wait for a response then use that value.
Heres when the error acquired, I wanted to change screen if the process is success and stayed if the process failed
Status() {
String rawJson =
LG();
Map<String, dynamic> map = jsonDecode(rawJson);
String status = map["STATUS"];
if (status == "Success") {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => SPAL()),
(Route<dynamic> route) => false,
);
} else {
print("Failed");
}
}
here's where the it is executed
ButtonWidget(
text: 'Submit',
onClicked: () async {
if (_emailing.currentState.validate() &&
_passwd.currentState.validate()) {
Status();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', emailController.text);
}
},
),
Don't return Navigator.pushAndRemoveUntil() and add the build context as param: Status(context)
I have a menus collection on firestore and I want to perform a map operation on each document and return a new stream. So, instead of the Stream<QuerySnapShop>, I wanted Stream<VendorMenuItem>
Stream<VendorMenuItem> getAllVendorMenuItems(String vendorId) async* {
var collectionReference = fs.collection('restaurants').doc('$vendorId').collection("menus").snapshots();
collectionReference.map((event) {
print("mapping");
event.docs.forEach((element) {
return VendorMenuItem.fromMap(element.data());
});
});
}
and I am calling it within a build method just to test my approach, and I got nothing printed on the console, here is how I called it
#override
Widget build(BuildContext context) {
var fs = Provider.of<FireStoreDatabaseRoute>(context);
fs.getAllVendorMenuItems("ewP3B6XWNyqjM98GYYaq").listen((event) {
print("printing final result");
print(event.name);
});
Any clues? thank you
UPDATE:
I wasn't yielding anything, however the yield keyword didnt help
Stream<VendorMenuItem> getAllVendorMenuItems(String vendorId) async* {
var collectionReference = FirebaseFirestore.instance.collection('restaurants').doc('$vendorId').collection("menus").snapshots();
yield* collectionReference.map((event) => event.docs.map((e) => VendorMenuItem.fromMap(e.data())));
}
This is how you transform stream using the method you use.
Stream<List<VendorMenuItem>> getAllVendorMenuItems(String vendorId) async* {
var collectionReference =
FirebaseFirestore.instance.collection('Files').snapshots();
yield* collectionReference.map(
(event) => event.docs
.map(
(e) => VendorMenuItem.fromMap(e.data()),
)
.toList(), //Added to list to Match the type, other wise dart will throw an error something Like MappedList is not a sub type of List
);
}
This is a second way to achieve the same task using a stream controller.
Stream<List<VendorMenuItem>> getAllVendorMenuItems2(String vendorId) {
StreamController<List<VendorMenuItem>> controller =
StreamController<List<VendorMenuItem>>();
FirebaseFirestore.instance.collection("Files").snapshots().listen((event) {
controller.add(event.docs
.map(
(e) => VendorMenuItem.fromMap(e.data()),
)
.toList() //ToList To Match type with List
);
});
return controller.stream;
}
So the reason why it didn't work was I didnt realize the map function is only a middleware and therefore the async* is not required; here is an alternative to #Taha's solution
(without the use of a stream controller)
Stream<List<VendorMenuItem>> getAllVendorMenuItems(String vendorId) {
var snapshot = fs.collection('restaurants').doc(vendorId).collection('menus').snapshots();
return snapshot.map<List<VendorMenuItem>>((event) {
return event.docs.map((e) {
return VendorMenuItem.fromMap(e.data());
}).toList();
});
}