How to prevent admob from working on background - android

I'm using this(admob_flutter) package to add Admob ads on my flutter app, recently I got a notification from google play that my app got removed and they send the following statement in my email:
Ads associated with your app must not interfere with other apps, ads,
or the operation of the device, including system or device buttons and
ports. This includes overlays, companion functionality, and widgetized
ad units. Ads must only be displayed within the app serving them.
After some research by that unclear confusing stressful email, I got that happened because the Interstitial Ad could still keep working if the app not active or be in the foreground or even after closing the app by the back button, not terminated.
So I have searched for how to listen to those events on flutter, and I did found the didChangeAppLifecycleState but I couldn't figure out how to implement it with my ad!!
Here's my code: ( didn't implement the widget tree in here as I guess not important )
AdmobInterstitial interstitialAd;
dynamic _state;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
interstitialAd = AdmobInterstitial(
adUnitId: 'xxxxxxxxxxxxxx',
listener: (AdmobAdEvent event, Map<String, dynamic> args) {
if (event == AdmobAdEvent.loaded) interstitialAd.show();
if (event == AdmobAdEvent.closed) interstitialAd.dispose();
if (event == AdmobAdEvent.failedToLoad) {
print("Error code: ${args['errorCode']}");
}
},
);
interstitialAd.load();
}
#override
void dispose() {
interstitialAd.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
setState(() {
_state = state;
});
switch(state){
case AppLifecycleState.paused:
print('paused state');
break;
case AppLifecycleState.resumed:
print('resumed state');
break;
case AppLifecycleState.inactive:
print('inactive state');
break;
default:
break;
}
}

Update the interstitialAd initialization code block with the following snippet:
interstitialAd = AdmobInterstitial(
adUnitId: 'xxxxxxxxxxxxxx',
listener: (AdmobAdEvent event, Map<String, dynamic> args) {
if (event == AdmobAdEvent.loaded) {
If (_state != AppLifecycleState.paused) {
interstitialAd.show();
}
}
if (event == AdmobAdEvent.closed) interstitialAd.dispose();
if (event == AdmobAdEvent.failedToLoad) {
print("Error code: ${args['errorCode']}");
}
},
);

Related

Flutter: `setState()` when screen off, or how to detect if screen is on?

