How to show this message to view? so that the user knows that the connection cannot connect to the server
this my socket connection code :
Socket.connect(pIp, port, timeout: const Duration(seconds: 5))
.then((socket) {
_channel = socket;
_isOn = true;
if (pWithdata.isNotEmpty) {
if (_channel != null) {
send(pWithdata);
// submitMessage("LOGIN#xXYjY`#Uf]bXcMM#FFHE#V30#V901");
}
}
socket.listen(
(onData) {
_isOn = true;
_listeners.forEach((Function callback) {
// callback(onData);
callback(String.fromCharCodes(onData).trim());
});
},
onDone: onDone,
onError: onError,
);
});
I want to perform an operation, but before that I want to check the connection status to the server whether it is still connected or not. If it is connected then perform the operation, if not then reconnect.
here is my code on button on pressed
Future<void> masuk() async {
if (_socketConnection == null) {
setState(() {
connect();
});
} else if (_subscription == null) {
setState(() {
connect();
});
} else {
_getId().then((id) {
setState(() {
deviceId = id;
print("Sambungan Masih terhubung");
send();
});
});
}
}
and here is code for connection
Future<void> connect() async {
var server = "10.0.2.2";
int port = 8000;
if (server.isNotEmpty) {
SocketClient socketClient = SocketClient(server, port);
_subscription = socketClient.connect().doOnCancel(() {
setState(() {
_subscription = null;
_socketConnection = null;
});
}).listen((connection) async {
print("listen:$connection");
setState(() {
_socketConnection = connection;
});
}, onError: (error) {
_result = "Connect Error:$error";
_subscription?.cancel();
setState(() {
_subscription = null;
_socketConnection = null;
});
}, cancelOnError: true);
}
setState(() {});
}
I have to restart the application first if I want to reconnect if the condition is "The server is off or restarted"
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 have added Facebook login to my flutter project and its logging in successfully but the problem is, The user is not able to enter the home screen of the application and stays back on the login screen. It is responding as Http status error [500].
Below is the code for Facebook login/authentication:
void doFacebookSignIn() async{
print("FaceBook Clicked");
try {
final result =
await FacebookAuth.i.login(permissions: ['email']);
if (result.status == LoginStatus.success) {
final userData = await FacebookAuth.i.getUserData();
print(userData);
hitFacebookApi(result.accessToken.token);
await FacebookAuth.i.logOut();
if (result.status == LoginStatus.cancelled) {
ToastUtils.showCustomToast(context, "cancelled", Colors.white , MyColors.primaryColor);
}
if (result.status == LoginStatus.failed) {
ToastUtils.showCustomToast(context, result.message, Colors.white , MyColors.primaryColor);
}
}
} catch (error) {
print(error);
}
}
Code for entering from Login to Home screen:
void hitFacebookApi(String token) {
CommonApis().logInWithFB(
{"token": "$token"}, CommonUtils.getLanguage(context) == "english")
.then((value) async{
if (value is Map) {
String fullToken = "Bearer ${value['token']}";
ApiUtils.headerWithToken.update("Authorization",(value)=> fullToken);
await userData.save(fullToken, "client");
await userService.getProfile();
Navigator.pushAndRemoveUntil(context,PageTransition(type: PageTransitionType.fade, child: ClientMain()), (Route<dynamic> route) => false);
} else {
ToastUtils.showCustomToast(
context, value, Colors.white, MyColors.primaryColor);
print("the cause "+value);
}
});
}
Code for API method:
Future<dynamic> logInWithFB(dynamic data ,bool isEnglish) async{
try{
final response= await Dio().post("${ApiUtils.BaseApiUrl}/auth/social/facebook",data: data,options: Options(headers: ApiUtils.headerForRegister ));
if(response.statusCode==200){
return {
"token" : response.data['token']
};
}
else{
return isEnglish?response.data['error']['en']:response.data['error']['ar'];
}
}on DioError catch(e) {
if(e.response !=null) {
return e.message;
}
}
}
I'm trying to create a Login Page for my app. I want it to say 'Logging In' then update if there's an issue.
When run, the app doesn't appear to do anything for the timeout duration. I'm guessing the 'Logging In' status is appearing microseconds before the AlertDialog shows as both the status and AlertDialog seem to appear at the same time. Once I click OK, the status updates to 'Retry Login'.
What I want is the 'Logging In' to appear as the text for my status box as soon as the button is pressed.
Button onPressed
ElevatedButton(
onPressed: () async {
setState((){
statusText = 'Logging In';
});
LoginNow();
TimeOutCheck();
},
child: Text('Log In'),
),
TimeOutCheck()
Future<void> TimeOutCheck() async{
hasTimedOut = false;
Duration timeoutDuration = GLOBAL_CONFIG.DEFAULT_TIMEOUT;
final endTime = DateTime.now().add(timeoutDuration);
while (!isComplete && DateTime.now().isBefore(endTime)){
sleep(Duration(milliseconds: 500));
print('Waiting ${DateTime.now()}');
}
hasTimedOut = !isComplete;
if (hasTimedOut){
await _showAlert('Timeout', 'Login Attempt Timed Out.\n\nPlease try again.');
setState(() {
_StatusText = 'Retry Login';
});
}
}
LoginNow()
Future<void> LoginNow(BuildContext context) async {
final String funcName = 'LoginNow';
bool doLogin = false;
_LoginForm.currentState!.validate();
setState(() {
if (LoginInfo['username'] == null || LoginInfo['username'] == ''){
_StatusText = 'Please Enter your User Name';
isComplete = true;
}
else if (LoginInfo['password'] == null || LoginInfo['password'] == '') {
_StatusText = 'Please Enter your Password';
isComplete = true;
}
else {
logger.wtf('[${className}.${funcName}]\t Setting Status to:\t "Logging In"\n');
//_StatusText = 'Logging In';
doLogin = true;
}
});
if (doLogin){
logger.d('[$className.$funcName\t Do Login: $doLogin');
logger.d('[$className.$funcName\t _StatusText Value: $_StatusText');
String URL = GetLoginRequest();
Uri uri = Uri.parse(URL);
var response = await http.get(uri);
isComplete = true;
// Do the rest of login stuff
}
I think what you need is to simply await the functions.
...
await LoginNow();
await TimeOutCheck();
// Make sure to update the status text at the end.
setState(() {
statusText = "Finished";
}
The problem is in the TimeOutCheck(). During the while, I'm using Sleep. Sleep is a synchronous function. Sleep is holding up all changes that may be completed by other async processes.
while (!isComplete && DateTime.now().isBefore(endTime)){
sleep(Duration(milliseconds: 500));
}
Instead of Sleep, I should've used Future.delayed(). Future.delayed() allows other threads to run while the thread it's called in waits for the delay.
while (!isComplete && DateTime.now().isBefore(endTime)){
await Future.delayed(Duration(milliseconds: 500));
}
The working code looks like:
Future<void> TimeOutCheck() async{
this.hasTimedOut = false;
Duration timeoutDuration = GLOBAL_CONFIG.DEFAULT_TIMEOUT;
final endTime = DateTime.now().add(timeoutDuration);
while (!(this.isComplete) && DateTime.now().isBefore(endTime)){
await Future.delayed(Duration(milliseconds: 500));
print('Waiting ${DateTime.now()}\n\t isComplete\t ${this.isComplete}');
}
this.hasTimedOut = !this.isComplete;
if (this.hasTimedOut && !this.isComplete){
await _showAlert('Timeout', 'Login Attempt Timed Out.\n\nPlease try again.');
setState(() {
this._StatusText = 'Retry Login';
});
}
}