I have a problem on my flutter app, when try to load a data from api using dio and this api is private so need to be connected to the same network, so to check everything is fine I tried to connect using mobile data that means dio connection won't success and return timeout, when I call that inside BLoC and use BloCBuilder to build UI depending on state bloc return loadingstate then return errorstate and try to do the event again and fail then repeat this over and over, I just want to avoid this and return error state only and stop listening on that event
void _loadAllSpecialities(
LoadAllSpecialities event, Emitter<DoctorsState> emit) async {
emit(
const DoctorsLoadingState(),
);
emit(const DoctorsLoadingState());
final result = await doctorService.getAllSpeciality(event.jwtToken);
print(result.toString());
//has no error and data loaded
if (result.item1 == null) {
final speicailities = result.item2;
emit(DoctorsSpecialitiesLoaded(specialities: speicailities));
} else {
//has error (error not null)
emit(DoctorErrorState(result.item1!));
}
}```
.
class DoctorService {
final List<DoctorSpeciality> specialities = [];
final options = Options(
responseType: ResponseType.json,
receiveTimeout: 2000,
sendTimeout: 2000,
);
final _dio = Dio();
Future<Tuple<String?, List<DoctorSpeciality>>> getAllSpeciality(
String jwtToken) async {
specialities.clear();
var tuple = Tuple<String?, List<DoctorSpeciality>>(null, []);
try {
final response = await _dio.get<List>(ApiVars.specialitiesEndPoint,
options:
options.copyWith(headers: {"Authorization": "Bearer $jwtToken"}));
if (response.statusCode == 200) {
//has no data
if (response.data == null) {
//set error 1
tuple.setNewValues('No data loaded', []);
//print it
log(tuple.item1 ?? '');
//return tuple with error and empty list
return tuple;
}
//has data then map it into list of specialities
response.data?.forEach((element) {
//convert json to speciality and add it to specialities list
specialities.add(DoctorSpeciality.fromJson(element));
});
//set error to null and list to specialites list
tuple.setNewValues(null, specialities);
return tuple;
} else {
//set error to error with the code and list to empty list
tuple.setNewValues('error occur with code ${response.statusCode}', []);
log(tuple.item1 ?? '');
return tuple;
}
} on DioError catch (error) {
//set error to error message and list to empty list
tuple.setNewValues("doc service ${error.message}", []);
log(tuple.item1 ?? '');
return tuple;
}
}
}
I tried add droppable, sequential and didn't work
on<LoadAllSpecialities>(_loadAllSpecialities, transformer: droppable());
I solved the problem by adding Future before function that called inside the handler function and await it to end as code in below
///constructor called super and pass initial state...
on<DoctorsEvent>(
(event, emit) async {
try {
if (event is LoadAllSpecialities) {
// * load all specialities of doctors from api ...
//add await here
await _loadAllSpecialities(event, emit);
}
} catch (error) {
emit(DoctorErrorState(error.toString()));
}
},
);
}
//add future here
Future<void> _loadAllSpecialities(
LoadAllSpecialities event, Emitter<DoctorsState> emit) async {
emit(
const DoctorsLoadingState(),
);
emit(const DoctorsLoadingState());
final result = await doctorService.getAllSpeciality(event.jwtToken);
//has no error and data loaded
if (result.item1 == null) {
final speicailities = result.item2;
emit(DoctorsSpecialitiesLoaded(specialities: speicailities));
} else {
//has error (error not null)
emit(DoctorErrorState(result.item1!));
}
}
Related
I'm new on the Flutter & working on the integration of POS printing machine in flutter & using the pos_printer_manager package.
It shows an error in the catch part of this package i.e.
type 'Future<bool?>' is not a subtype of type 'FutureOr<bool>' in type cast
& pointing out in this code
/// [writeBytes] let you write raw list int data into socket
#override
Future<ConnectionResponse> writeBytes(List<int> data,
{bool isDisconnect: true}) async {
try {
if (!isConnected) {
await connect();
}
if (Platform.isAndroid || Platform.isIOS) {
if ((await (bluetooth.isConnected as FutureOr<bool>))) {
Uint8List message = Uint8List.fromList(data);
PosPrinterManager.logger.warning("message.length ${message.length}");
await bluetooth.writeBytes(message);
if (isDisconnect) {
await disconnect();
}
return ConnectionResponse.success;
}
return ConnectionResponse.printerNotConnected;
}
// else if (Platform.isIOS) {
// // var services = (await fbdevice.discoverServices());
// // var service = services.firstWhere((e) => e.isPrimary);
// // var charactor =
// // service.characteristics.firstWhere((e) => e.properties.write);
// // await charactor?.write(data, withoutResponse: true);
// return ConnectionResponse.success;
// }
return ConnectionResponse.unsupport;
} catch (e) {
print("Error : $e");
return ConnectionResponse.unknown;
}
}
This is due to bluetooth.isConnected as FutureOr<bool>.
So any big difference between Future<bool?> & FutureOr<bool> ?
Basically I faced type casting error in the package & I need a solution to handle this on the package side & how to manage the optional.
Based on your findings typecast is not required, it requires a null check
change this it to
if (Platform.isAndroid || Platform.isIOS) {
bool? isConnected = await bluetooth.isConnected;
if (isConnected != null && isConnected!) {
Uint8List message = Uint8List.fromList(data);
PosPrinterManager.logger.warning("message.length ${message.length}");
await bluetooth.writeBytes(message);
if (isDisconnect) {
await disconnect();
}
return ConnectionResponse.success;
}
return ConnectionResponse.printerNotConnected;
}
Resolved it by a simple check:
bool? btConnected = await bluetooth.isConnected ?? false;
I have this Future and my code template like this:
Future getDevices() async {
stream.listen();
Timer.periodic(Duration(seconds:5), (timer) {
POST TO SERVER.then((value){
return Future.value(value);
});
});
}
I'm Listening to a Stream to scan for beacon devices.
I fill the yield named "beacons" in the Listen function of this Stream.
With Timer.periodic, I control the yield named "beacons" and perform POST operation. I want to return Future.value on "then" of this POST operation. But Future returns null without waiting for the result of POST operation.
I tried Completer like this:
Future getDevices() async {
final completer = Completer();
stream.listen();
Timer.periodic(Duration(seconds:5), (timer) {
POST TO SERVER.then((value){
return completer.complete(value);
});
});
return completer.future;
}
but this time I also got this error: "Unhandled Exception: Bad state: Future already completed"
EDIT:
I try to use Stream.asyncMap but same result when i did it.
Stream myStream = Stream.periodic(Duration(seconds: 5), (timer) async {
beacons.removeWhere((key, value) =>
DateTime.now().difference(value['lastUpdate']).inSeconds > 6);
if (beacons.isNotEmpty) {
bool processResult = false;
beacons.forEach((key, value) async {
if (int.parse(value['onlyEnterance'].toString()) == 1 &&
double.parse(value['distance'].toString()) <
double.parse(value['minDistance'].toString())) {
await userRepo
.createPayrollTracking(context, forEnter, value['dbId'])
.then((value) {
processResult = value;
if (value == true) {
stopMonitoring();
}
});
return await Future.value(processResult);
}
});
}
}).asyncMap((event) async => await event);
I want to use a string in this function that has the phone number of the device.
I get the phone number with this:
Future<void> initMobilNumberState() async {
if (!await MobileNumber.hasPhonePermission) {
await MobileNumber.requestPhonePermission;
return;
}
String mobileNumber = '';
try {
mobileNumber = await MobileNumber.mobileNumber;
_simCard = await MobileNumber.getSimCards;
} on PlatformException catch (e) {
debugPrint("Failed to get mobile number because of '${e.message}'");
}
if (!mounted) return;
setState(() {
var re = RegExp(r'\+[^]*');
_mobileNumber = mobileNumber.replaceRange(0, 3, ''.replaceAll(re, '+'));
});
}
My problem is that if I want to print _mobileNumber or use it in http.get I get null or a error with "Invalid Arguments"
Future<http.Response> _fetchSampleData() async {
String s = _mobileNumber;
print(s);
return http.get('http://test.php?TestPhone=' + _mobileNumber);
}
Future<void> getDataFromServer() async {
final response = await _fetchSampleData();
if (response.statusCode == 200) {
Map<String, dynamic> data = json.decode(response.body);
_list = data.values.toList();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
showAlertNoInternet(context);
print('Failed to load data from server');
}
}
Where is my mistake?
The problem is that I want to call the phone number before it has even been fetched. So this always resulted in null. I fixed this by fetching the number when I start the app with all the other data I need.
Notifylisteners is continuously running on a loop
Future<void> fetchAndSetMeal() async {
final List<Meal> loadedProducts = [];
var data = await DataBaseHelpers.getData();
if (data.documents == null) {
return;
}
List<DocumentSnapshot> list = data.documents;
list.forEach((document) async {
var url = await DataBaseHelpers.getImageUrl(document.documentID);
loadedProducts.insert(
0,
Meal(
id: document.documentID,
category: document.data['category'],
description: document.data['description'],
title: document.data['title'],
price: document.data['price'],
imgUrl: url));
});
await Future.delayed(Duration(milliseconds: 300));
print(loadedProducts[0]);
notifyListeners();
_items = loadedProducts;
}
}
//The print and network call statements are also running twice as opposed to //just once for every call. notifyListeners() is running on a loop;
You got your notifyListeners inside foreach loop. Move it outside
I have a relatively simple app (my first) that needs to display information that is retrieved from a GraphQL query and then stored in AsyncStorage. Everything works fine until you turnoff data/Wifi connections and relaunch the app - it will not load the same local data it did when networking is on. This is the same on a physical or emulated Android device.
There are no data calls except when the user initially sets their details. The app is built with version 2.7.1 of Expo & AWS Amplify. I have wasted several days with this final issue and gotten the same behaviour with Expo SecureStore & Amplify Cache and am loath to go down the route of learning and including Redux on such a simple app...
//import from react native not react
import { AsyncStorage } from 'react-native'
//calling a function as app loads
componentDidMount() {
this._loadInitialState();
}
//retrieving String from AsyncStorage
_loadInitialState = async () => {
try {
const policyNum = await AsyncStorage.getItem('policyNum')
//...
} catch {
//...
}
//setting state
if (policyNum != null && policyNum != undefined) {
this.setState({ policyNum: policyNum })
//...
}
//the original setting of the item
setPolicyDetails = async () => {
if (this.state.policyNum != null) {
const policyNumber = this.state.policyNum
this.state.policyNumSet = true
try {
await AsyncStorage.setItem('policyNum', policyNumber)
} catch (err) {
//...
}
}
}
You are using badly the fact of change the state.
Where you do this:
this.state.policyNumSet = true
You should change the state with the setState() function like this:
this.setState({ policyNumSet: true })
This was a conflict with an external API
Are you storing a string? asyncstorage only can store strings . try using JSON.stringify and JSON.parse
_loadInitialState = async () => {
try {
var policyNum = await AsyncStorage.getItem('policyNum')
policyNum = JSON.parse(policyNum) // converting to original
//...
} catch {
//...
}
//setting state
if (policyNum != null && policyNum != undefined) {
this.setState({ policyNum: policyNum })
//...
}
//the original setting of the item
setPolicyDetails = async () => {
if (this.state.policyNum != null) {
const policyNumber = this.state.policyNum
this.setState({ policyNumSet: true })
try {
var policyNumberString = JSON.stringify(policyNumber)
await AsyncStorage.setItem('policyNum', policyNumberString) //converting to string
} catch (err) {
//...
}
}
}