TLDR: I have a problem that should be solved if there is answer to one of the below questions:
Is there a way I can detect if the screen is on e.g.
if (SCREEN IS ON) {
setState(() {}); // i.e. update displayed widgets
}
Is there a way to call setState() so that is finishes when the screen is off?
More details:
I have an app that plays audio. When the screen is on, the widgets update via setState() (e.g. to display the progress of the audio). Nonetheless it would be nice to let a user turn off their screen to save battery - however if I call setState() when the screen is off, it seems that the app waits for the screen to be turned back on before finishing the setState() code.
Hence I am hoping to determine if I can detect and only call setState() when the screen is on, alternatively can I call setState() when the screen is off so that is completes.
I use this for chatApp to track the app State. I wrap the MaterialApp. With some changes you could do what you want.
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'constants/credentials.dart';
import 'domain/repositories/my_user_repository.dart';
class AppLifeCycleManager extends StatefulWidget {
final Widget child;
final MyUserRepository myUserRepo;
const AppLifeCycleManager(
{Key? key, required this.child, required this.myUserRepo})
: super(key: key);
#override
_AppLifeCycleManagerState createState() => _AppLifeCycleManagerState();
}
class _AppLifeCycleManagerState extends State<AppLifeCycleManager>
with WidgetsBindingObserver {
#override
void initState() {
super.initState();
widget.myUserRepo.setOnlineCustomer();
WidgetsBinding.instance.addObserver(this);
// set the publishable key for Stripe - this is mandatory
Stripe.publishableKey = pkTest;
}
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.paused:
widget.myUserRepo.setInactiveCustomer();
break;
case AppLifecycleState.resumed:
widget.myUserRepo.setOnlineCustomer();
break;
case AppLifecycleState.inactive:
widget.myUserRepo.setInactiveCustomer();
break;
case AppLifecycleState.detached:
widget.myUserRepo.setInactiveCustomer();
break;
}
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void dispose() {
widget.myUserRepo.setInactiveCustomer();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
mounted might be what you are looking for. Alternatively, you could check the AppLifecycleState by using WidgetsBindingObserver.
I haven't tested this, but screen state in the pub might be what you're looking for.
Big thanks to #mario-francois I got the following to work for me:
import 'package:flutter/material.dart';
class MyPage extends StatefulWidget {
MyPage({Key? key}) : super(key: key);
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> with WidgetsBindingObserver {
bool screenOn = true;
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.paused:// screen off/navigate away
// this.screenOn = false;
break;
case AppLifecycleState.resumed: // screen on/navigate to
this.screenOn = true;
break;
case AppLifecycleState.inactive: // screen off/navigate away
this.screenOn = false;
break;
case AppLifecycleState.detached:
break;
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
Widget build(BuildContext context) {
...
}
}

How can I update a stream from within a StreamBuilder?

Thank you very much in advance for your help! Please let me know if the question is not clear i would be happy to add more details if needed.
I have a Finite State Machine that handles some audio recognition. This FSM is wrapped by a "manager" whose job is to handle the state transitions (processState, nextState). The FSM manager exposes a stream which is updated every time nextState is called
FSM/Manager layout
class FSM_Manager{
StreamController<RecognitionState> _stateStream =
StreamController<RecognitionState>();
Sink<RecognitionState> get _inState => _stateStream.sink;
Stream<RecognitionState> get outState => _stateStream.stream;
RecognitionState _currentState, _previousState;
void setState(RecognitionState state) {
_previousState = _currentState;
_currentState = state;
_addCurrentStateToStream();
}
void _addCurrentStateToStream() {
_inState.add(_currentState);
}
Future nextState() async {
_currentState.nextState(this);
}
Future processState(itemToRecognize) async {
await _currentState.processState(itemToRecognize);
}
}
abstract class BaseState {
RecognitionStateID get stateID; //enum with each state's ID
Future processState(itemToRecognize);
Future nextState(FSM_Manager manager);
}
class FSM_State1 implements BaseState{
bool isSuccess = false;
void processState(itemToRecognize) async {
isSuccess = await performRecognition(itemToRecognize);
}
void nextState (FSM_Manager fsmManager) {
if(isSuccess){
// go to next State
fsmManager.setState(NEXT_STATE);
} else {
//go to some other state
fsmManager.setState(SOME_OTHER_STATE);
}
}
}
class FSM_State2 implements BaseState{
bool isSuccess = false;
void processState(itemToRecognize) async {
isSuccess = await performRecognition(itemToRecognize);
}
void nextState () {
if(isSuccess){
// go to next State
} else {
// go to another State
}
}
}
I have a screen (Stateful Widget) which uses a StreamBuidler to listen to the "outState" stream in order to rebuild the screen with the information in the new State.
Stateful Widget
class _RecognitionScreenState extends State<RecognitionScreen> {
ItemToRecognize item;
var currStateiD;
FSM_Manager _fsmManager;
RecognitionScreenState(
ItemToRecognize item, FSM_Manager fsmManager) {
this.item = item;
this._fsmManager = fsmManager;
}
#override
Widget build(BuildContext context) {
String outString = '';
return StreamBuilder<RecognitionState>(
stream: _stateContext.outState,
builder: (BuildContext context,
AsyncSnapshot<RecognitionState> snapshot) {
if (snapshot.hasData) {
outString = snapshot.data.stateID.toString();
return Text(outString);
} else {
return Text('');
}
});
}
}
Now, I do not know where/how to call processState and nextState from, I cant do it from the build method so i Tried to use initState() and didUpdateWidget in the StatefulWidget so that the states are processed in the beginning and after every build respectively. This approach didnt work, the nextState method was never called. I feel like im missing something trivial but i just dont see where to call those functions from outside of the Stateful Widget in order to trigger a rebuild only after the state has changed
Thanks again for your help
EDIT
I apologize for the confusion,
I added the BaseState definition (just an abstract class with some method so that i dont forget to implement them)
the nextState method takes an FSM_Manager as a parameter and calls setState on success or failure and sets the next state
the States "implement" the BaseState class, they dont "extend" it
It's difficult for me to tell from your code because I don't know what your BaseState does but inside your builder - presumably from some event or callback - you would do:
inState.add(<--Some RecognitionState-->);
This would trigger the StreamBuilder to rebuild.
If everything else in your code is put together properly.

Upload to Firestore in background with Flutter

I am using christocracy's flutter_background_geolocation package to build a crowdsensing app. This app relies on the geofencing function of the aforementioned package quite heavily. In the main function, I have implemented a callback function that is as follows (partial code):
void _onGeofence(bg.GeofenceEvent event) async {
await showGeofenceNotification(flutterLocalNotificationsPlugin,
title: "Geofence", body: "$event", id: notification_id);
if (action == "ENTER") {
// update certain variables
BarometerService.startBarometerService();
BarometerService.floorChange.listen((floorChanges) {
// update floor
updateDatabase();
});
}
else if (action == "EXIT") {
// update certain variables
BarometerService.stopBarometerService();
}
updateDatabase();
setState(() {
// update UI
});
}
The code works perfectly when the app is open and in focus. However, when in background, the barometer service stops. The updateDatabase() function is also not carried out as my Firestore console doesn't get updated.
Here is the code for updating the database:
Future updateUserState(String matric, bool inLWN, bool inVaughan, String activity, int confidence, int floor) async {
return await userCollection.document(uid).setData({
'matric': matric,
'inLWN': inLWN,
'inVaughan': inVaughan,
'activity': activity,
'confidence': confidence,
'floor': floor,
});
}
And here is the code for BarometerService (which uses Flutter sensors plugin):
import 'package:sensors/sensors.dart';
static startBarometerService() {
Stream<BarometerEvent> barometer10Events = barometerEvents.throttle(Duration(seconds:PERIOD_SECONDS));
subscription = barometer10Events.listen(onBarometer);
streamController = new StreamController();
}
How do I make my services run even when app is closed or terminated? I have implemented the same code in my headless callback functions (except updating UI), but nothing besides updating my (local) variables and showing local flutter notifications is working.
Headless task for reference:
void headlessTask(bg.HeadlessEvent headlessEvent) async {
print('[BackgroundGeolocation HeadlessTask]: $headlessEvent');
switch(headlessEvent.name) {
case bg.Event.GEOFENCE:
bg.GeofenceEvent geofenceEvent = headlessEvent.event;
onHeadlessGeofence(geofenceEvent);
print('- [Headless] GeofenceEvent: $geofenceEvent');
break;
case bg.Event.ACTIVITYCHANGE:
bg.ActivityChangeEvent event = headlessEvent.event;
onHeadlessActivityChange(event);
print('- [Headless] ActivityChangeEvent: $event');
break;
}
}
onHeadlessGeofence is almost identical to the callback _onGeofence, besides the setState().
The full code can be found here

Flutter GoogleMap is blank after resuming from background

I'm experiencing the following issue: My Flutter app uses a GoogleMap. The map loads just fine initially. However, if I put the app into the background and resume a while later, the map stays blank. The Google logo still shows, like it happens when the API key isn't specified. My polygon overlay doesn't show up, either.
The behavior is not reliably repruducable. Sometimes, the map loads fine after the app had been in the background for hours, sometimes the map is blank after minutes. So far, I have only seen this behavior on Android.
There are no specific log outputs that indicate an error.
Any ideas how to fix/work around this?
I filed an issue with screenshot here: https://github.com/flutter/flutter/issues/40284
EDIT 1:
I was able to reproduce this with a GoogleMap as root widget and also without any polygon/feature overlay. Also, I found that wildly zooming in at some point 'reanimates' the map (suddenly the map becomes visible again). Is this maybe a known issue with the underlying Android Google Maps SDK?
EDIT 2:
I found that the map is still reacting (e.g. tap/gesture listeners still trigger). Also, the map isn't really empty, it just becomes translucent, so the screen displays whatever widget is behind the map.
I discovered that if you tap a marker or change the style the map re-renders
class TheWidgetThatHasTheMap with WidgetsBindingObserver {
//...your code
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
controller.setMapStyle("[]");
}
}
}
Not a solution to the core problem, but I was able to work around this bug by creating a fork of the plugins project and modifying GoogleMapController.java as follows:
#Override
public void onActivityResumed(Activity activity) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
return;
}
mapView.onResume();
// Workaround for https://github.com/flutter/flutter/issues/40284
// This apparently forces a re-render of the map.
if (googleMap != null) {
googleMap.setMapType(googleMap.getMapType());
}
}
Now, on every resume event, the map will be re-rendered.
I tried something & it seems to be working!
Step 01,
Implement WidgetsBindingObserver for related class's State class as follows,
i.e:
class MainScreenState extends State<MainScreen> with WidgetsBindingObserver {....
Step 02,
Override didChangeAppLifecycleState method
i.e:
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.inactive:
print('appLifeCycleState inactive');
break;
case AppLifecycleState.resumed:
print('appLifeCycleState resumed');
break;
case AppLifecycleState.paused:
print('appLifeCycleState paused');
break;
case AppLifecycleState.detached:
print('appLifeCycleState detached');
break;
}
}
Step 03
add this for init state
WidgetsBinding.instance!.addObserver(this);
Step 04
Step 4 should be as follows
//onMapCreated method
void onMapCreated(GoogleMapController controller) {
controller.setMapStyle(Utils.mapStyles);
_controller.complete(controller);
}
// lifecycle
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.inactive:
print('appLifeCycleState inactive');
break;
case AppLifecycleState.resumed:
**//Add These lines**
final GoogleMapController controller = await _controller.future;
onMapCreated(controller);
print('appLifeCycleState resumed');
break;
case AppLifecycleState.paused:
print('appLifeCycleState paused');
break;
case AppLifecycleState.detached:
print('appLifeCycleState detached');
break;
}
}
When dealing with a stateful widget, put the code below in your code as shown below
class MainScreenState extends State<MainScreen> with WidgetsBindingObserver
{....
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
... // Other codes here
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
mapController!.setMapStyle("[]");
}
}
}
Then you can add the code below in the state widget
Another simpler way to implement solution with setMapStyle within statefull widget of map widget. No need to change anything else:
Import flutter services:
import 'package:flutter/services.dart';
Code:
SystemChannels.lifecycle.setMessageHandler((msg) {
if (msg == AppLifecycleState.resumed.toString()) {
mapController.setMapStyle("[]");
}
});
"mapController" here is the instance of Google map controller you named somewhere in your code. In my case it is like this:
GoogleMapController _mapController;
GoogleMapController get mapController => _mapController;
if you facing this problem in 2022 also add this line above your class
class YourClass extends StatefulWidget with WidgetsBindingObserver
Completer<GoogleMapController> controller = Completer();
#override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
print('\n\ndidChangeAppLifecycleState');
if (state == AppLifecycleState.resumed) {
final GoogleMapController controller1 = await controller.future;
controller1.setMapStyle('[]');
}
}
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}
Another temporary fix that doesn't required forking the plugins, building, etc.
Add a didChangeAppLifecycleState implemented via WidgetsBindingObserver to your Widget and make the GoogleMap widget rebuild with a state change.
In my case the map threw a black screen when called setState, the below solution solved my problem.
return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child:SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: //Your GoogleMap here,
),
);

