APIs won't load when published on Google Playstore - android

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?

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

RedirectURI does not redirect for Mobile using WebAuthenticator.AuthenticateAsync

I'm attempting to use Xamarin.Essentials.WebAuthenticator to Authenticate using Azure AD which in turn should call back to my mobile app with an WebAuthenticatorResult. The process works up to the point where the Callback URI should callback into my app.
Command in Mobile App is fired calling the AuthenticateAsync method.
A new web browser opens on the mobile and I am prompted to enter my Microsoft Credentials
Sign in using my organisations user credentials.
Sign in successful.
Error message displays
The callback URI is never fired and the only option I have is to close the browser which then throws an exception in my app, this is expected when the process fails or the user closes the browser. The authentication result is never returned in my app.
What I expect to happen is once the authentication was successful the browser would redirect the browser to the RedirectURI and my mobile app would handle it.
Am I misunderstanding how this is supposed to work or have I misconfigured something?
WebAuthenticator called from my ViewModel
async Task<bool> SSOLogin()
{
ErrorMessage = string.Empty;
try
{
var authRequestUrl = new Uri("https://myapps.microsoft.com/signin/2borno2-1234-abcd-baba-42aaa70ab1da?tenantId=ab12ac17-4321-acbd-1234-72aae60ed1ca6");
var callbackUrl = new Uri("mobile://myapp");
var authResult = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = authRequestUrl,
CallbackUrl = callbackUrl,
PrefersEphemeralWebBrowserSession = true
});
var accessToken = authResult?.AccessToken;
return true;
}
catch(Exception e)
{
var msg = e.Message;
}
finally
{
IsBusy = false;
}
return false;
}
Activity to handle callback URI (mobile://myapp)
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionView },
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
DataScheme = "mobile")]
public class WebAuthenticationCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity
{
}
Azure AD Application Setup
N.B. the tenant Id and client ids are not the actual ones I'm using. I'm confident these are working as the browser in the mobile app takes me to the correct log in page and the error message displays the application name I've set up in Azure AD.
The sign-on URL error in your screenshot ("Undefined sign-on url for app") typically indicates that you need to set the Home Page URL under the app registration's branding tab.
From your other screenshot I am unable to tell whether you have selected the checkbox next to the mobile://myapp URL to make it the default. If you haven't done that and have multiple Redirect URIs added, the first one will get selected by default.
In the end I used the Microsoft.Identity.Client nuget package instead and configured the Mobile app platforms in Azure AD.
public class AzureADTokenService
{
// Replace these with your details
readonly string AndroidAppId = "YOUR.APP.NAME";
readonly string GeneratedAndroidAppSignature = "1234567890asdfghjkl";
readonly string iOSAppId = "YOUR.PACKAGE.BUNDLENAME";
readonly string AzureADClientID = "aaaaaaaa-1111-2222-3333-444444444444";
readonly string AzureADTenantID = "aaaaaaaa-1111-2222-3333-444444444444";
private IParentWindowLocatorService _parentWindowLocatorService;
readonly List<string> Scopes = new List<string> { "user.read" };
private IPublicClientApplication _pca;
string RedirectUri
{
get
{
if (DeviceInfo.Platform == DevicePlatform.Android)
return $"msauth://{AndroidAppId}/{GeneratedAndroidAppSignature}";
else if (DeviceInfo.Platform == DevicePlatform.iOS)
return $"msauth.{iOSAppId}://auth";
return string.Empty;
}
}
public AzureADTokenService(IParentWindowLocatorService parentWindowLocatorService)
{
_parentWindowLocatorService = parentWindowLocatorService;
// Create tha application
_pca = PublicClientApplicationBuilder.Create(AzureADClientID)
.WithIosKeychainSecurityGroup("com.microsoft.adalcache")
.WithRedirectUri(RedirectUri)
.WithAuthority(AzureCloudInstance.AzurePublic, AzureADTenantID)
.Build();
}
public async Task<string> SignInAsync()
{
AuthenticationResult result;
try
{
var accounts = await _pca.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
// Attempt to sign in silently using existing cached tokens
if (firstAccount != null)
result = await _pca.AcquireTokenSilent(Scopes, firstAccount).ExecuteAsync();
else
{
var builder = _pca.AcquireTokenInteractive(Scopes)
.WithUseEmbeddedWebView(true);
if (DeviceInfo.Platform == DevicePlatform.Android)
{
var windowLocatorService = _parentWindowLocatorService.GetCurrentParentWindow();
builder = builder.WithParentActivityOrWindow(windowLocatorService);
}
result = await builder.ExecuteAsync();
}
}
catch (MsalUiRequiredException)
{
var builder = _pca.AcquireTokenInteractive(Scopes)
.WithUseEmbeddedWebView(true);
if (DeviceInfo.Platform == DevicePlatform.Android)
{
var windowLocatorService = _parentWindowLocatorService.GetCurrentParentWindow();
builder = builder.WithParentActivityOrWindow(windowLocatorService);
}
result = await builder.ExecuteAsync();
}
if (result == null)
{
return null;
}
return result.AccessToken;
}
public async Task<bool> SignOutAsync()
{
try
{
var accounts = await _pca.GetAccountsAsync();
// Go through all accounts and remove them.
while (accounts.Any())
{
await _pca.RemoveAsync(accounts.FirstOrDefault());
accounts = await _pca.GetAccountsAsync();
}
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return false;
}
}
}

Flutter - Google Sign In - Android - Access Token always returns invalid credential

I'm implementing the Google Sign In option on a Flutter app, it works fine with iOS (emulator) but in Android (emulator) the credentials that I get are always marked invalid when I check them with Google.
Future<GoogleSignInAuthentication?> _validateGoogleToken(
GoogleSignInAccount? googleSignInAccount) async {
try {
if (googleSignInAccount?.authentication != null) {
final credentials = await googleSignInAccount?.authentication;
if (credentials != null && credentials.idToken != null) {
final urlRequest = Uri.parse(https://www.googleapis.com/oauth2/v3/userinfo);
final response = await http.get(urlRequest, headers: {
'Authorization': 'Bearer ${credentials.accessToken}',
});
final responseData = json.decode(response.body);
if (responseData['error'] == null &&
responseData['email_verified'] == true) {
this._profile = Profile.fromJson(json.decode(response.body));
_authenticationToken = credentials.idToken!;
return Future.value(credentials);
}
}
}
return Future.value(null);
} catch (error) {
return Future.value(null);
}
}
The Google OAuth always returns me that the credentials are invalid. However, in the case of iOS, the credential that my app receives always gets validated with no issues.
I noticed that for some reason the credential in Android, is always the same and it seems to be shorter/truncated than the one from iOS.
Any recommendations?

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

Flutter String is in Future null

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.

Categories

Resources