I've written a Class to handle network connection.
I need because my app works only in a specific environment so I want to check the wifi SSID.
I use the connectivity package to check if I'm connected to WIFI.
I want to use the network_info_plus package to read the wifi SSID name, but it throws this exception:
MissingPluginException (MissingPluginException(No implementation found for method getLocationServiceAuthorization on channel dev.fluttercommunity.plus/network_info))
My code is the following:
import 'dart:async';
import 'package:network_info_plus/network_info_plus.dart' as info;
import 'package:connectivity/connectivity.dart';
import 'package:flutter/services.dart';
enum ConnectivityStatus { Connected, NotConnected }
class ConnectivityService {
// Create our public controller
StreamController<ConnectivityStatus> connectionStatusController =
StreamController<ConnectivityStatus>();
ConnectivityService() {
// Subscribe to the connectivity Chanaged Steam
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
// Use Connectivity() here to gather more info if you need t
connectionStatusController.add(_getStatusFromResult(result));
});
}
// Convert from the third part enum to our own enum
ConnectivityStatus _getStatusFromResult(ConnectivityResult result) {
switch (result) {
case ConnectivityResult.mobile:
return ConnectivityStatus.NotConnected;
case ConnectivityResult.wifi:
_initNetworkInfo();
return ConnectivityStatus.Connected;
case ConnectivityResult.none:
return ConnectivityStatus.NotConnected;
default:
return ConnectivityStatus.NotConnected;
}
}
}
Future<String?> _initNetworkInfo() async {
String? wifiName;
final _networkInfo = info.NetworkInfo();
try {
var status = await _networkInfo.getLocationServiceAuthorization();
if (status == info.LocationAuthorizationStatus.notDetermined) {
status = await _networkInfo.requestLocationServiceAuthorization();
}
if (status == info.LocationAuthorizationStatus.authorizedAlways ||
status == info.LocationAuthorizationStatus.authorizedWhenInUse) {
wifiName = await _networkInfo.getWifiName();
} else {
wifiName = await _networkInfo.getWifiName();
}
} on PlatformException catch (e) {
print(e.toString());
wifiName = 'Failed to get Wifi Name';
}
return wifiName;
}
EDIT: I decided to create a class to handle the retrieval of wifi name:
...
case ConnectivityResult.wifi:
if (NetworkInfo().wifiName == 'WIFI_SSID_NAME') // !!! HANDLE ASYNC
return ConnectivityStatus.Connected;
else
return ConnectivityStatus.NotConnected;
...
class NetworkInfo {
String? wifiName;
NetworkInfo() {
_initNetworkInfo();
}
Future<void> _initNetworkInfo() async {
String? wifiName;
final _networkInfo = info.NetworkInfo();
try {
var status = await _networkInfo.getLocationServiceAuthorization();
if (status == info.LocationAuthorizationStatus.notDetermined) {
status = await _networkInfo.requestLocationServiceAuthorization();
}
if (status == info.LocationAuthorizationStatus.authorizedAlways ||
status == info.LocationAuthorizationStatus.authorizedWhenInUse) {
wifiName = await _networkInfo.getWifiName();
} else {
wifiName = await _networkInfo.getWifiName();
}
} on PlatformException catch (e) {
print(e.toString());
wifiName = 'Failed to get Wifi Name';
}
this.wifiName = wifiName;
}
}
The getLocationServiceAuthorization is only for iOS so if you are coding for Android too, you should split them into separate blocks:
final _networkInfo = info.NetworkInfo();
try {
//may throw on non-iOS platforms
var status = await _networkInfo.getLocationServiceAuthorization();
if (status == info.LocationAuthorizationStatus.notDetermined) {
status = await _networkInfo.requestLocationServiceAuthorization();
}
} on PlatformException catch (e) {
print(e.toString());
wifiName = 'Failed to verify authorization';
}
try {
wifiName = await _networkInfo.getWifiName();
} on PlatformException catch (e) {
print(e.toString());
wifiName = 'Failed to get Wifi Name';
}
Also inside /android/app/build.gradle you should insert:
shrinkResources false
minifyEnabled false
in the buildTypes/release block:
buildTypes {
release {
shrinkResources false
minifyEnabled false
Related
I have this function here it gets data from API and stores the data locally and I want to display a percent value like 20% ..... 100% in the splash screen while this function is being executed how can I do that?
_getCleaningDate(
{required AppGetCleaningDateEvent event,
required Emitter<AppState> emit}) async {
userDataModel = await SQLService.getUserData();
if (userDataModel == null) {
print('No User Data');
}
if (userDataModel != null) {
try {
emit(AppGetCleaningDateLoadingState());
propertiesModel = null;
offlineProperties = null;
bool hasNetwork = await Services.hasNetwork();
this.hasNetwork = hasNetwork;
if (hasNetwork) {
propertiesModel = await Services.getCleaningDate(
token: userDataModel!.token!,
cleanday: AvadaTheme.formateCleaningDate(event.dateTime));
await _syncAllData(event: AppSyncAllDataEvent(), emit: emit);
offlineProperties = await SQLService.getProperties(
AvadaTheme.formateCleaningDateFromStored(event.dateTime));
emit(AppGetCleaningDateSuccessState());
} else {
offlineProperties = await SQLService.getProperties(
AvadaTheme.formateCleaningDateFromStored(event.dateTime));
emit(AppGetCleaningDateSuccessState());
}
} on DioError catch (error) {
emit(AppGetCleaningDateErrorState(error.message));
}
}
}
am learning api integration with bloc, these exception is been thrown when data is trying to fetch, for loadingstate i assigned a progressindicator then after that state when trying to get data,these exeption is been thrown ,pls helpenter image description here
as per the console i tried to change the data type to from double to num, still same exception
try {
_emitters.add(emitter);
await handler(event as E, emitter);
} catch (error, stackTrace) {
onError(error, stackTrace);
rethrow;
} finally {
onDone();
}
networkfile.dart
class Repository {
List<FakeStore> collections = [];
Future<List<FakeStore>?> getdata() async {
String url = 'https://fakestoreapi.com/products';
final data = await http.Client().get(Uri.parse(url));
if (data.statusCode != 200) {
return null;
} else {
Iterable values = jsonDecode(data.body);
for (var value in values) {
FakeStore fakeStore = FakeStore.fromJson(value);
collections.add(fakeStore);
}
return collections;
}
}
}
bloc.dart
class FakestoreBloc extends Bloc<FakestoreEvent, FakestoreState> {
final Repository repository;
FakestoreBloc({required this.repository}) : super(FakestoreInitialstate()) {
on<FakestoreEvent>((event, emit) async {
if (event is StorelaodEvent) {
emit(Fakestorelaodingstate());
List<FakeStore>? apiresult = await repository.getdata();
if (apiresult == null) {
emit(FAkestoreErrorstate());
} else {
emit(Fakestoreloadedstate(apiresult: apiresult));
}
}
});
}
}
Future<void> checkConsent() async {
ConsentManager.requestConsentInfoUpdate(Constants.kAppodealKey);
ConsentManager.setConsentInfoUpdateListener(
(onConsentInfoUpdated, consent) {
print('PRINT: onConsentInfoUpdated $consent');
}, (onFailedToUpdateConsentInfo, error) {
print('PRINT: onFailedToUpdateConsentInfo $error');
});
var consentStatus = await ConsentManager.getConsentStatus();
print('PRINT: consentStatus $consentStatus');
if (consentStatus.toString() == 'Status.UNKNOWN') {
var shouldShow = await ConsentManager.shouldShowConsentDialog();
print('PRINT: shouldShow $shouldShow');
if (shouldShow.toString() == 'ShouldShow.TRUE') {
ConsentManager.loadConsentForm();
var isLoaded = await ConsentManager.consentFormIsLoaded();
print('PRINT: isLoaded $isLoaded');
if (isLoaded == true) {
ConsentManager.showAsDialogConsentForm();
ConsentManager.showAsActivityConsentForm();
ConsentManager.setConsentFormListener((onConsentFormLoaded) {
print('PRINT: onConsentFormLoaded');
}, (onConsentFormError, error) {
print('PRINT: onConsentFormError $error');
}, (onConsentFormOpened) {
print('PRINT: onConsentFormOpened');
}, (onConsentFormClosed, consent) {
print('PRINT: onConsentFormClosed $consent');
});
}
}
}
}
Constants.kAppodealKey is what I got in the Application key from here: https://app.appodeal.com/apps
But this is what I got:
I/flutter ( 9497): PRINT: consentStatus Status.UNKNOWN
I/flutter ( 9497): PRINT: shouldShow ShouldShow.UNKNOWN
In the documentation ShouldShow.UKNOWN means this: https://wiki.appodeal.com/en/android/get-started/data-protection/gdpr-and-ccpa
UNKNOWN The value is undefined(the requestConsentInfoUpdate method was not called).
But I have called it on the first line of my method. May I know why it is having a problem?
Important update 2022 07 18
less than 10 days after I posted this answer, a new Flutter Appodeal plugin (3.0.0) was made available which is claimed to make the handling of consent much easier.
Old answer
I was able to reproduce your issue with your code, my verified app key and those prints on the first attempt after app installation:
I/flutter (21755): PRINT: consentStatus Status.UNKNOWN
I/flutter (21755): PRINT: shouldShow ShouldShow.UNKNOWN
I/flutter (21755): PRINT: onConsentInfoUpdated {"createdAt":1655890242,"zone":"NONE","acceptedVendors":[],"iab":{"IABConsent_SubjectToGDPR":"0"},"updatedAt":1655890242,"status":"UNKNOWN"}
The main problem here is that you cannot rely on the end of the execution of a method call to consider its work completed. Even with await.
You have to either rely on retry and wait loops which I don't recommend as it can be intensive and it can fail easily.
Or you can rely on the listeners.
What I did, is a mix of both because listeners are not always available.
I would love to get some feedback on this proposal as I am not certain I interpreted correctly the AppoDeal documentation.
I call AdManager.init(); once the app is loaded. This may also work if called earlier.
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stack_appodeal_flutter/stack_appodeal_flutter.dart';
import 'dart:io' show Platform;
class AdManager {
static var consent = false;
static Status consentStatus = Status.UNKNOWN;
static int requestConsentInfoUpdateRetries = 20;
static int requestLoadConsentFormRetries = 20;
static int adType = Appodeal.INTERSTITIAL;
static String get appKey =>
Platform.isAndroid ?
"your android app key"
:
"your ios app key"
;
//app ids found here: https://app.appodeal.com/apps
static initializeAfterConsent() {
Appodeal.initialize(
appKey,
[
adType,
],
boolConsent: consent,
).whenComplete(() {
Appodeal.isLoaded(adType).then((bool isLoaded) {
if (!isLoaded) {
Appodeal.cache(adType).whenComplete(() {
debugPrint("Note: appodeal ad has been loaded");
});
}
else {
debugPrint("Note: appodeal ad was loaded");
}
});
});
}
static init(bool testing) async {
try {
await handleATT();
await Appodeal.setTesting(true);
await Appodeal.setLogLevel(Appodeal.LogLevelVerbose);
await Appodeal.disableNetwork("admob");
await Appodeal.setAutoCache(adType, true);
await Appodeal.setAutoCache(Appodeal.REWARDED_VIDEO, false);
await Appodeal.setAutoCache(Appodeal.BANNER, false);
await Appodeal.setAutoCache(Appodeal.MREC, false);
await Appodeal.setChildDirectedTreatment(false);
await Appodeal.setUseSafeArea(true);
await Appodeal.muteVideosIfCallsMuted(true);
await doTheConsentStuff();
}
catch (e, st) {
debugPrint("Error initializing ads: ${e.toString()} ${st.toString()}");
}
}
static Future<String> showInterstitialAd(
{Function doWhenComplete, int retries = 10}) async {
try {
Map<String, Function> callsForAd = {
"not initialized": () async {
return await Appodeal.isInitialized(adType);
},
"not loaded": () async {
return await Appodeal.isLoaded(adType);
},
"cannot show": () async {
return await Appodeal.canShow(adType);
},
"not shown": () async {
return await Appodeal.show(adType);
},
};
for (int i = 0; i < callsForAd.length; i++) {
if (!await callsForAd.values.toList()[i]()) {
String res = callsForAd.keys.toList()[i];
switch (res) {
case "not initialized":
await initializeAfterConsent();
break;
case "not loaded":
await Appodeal.cache(adType);
break;
case "cannot show":
case "not shown":
if (retries > 0) {
await Future.delayed(Duration(milliseconds: 100));
}
break;
default:
return (res);
break;
}
if (retries > 0) {
debugPrint(
"Warning from ads: " + res + "; with retries: $retries");
return (await showInterstitialAd(
doWhenComplete: doWhenComplete, retries: retries - 1));
}
return (res);
}
else {
if (doWhenComplete != null) {
doWhenComplete();
}
}
}
return (null);
}
catch (e, st) {
return ("${e.toString()} ${st.toString()}");
}
}
static retryMaybeConsentInfoUpdate() {
if (requestConsentInfoUpdateRetries > 0) {
Future.delayed(Duration(milliseconds: 2000)).whenComplete(() {
requestConsentInfoUpdateRetries--;
ConsentManager.requestConsentInfoUpdate(appKey);
});
}
else {
debugPrint(
"Error: exhausted all retries on retryMaybeConsentInfoUpdate, aborting consent handling");
initializeAfterConsent();
}
}
static retryMaybeLoadConsentForm() {
if (requestLoadConsentFormRetries > 0) {
Future.delayed(Duration(milliseconds: 2000)).whenComplete(() {
requestLoadConsentFormRetries--;
ConsentManager.loadConsentForm();
});
}
else {
debugPrint(
"Error: exhausted all retries on requestLoadConsentFormRetries, aborting consent handling");
initializeAfterConsent();
}
}
static doTheConsentStuff() async {
try {
ConsentManager.setConsentInfoUpdateListener(
(onConsentInfoUpdated, consent) {
debugPrint(
"Note: Appodeal consent onConsentInfoUpdated: $onConsentInfoUpdated : $consent");
if (onConsentInfoUpdated == "onConsentInfoUpdated") {
try {
Map<String, dynamic> result = jsonDecode(consent);
switch (result["status"]) {
case "UNKNOWN":
debugPrint("Note: got a consent info status of unknown");
updateConsentStatus().whenComplete(() {
getShouldShow().then((shouldShow) {
switch (shouldShow) {
case ShouldShow.TRUE:
ConsentManager.setConsentFormListener(
(onConsentFormLoaded) {
debugPrint(
"Note: consent form loaded: $onConsentFormLoaded");
if (Platform.isIOS) {
ConsentManager.showAsActivityConsentForm();
}
else {
ConsentManager.showAsDialogConsentForm();
}
},
(onConsentFormError, error) {
debugPrint(
"Error: consent form error: $onConsentFormError: $error");
if (error == "Nothing to load") {
updateConsentStatus().whenComplete(() {
initializeAfterConsent();
});
}
else {
retryMaybeLoadConsentForm();
}
},
(onConsentFormOpened) {
debugPrint(
"Note: consent form opened: $onConsentFormOpened");
},
(onConsentFormClosed, consent) {
debugPrint(
"Note: consent form closed: $onConsentFormClosed: $consent");
updateConsentStatus().whenComplete(() {
initializeAfterConsent();
});
}
);
ConsentManager.loadConsentForm();
break;
case ShouldShow.FALSE:
debugPrint("Note: no need to show consent form");
updateConsentStatus().whenComplete(() {
initializeAfterConsent();
});
break;
case ShouldShow.UNKNOWN:
retryMaybeConsentInfoUpdate();
break;
default:
debugPrint(
"Error: undefined consent shouldShow value, aborting consent handling");
initializeAfterConsent();
break;
}
});
});
break;
case "PARTLY_PERSONALIZED":
debugPrint(
"Note: got a consent info status of PARTLY_PERSONALIZED");
updateConsentStatus().whenComplete(() {
initializeAfterConsent();
});
break;
case "PERSONALIZED":
debugPrint(
"Note: got a consent info status of PERSONALIZED");
updateConsentStatus().whenComplete(() {
initializeAfterConsent();
});
break;
case "NON_PERSONALIZED":
debugPrint(
"Note: got a consent info status of NON_PERSONALIZED");
updateConsentStatus().whenComplete(() {
initializeAfterConsent();
});
break;
default:
debugPrint("Error: got an unknown consent info status");
retryMaybeConsentInfoUpdate();
break;
}
}
catch (e, st) {
debugPrint("Error reading onConsentInfoUpdated data");
retryMaybeConsentInfoUpdate();
}
}
},
(onFailedToUpdateConsentInfo, error) {
debugPrint(
"Note: Appodeal consent onFailedToUpdateConsentInfo: $onFailedToUpdateConsentInfo : $error with $requestConsentInfoUpdateRetries retries");
retryMaybeConsentInfoUpdate();
});
await ConsentManager.requestConsentInfoUpdate(appKey);
}
catch (e, st) {
debugPrint("Error: consent form general error: ${e.toString()} ${st
.toString()}");
}
}
static Future<void> updateConsentStatus() async {
consentStatus = await ConsentManager.getConsentStatus();
switch (consentStatus) {
case Status.UNKNOWN:
debugPrint("Warning: consent status is unknown");
consent = false;
break;
case Status.NON_PERSONALIZED:
case Status.PARTLY_PERSONALIZED:
consent = false;
break;
case Status.PERSONALIZED:
debugPrint("Note: consent status is known");
consent = true;
break;
default:
consent = false;
debugPrint("Warning: undefined consent status");
break;
}
}
static Future<ShouldShow> getShouldShow([int retries = 20]) async {
var shouldShow = await ConsentManager.shouldShowConsentDialog();
if (shouldShow == ShouldShow.UNKNOWN) {
if (retries > 0) {
await Future.delayed(Duration(milliseconds: 500));
return (getShouldShow(retries - 1));
}
}
return (shouldShow);
}
static Future<void> handleATT({int retries = 20}) async {
if (Platform.isIOS) {
Permission appTrackingTransparencyPermission = Permission
.appTrackingTransparency;
PermissionStatus aTTPermissionStatus = await appTrackingTransparencyPermission
.status;
if (aTTPermissionStatus == null ||
aTTPermissionStatus == PermissionStatus.denied) {
debugPrint("ATT is null or denied, requesting with retries: $retries");
aTTPermissionStatus = await appTrackingTransparencyPermission.request();
if (aTTPermissionStatus == null ||
aTTPermissionStatus == PermissionStatus.denied) {
if (retries > 0) {
await Future.delayed(Duration(milliseconds: 500));
await handleATT(retries: retries - 1);
}
else {
debugPrint(
"Warning: ATT permission request retries exhausted, aborting");
}
}
return;
}
debugPrint(
"ATT is either granted, permanently denied, limited or restricted, thus, not requesting");
}
}
}
With this code, I get this edited consent form:
Then, the following is shown when I request to display an ad:
So, to me, apart from the deprecation warnings, everything seems to work fine.
Edit 2022 06 23:
Running on iOS, I realized that the shouldShow answer is enough to know whether to show the consent form or not. Thus, the consent form is only displayed when my VPN is set to Europe or California.
Also for iOS, I added ATT handling.
I have made some other general improvements.
I do not work for Appodeal and I am not a legal expert. I just provide this code based on my personal understanding which may not lead to the right way to handle those ads and consent questions.
I was studying with an AWS tutorial however, that tutorial is not null safety.
I'v tried to convert it, but is showing:
LateInitializationError: Field '_credentials#26120019' has not been initialized.
I think that "late" modifier is not initializing the variables when I try to get the values in verifyCode
Pleease, how can I fix the code below
enum AuthFlowStatus {login, signUp, verification, session}
class AuthState {
final AuthFlowStatus? authFlowStatus;
AuthState({this.authFlowStatus});
}
class AuthService {
final authStateController = StreamController<AuthState>();
late AuthCredentials _credentials;
void showSignUp() {
final state = AuthState(authFlowStatus: AuthFlowStatus.signUp);
authStateController.add(state);
}
void showLogin() {
final state = AuthState(authFlowStatus: AuthFlowStatus.login);
authStateController.add(state);
}
void loginWithCredentials(AuthCredentials credentials) async {
try {
final result = await Amplify.Auth.signIn(
username: credentials.username, password: credentials.password,
);
if (result.isSignedIn) {
final state = AuthState(authFlowStatus: AuthFlowStatus.session);
authStateController.add(state);
} else {
print('User could not be signed in');
}
} on AuthException catch (authError) {
print('Could not login - ${authError}');
}
}
void signUpWithCredentials(SignUpCredentials credentials) async {
try {
Map<CognitoUserAttributeKey, String> userAttributes = {
CognitoUserAttributeKey.email: credentials.email,
};
final result = await Amplify.Auth.signUp(
username: credentials.username,
password: credentials.password,
options: CognitoSignUpOptions(
userAttributes: userAttributes
),
);
if (result.isSignUpComplete) {
loginWithCredentials(credentials);
} else {
this._credentials = credentials;
}
final state = AuthState(authFlowStatus: AuthFlowStatus.verification);
authStateController.add(state);
} on AmplifyException catch (authError) {
print('Failed ro sign up - ${authError}');
}
}
void verifyCode(String verificationCode) async {
try {
final result = await Amplify.Auth.confirmSignUp(
username: _credentials.username,
confirmationCode: verificationCode,
);
if (result.isSignUpComplete) {
loginWithCredentials(_credentials);
} else {
//not implemented yet
}
} on AuthException catch (authError) {
print('Could not verify code - ${authError}');
}
}
}
In my flutter app, I have a future that handles http requests and returns the decoded data. But I want to be able to send an error if the status code != 200 that can be gotten with the .catchError() handler.
Heres the future:
Future<List> getEvents(String customerID) async {
var response = await http.get(
Uri.encodeFull(...)
);
if (response.statusCode == 200){
return jsonDecode(response.body);
}else{
// I want to return error here
}
}
and when I call this function, I want to be able to get the error like:
getEvents(customerID)
.then(
...
).catchError(
(error) => print(error)
);
Throwing an error/exception:
You can use either return or throw to throw an error or an exception.
Using return:
Future<void> foo() async {
if (someCondition) {
return Future.error('FooError');
}
}
Using throw:
Future<void> bar() async {
if (someCondition) {
throw Exception('BarException');
}
}
Catching the error/exception:
You can use either catchError or try-catch block to catch the error or the exception.
Using catchError:
foo().catchError(print);
Using try-catch:
try {
await bar();
} catch (e) {
print(e);
}
You can use throw :
Future<List> getEvents(String customerID) async {
var response = await http.get(
Uri.encodeFull(...)
);
if (response.statusCode == 200){
return jsonDecode(response.body);
}else{
// I want to return error here
throw("some arbitrary error"); // error thrown
}
}
Another way to solve this is by using the dartz package.
An example of how to use it would look something similar like this
import 'package:dartz/dartz.dart';
abstract class Failure {}
class ServerFailure extends Failure {}
class ResultFailure extends Failure {
final int statusCode;
const ResultFailure({required this.statusCode});
}
FutureOr<Either<Failure, List>> getEvents(String customerID) async {
try {
final response = await http.get(
Uri.encodeFull(...)
);
if (response.statusCode == 200) {
return Right(jsonDecode(response.body));
} else {
return Left(ResultFailure(statusCode: response.statusCode));
}
}
catch (e) {
return Left(ServerFailure());
}
}
main() async {
final result = await getEvents('customerId');
result.fold(
(l) => print('Some failure occurred'),
(r) => print('Success')
);
}
You can return the error data like this if you want to read the error object:
response = await dio.post(endPoint, data: data).catchError((error) {
return error.response;
});
return response;
//POST
Future<String> post_firebase_async({String? path , required Product product}) async {
final Uri _url = path == null ? currentUrl: Uri.https(_baseUrl, '/$path');
print('Sending a POST request at $_url');
final response = await http.post(_url, body: jsonEncode(product.toJson()));
if(response.statusCode == 200){
final result = jsonDecode(response.body) as Map<String,dynamic>;
return result['name'];
}
else{
//throw HttpException(message: 'Failed with ${response.statusCode}');
return Future.error("This is the error", StackTrace.fromString("This is its trace"));
}
}
Here is how to call:
final result = await _firebase.post_firebase_async(product: dummyProduct).
catchError((err){
print('huhu $err');
});