Flutter String is in Future null - android

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.

Related

Flutter BloC repeat an event again after dio request time out

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!));
}
}

Flutter: type 'Future<bool?>' is not a subtype of type 'FutureOr<bool>' in type cast

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;

APIs won't load when published on Google Playstore

So this is my first time publishing a Flutter app to the Google Play Store. I wanted to create something simple so I could learn the entire process. So I created a simple Trivia App.
When I run the app from my phone or emulator (Android or iPhone) the free API that I'm using here loads the categories without any issues.
The issue I'm having is when I publish the app to the Play Store, the API doesn't load and I don't even get an error message. Just a blank screen.
Here is the API service call in my app:
static Future<List<Category>> fetchCategories() async {
const url = "https://opentdb.com/api_category.php";
var response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
var jsonString = response.body;
final Map<String, dynamic> responseData = json.decode(jsonString);
var list = responseData['trivia_categories'] as List;
var _items = list.map((i) => Category.fromJson(i)).toList();
return _items;
} else {
return null;
}
} on Exception {
throw response.statusCode;
}
}
And this is the code in the controller that calls the API's method.
void fetchCategories() async {
// flag as loading.
isLoading(true);
try {
//get categories from API server
var categories = await ApiServices.fetchCategories();
if (categories != null) {
categories.forEach((Category categoryItem) async {
// adjust data accordingly
categoryItem.totalQuestions =
await fetchCategoryCount(categoryItem.id);
if (userDataController.data.read(categoryItem.id.toString()) !=
null) {
// get a list of answered questions from the device and count them
List<dynamic> correctAnswers =
userDataController.data.read(categoryItem.id.toString());
categoryItem.correctAnswers = correctAnswers.length;
} else {
categoryItem.correctAnswers = 0;
}
categoryTiles.add(categoryItem);
});
}
} on Exception catch (e) {
throw new Exception("An error occured fetching the data");
} finally {
isLoading(false);
}
}
Has anyone else ran into this issue?

Why can't I connect to an API when I generate the APK?

I have a problem with a login that I am doing, in my emulator it works correctly but when I generate the apk and test it on the phone, the API does not work.
I already assigned the Internet permission in the .xml and made tests to use Internet images and everything fine, so I discarded the permissions.
I do not know if it gives me an error because I use an http and not an https, or if someone has an idea of ​​what is happening here they tell me, I attach my code
Code:
void _login(BuildContext context) async {
if (!_loading) {
setState(() {
_loading = true;
});
}
//print(_username.value.text);
//print(_password.value.text);
var url = Uri.parse(
"http://taquillamedicina.mdgsystem.com/index.php?route=api/loginapp");
String var1 = 'vipmedico';
String var2 =
'xxxxx';
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'username': var1,
'password': var2,
'usernameapp': _username.value.text,
'passwordapp': _password.value.text
};
var jsonResponse = null;
var response = await http.post(url, body: data);
//print(response);
if (response.statusCode == 200) {
jsonResponse = json.decode(response.body);
// print(jsonResponse);
if (jsonResponse != null) {
setState(() {
_loading = false;
});
sharedPreferences.setString("client_id", jsonResponse['client_id']);
if (jsonResponse['error'] == '1') {
Navigator.of(context).pushReplacementNamed("/list");
}
}
} else {
setState(() {
_loading = false;
});
print(response.body);
}
}
To get over this, you will have add android:usesCleartextTraffic="true" to your AndroidManifest.XML file. But as advised, this isn't solving th eproblem, it's sweeping it under the carpet, but will get your task done for now.
After I upgrade the Flutter 2.0 my app is down and I add under Anroid>Src>Main>AndroidManifest.xml
android:usesCleartextTraffic="true"
API and app is worked

Release APK behaves differently than debug with type Future<dynamic> is not a subtype of FutureOr<Response> error

Im implementing a simple app with a login screen using bloc pattern. The app runs fine when running on debug mode on the android emulator or on my android phone from android studio. When I tried the release-apk with no changes on the code, I encounter the Future is not a subtype of FutureOr error.
Here's the method on the bloc with the logic:
void submit() async{
_loginStateController.sink.add(LoginStateLoading());
User user = User();
user.email = emailController.text;
user.password = passwordController.text;
String responseBody = await ApiClient.postUser(user, "/login").catchError( (error){
developer.log("error_while_posting_user: " + error.toString());
String errorMessage = error.toString();
if(errorMessage == "missing email" || errorMessage == "missing password"){
errorMessage = "Datos faltantes";
}
else if(errorMessage == "incorrect email or password"){
errorMessage = "Correo o contraseña incorrecta";
}
else{
errorMessage = "Error inesperado";
}
_loginStateController.sink.add( LoginStateError(errorMessage));
return;
});
Map<String, dynamic> responseMap = json.decode(responseBody);
Utils.saveLoginInfo(responseMap);
_loginStateController.sink.add(LoginStateReady());
}
Here's the code where the http request is being made:
static Future<String> postUser(User user, String path) async {
Map<String, String> headers = Map();
headers["content-type"] = "application/json";
Map<String, dynamic> bodyMap = user.toJson();
String body = jsonEncode(bodyMap);
try {
final response = await http.post(
API_ENDPOINT + path, headers: headers, body: body).catchError( (error) {
return Future.error(error);
}).timeout(Duration(milliseconds: 10000));
if (response == null) {
return Future.error("request error");
}
if (response.statusCode != 200) {
String errorMessage = jsonDecode(response.body)["message"];
return Future.error(errorMessage);
}
return response.body;
}
catch (Exception) {
return Future.error(Exception.toString());
}
return "";
}
I've tried flutter clean several times and still getting the error.
have you checked if you have internet permission in the AndroidManifest.xml?
Can you try adding this line in the AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />

Categories

Resources