I know that is possible to disable by wrapping a widget with WillPopScope, but it's a pain do this with all widgets.
So, is there a way to Globally Disable Back Button in Flutter?
For Android, you can override onBackPressed in your MainActivity.java (that extends FlutterActivity in your app)
#Override
public void onBackPressed() {
// do nothing
// super.onBackPressed();
}
In Kotlin:
override fun onBackPressed() {
// do nothing
}
Related
After user press HOME button, I would like to display a password protection screen when user
Restore the Activity from recent list. Or,
Press the app icon, and restore the last "minimize" Activity.
But not during
When user launches the Activity.
When user rotates device, and the Activity went through configuration change.
When user launches "other Activity", and then close the "other Activity".
All the above 5 actions, will trigger Activity's onResume
But, I would like to display a password protection screen only for the first 2 actions, and avoid from doing anything in the last 3 actions.
May I know, how can I achieve so? Will LifecycleObserver able to assist me on this?
What I had tried on LifecycleObserver
public class NewGenericFragmentActivity extends AppCompatActivity {
private static class MyLifeCycleObserver implements DefaultLifecycleObserver {
#Override
public void onResume(LifecycleOwner owner) {
Log.i("CHEOK", "onResume(LifecycleOwner owner)");
}
#Override
public void onPause(LifecycleOwner owner) {
Log.i("CHEOK", "onPause(LifecycleOwner owner)");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getLifecycle().addObserver(new MyLifeCycleObserver());
For MyLifeCycleObserver's onResume and onPause, it will be trigger for Actions 1 to 5. It is not clear, how I can separate Action 1, 2 from 3, 4, 5.
Yes, LifecycleObserver can do this.
After adding the proper imports to your build.gradle file, create the following file in your project:
class SampleLifecycleListener : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
Log.d("SampleLifecycle", "Returning to foreground…")
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
Log.d("SampleLifecycle", "Moving to background…")
}
}
and in your app's custom Application class, add this line to onCreate():
ProcessLifecycleOwner.get().lifecycle.addObserver(SampleLifecycleListener())
Currently, I'm using androidx.lifecycle.DefaultLifecycleObserver, to execute some code when the app is "quit". How I define "quit" are
When HOME button is pressed and app is not visible on screen.
When BACK button is pressed (one time or multiple times) till the app is not visible on screen.
This DOES NOT include when you launch a child activity, and causes onPause of main activity being triggered.
This DOES NOT include configuration change like phone rotation
As such, I using the following way to capture "quit" event.
public class WeNoteApplication extends MultiDexApplication {
public static class AppLifecycleObserver implements DefaultLifecycleObserver {
#Override
public void onResume(LifecycleOwner owner) {
// Capture "launch" event when app is "launched"
}
#Override
public void onPause(LifecycleOwner owner) {
// Capture "quit" event when app is "quit"
}
}
private static final AppLifecycleObserver appLifecycleObserver = new AppLifecycleObserver();
private static WeNoteApplication me;
private SharedPreferences sharedPreferences;
#Override
public void onCreate() {
super.onCreate();
me = this;
initLifecycleObserver();
}
private void initLifecycleObserver() {
Lifecycle lifecycle = ProcessLifecycleOwner.get().getLifecycle();
lifecycle.removeObserver(appLifecycleObserver);
lifecycle.addObserver(appLifecycleObserver);
}
public static WeNoteApplication instance() {
return me;
}
}
This works well most of the time but all the time.
The following is the edge case which makes app "quit" event failed.
Assume there is a home screen widget for the app.
The following code is executed when a component of the home screen widget is being pressed.
Intent i = new Intent(context, NoteListAppWidgetConfigureFragmentActivity.class);
// Avoid crash in Android 6.
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
i.putExtra(NoteListAppWidgetConfigureFragmentActivity.INTENT_EXTRA_NOTE_LIST_CONFIG, noteListConfig);
context.startActivity(i);
Press back button to quit the home widget launched Activity.
Launch the app again by tapping app icon.
Press back button to quit the app. We notice that DefaultLifecycleObserver's onPause is not called.
Is there a reliable way for androidx.lifecycle.DefaultLifecycleObserver to detect app quit event?
This is because in our configuration activity for home screen widget, we're calling super.onPause twice. This makes DefaultLifecycleObserver goes hair-wire.
public class StickyNoteAppWidgetConfigureFragmentActivity extends AppCompatActivity {
#Override
public void onPause() {
super.onPause();
// ...
// ...
// ...
super.onPause();
}
}
We simply fix it by removing call of extra super.onPause().
public class StickyNoteAppWidgetConfigureFragmentActivity extends AppCompatActivity {
#Override
public void onPause() {
super.onPause();
// ...
// ...
// ...
}
}
I am newbie to Xamarin.Forms. I am trying to show a DisplayAlert() when NavigationBar back button is clicked. I have tried implementing according to this article. The problem is when I click on the button the popup doesn't come. I placed a breakpoint on OnOptionsItemSelected() method to see if its getting called or not, it doesn't. This is my MainActivity.cs
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
global::Xamarin.FormsMaps.Init(this, bundle);
LoadApplication(new App());
Android.Support.V7.Widget.Toolbar toolbar = this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
//Placed a debugger here
// check if the current item id is equals to the back button id
if (item.ItemId == 16908332)
{
// retrieve the current xamarin forms page instance
var currentpage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault() as NavBackButtonContentPage;
// check if the page has subscribed to the custom back button event
if (currentpage?.CustomBackButtonAction != null)
{
// invoke the Custom back button action
currentpage?.CustomBackButtonAction.Invoke();
// and disable the default back button action
return false;
}
// if its not subscribed then go ahead with the default back button action
return base.OnOptionsItemSelected(item);
}
else
{
// since its not the back button click, pass the event to the base
return base.OnOptionsItemSelected(item);
}
}
I am using it inside MasterDetailPage.
isaman kumara's comment from 2019-06-01 said the following:
The issue will be fixed when you add following lines to MainActivity
OnCreate method (after the LoadApplication(new App()); line)
Android.Support.V7.Widget.Toolbar toolbar
= this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
One of the responses said this:
This seems to no longer be working. SetSupportActionBar expects a
parameter of type AndroidX.AppCompat.Widget.Toolbar and it won't work
with Android.Support.V7.Widget.Toolbar.
I was able to get OnOptionsItemSelected working again in Xamarin Forms 4.8 by adding this to the MainActivity's OnCreate method after the LoadApplication line:
if (FindViewById(Resource.Id.toolbar) is AndroidX.AppCompat.Widget.Toolbar toolbar)
{
SetSupportActionBar(toolbar);
}
Apologies for not commenting directly to the earlier comment, but I don't have enough reputation points to do so.
I also had the same issue and found the solution. The issue is MainActivity was sub-classing from the FormsAppCompactActivity instead of old FormsApplicationActivity which was the previous parent class for MainActivity. So assume bug on new FormsAppCompactActivity
The issue will be fixed when you add following lines to MainActivity OnCreate method (after the LoadApplication(new App()); line)
Android.Support.V7.Widget.Toolbar toolbar
= this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
Referenced URL and thread as follows
https://forums.xamarin.com/discussion/comment/218663
https://theconfuzedsourcecode.wordpress.com/2017/03/02/formsappcompatactivity-is-not-calling-onoptionsitemselected-xamarin-android/
I know this is an old question, but you can track the OnPopViewAsync event through a NavigationRenderer like this:
[assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationRenderer))]
namespace YourApp.Droid
{
public class CustomNavigationRenderer : NavigationPageRenderer
{
public CustomNavigationRenderer(Context context) : base(context)
{
}
protected override async Task<bool> OnPopViewAsync(Page page, bool animated)
{
// Write your code here
}
}
}
Using this code, you can capture both events in the same place in Android, the hardware back button and the NavigationBar back button pressed.
Hope this helps
You probably haven't created an event for triggering.
In an contentpage you want to override the back button, try this:
this.CustomBackButtonAction = async () =>
{
var result = await this.DisplayAlert(null,
"Hey wait now! are you sure " +
"you want to go back?",
"Yes go back", "Nope");
if (result)
{
await Navigation.PopAsync(true);
}
};
You will then get the event and a popup asking if you really, really want to go back.
I want to prevent closing the app by pressing the hardware back button in xamarin forms on android.
I want, that you can navigate with the hardware back button in the app (what is working), but do not want to exit, when the first page in navigation stack is reached.
I tried to use the OnSleep event in xamarin forms, but here I can not cancel the exit.
I also tried catching the back button in android:
public override void OnBackPressed()
{
//base.OnBackPressed();
}
But when using xamarin forms, I do not know which page is currently showing. So I do not know if the navigation back is allowed or not
It works with evaluating the NavigationStack (when you use NavigationPage).
In my Activity, I override the OnBackPressed
public override void OnBackPressed()
{
if(App.Instance.DoBack)
{
base.OnBackPressed();
}
}
In my xamarin forms app (App.Instance (it is a singleton)), I will evaluate the NavigationStack of the current Page like this.
public bool DoBack
{
get
{
NavigationPage mainPage = MainPage as NavigationPage;
if (mainPage != null)
{
return mainPage.Navigation.NavigationStack.Count > 1;
}
return true;
}
}
When there is only one page left in the NavigationStack I will not call base.OnBackPressed, so that I will not close the App.
![test]
And here's what the code could look like for a Xamarin Forms MasterDetail page scenario...
public bool DoBack
{
get
{
MasterDetailPage mainPage = App.Current.MainPage as MasterDetailPage;
if (mainPage != null)
{
bool canDoBack = mainPage.Detail.Navigation.NavigationStack.Count > 1 || mainPage.IsPresented;
// we are on a top level page and the Master menu is NOT showing
if (!canDoBack)
{
// don't exit the app just show the Master menu page
mainPage.IsPresented = true;
return false;
}
else
{
return true;
}
}
return true;
}
}
Just give a blank call in the page where do you wanna prevent, like
protected override bool OnBackButtonPressed()
{
return true;
}
This will prevent the back button in XF-Droid.
Expanding Chris's answer as there is no App.Instance now and one cannot access App in a static manner within platform code.
1. App.xaml.cs in the Shared project
public bool DoBack
{
get
{
return MainPage.Navigation.NavigationStack.Count > 1;
}
}
2.MainActivity.cs in the Android project
Declare a variable in the class:
App app;
In OnCreate(Bundle bundle) change LoadApplication(new App()); to:
app = new App();
LoadApplication(app);
Override the OnBackPressed() method:
public override void OnBackPressed()
{
if (app.DoBack)
{
base.OnBackPressed();
}
}
Here is a solution that works on Android.
Introduce a counter in the application class and increment it with each OnStart and decrement it with each OnStop this way when the counter is 1 you'll know you are at your last activity.
Needless to say, use a base activity implementation so that you don't have to copy-past boiler plate code.
The solution proposed can work nicely, but I don't like "static property exposure" to solve problems. More over, I don't like the usage of "properties as methods" solutions, especially when a lot of logic is involved.
The main problem here how we can handle the OnBackButton() method from our Xamarin.Forms.Application class.
What about doing the same thing in a more elegant way?
First you need to extend the Xamarin.Forms.Application class like this:
namespace YourNameSpace
{
public class Application : Xamarin.Forms.Application
{
#region Public Methods
public bool HandleBackButton()
{
return OnBackPressed();
}
#endregion
#region Application Methods
protected virtual bool OnBackPressed()
{
return false;
}
#endregion
}
}
Your App implementation now will use this class as base class. Remember to modify your xaml and your xaml.cs accordingly:
<common:Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:common="clr-namespace:YourNameSpace"
x:Class="YourNameSpace.App">
</common:Application>
In your App.cs implementation you can now override the OnBackButton() method.
public partial class App : YourNameSpace.Application
{
#region Constructors
public App()
{
InitializeComponent();
}
#endregion
#region App Methods
protected override bool OnBackPressed()
{
// Handle when the back button is pressed
return false;
}
#endregion
}
Then you need to change a little your Activity class implementation.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
#region Constants and Fields
private App _app;
#endregion
#region Activity Methods
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
_app = new App();
LoadApplication(_app);
}
public override void OnBackPressed()
{
if(!_app.HandleBackButton())
base.OnBackPressed();
}
#endregion
}
You can use this nuget
chd.hwBackButtonManager nuget
github project
I have:
public override void OnBackPressed ()
{
base.OnBackPressed ();
}
How can I get previous state of my app?
Does Xamarin have such as
BackStackEntryCount, how can I use it?
For example I click MainMenu->Study->Categories->FirstCategory, if I click Back, I want to have Categories.
If the Categories activity precedes the Selected category in the activity stack and is not flagged for no history OnbackPressed without override of the base/super class will take you back to the categories class and hit the OnResume method.
If you loaded your Categories activity in OnCreate and dont do anything in OnResume then OnBackPressed should show the previous activity in the state you left it.
If you want it to do something else then override on OnBackPressed and flag Categories as no history.
Override OnBackPressed
public override void OnBackPressed()
{
//Do not call the base method
//base.Onbackpressed
//Do something else
*something else code*
//finish this activity unless you want to keep it for some reason
finish();
}
NoHistory
[Activity
(
NoHistory = true
)
]
Public Class Categories : Activity
public override void OnBackPressed ()
{
finish();
}//end onBackPressed()