Flutter : trigger a change of screen on a websocket message

I'm currently working on a flutter app with a websocket.
I want to change the current screen with a Navigator.pushNamed(context, '/path') in the method that is triggered by the websocket on a new message. The problem is that I don't have any context in this method.
So how to change the current screen from this method ?
I dont know if I'm thinking in the wrong way or if I just don't understand something.
Here is the method that is triggered on a new websocket message, his class is not a widget. This object is contained in every screen I create but his variable _parent is always set to match the active screen.
onMessageReceived(data) {
print("new message: " + data + " !");
data = jsonDecode(data);
data.forEach((key, value) {
switch (key) {
case "state":
_parent.newState(value);
break;
}
});
}
Here is the method of the widget:
newState(state){
if(state == "start"){
Navigator.pushNamed(, "/path");
}
}
Thanks in advance
You can use a stream for that. You can register the websocket response, and when it returns you can add it to the stream, and then on your screen you use a StreamBuilder to subscribe to the stream and navigate when needed. Something like this:
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
// Create a stream to receive the values
var webSocketStream = new BehaviorSubject<String>();
onMessageReceived(data) {
print("new message: " + data + " !");
data = jsonDecode(data);
data.forEach((key, value) {
switch (key) {
case "state":
// Write to the stream
webSocketStream.add(value);
break;
}
});
}
class CheckWebSocket extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<String>(
// Subscribe to the stream
stream: webSocketStream,
builder: (context, snapshot) {
// When the stream changes navigate
if (snapshot.hasData && snapshot.data == "start") {
Navigator.pushNamed(context, "/path");
} else {
return Container();
}
}),
);
}
}
I used rxdart for that, because it makes dealing with streams simpler. I also recommend that you extract the code that reads the websocket to a separate class, I put everything in one file just to make it simpler to understand.

Categories

Resources