How do you trigger a native Android back button press from inside a React Native button that is handled somewhere else in the application.
For example,
<Pressable onPress={nativeGoBack} />
The event is then handled in another component using the BackHandler API.
BackHandler.addEventListener('hardwareBackPress', () => {
// Perform action
});
NB: This is not a React Navigation specific question (So navigation.goBack() is not a solution).
If you want to communicate from react native to adnroid, you should use Native modules.
See documentation https://reactnative.dev/docs/native-modules-android
In short:
Create a native module which handle backbutton from specific activity
class BackPressReactModule(reactContext: ReactApplicationContext?) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "BackPressReactModule"
}
#ReactMethod
fun goBack() {
val context = reactApplicationContext
val currentActivity = context.currentActivity as Activity
currentActivity.onBackPressed()
}
}
Register BackPressReactModule into your packages in ReactNativeHost. (See documentation)
After successfully exposing module, you can call it from javascript like this:
import {NativeModules} from 'react-native';
const {BackPressReactModule} = NativeModules;
BackPressReactModule.goBack();
Related
I want my flutter app to get a notification every time the android system back button is pressed, and furthermore, I want to otherwise disable the back button. So this is the code I have on the kotlin side of things:
package com.example.ui
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
override fun onBackPressed() {
// send message to flutter that the system back button was pressed
val flutterEngine = getFlutterEngine()
if (flutterEngine != null) {
MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, "backButtonChannel").invokeMethod("backButtonPressed", null)
}
}
}
and here's my flutter app main function:
Future<void> backButtonPressed() async {
// Code to run when the back button is pressed
print('PRESSED!');
}
const platform = MethodChannel('com.example.ui/back_button');
void main() {
WidgetsFlutterBinding.ensureInitialized();
platform.setMethodCallHandler((call) async {
if (call.method == "backButtonPressed") {
return backButtonPressed();
}
});
return runApp(MyApp());
}
Unfortunately it seems to have no effect, are you able to see why?
You should make a call by using registered MethodChannel channel
Your kotlin code should be like
MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, "com.example.ui/back_button").invokeMethod("backButtonPressed", null)
I suggest to use WillPopScope widget to handle back press from dart code
I have an application in ionic and I need to customize the redirection with the Android back button. But on several occasions the function I have is not executed correctly or it executes other page routing functions or previous components. Also sometimes it is executed two or more times.
I have tried with unsubscribe in ionViewWillEnter, DidEnter, ngOnDestroy
If I use this function in contructor method, this action replies in all pages
constructor(
private _router: Router,
private _navController: NavController,
private _userService: UserService,
private _alertService: AlertService,
private translate: TranslateService,
private platform: Platform
) {}
ngOnInit() {
this.getCashInfo();
// this.stockId = url[url.length - 1];
}
ionViewDidEnter() {
this.subscriptionBack = this.platform.backButton.subscribe(() => {
this.goBack();
this._router.navigate(['tabs/tabs/trade']);
});
}
ionViewDidLeave() {
this.subscriptionBack.unsubscribe();
}
I have an app with two activities, a splash screen activity and another that contains the main navgraph for the app.
I was wanting to utilise the new deep linking handling that comes with the Android Navigation Component. However following the Google tutorial, the Splash screen is skipped when clicking the link and dives straight into the navgraph for the app.
Is there any way to use these new features, whilst still forcing the splash screen to be shown first before navigating to the correct part of the app?
Posting the solution that I came to here in case anyone else has the same requirements.
It was actually pretty simple in the end! In the splash screen activity, catch the pendingDynamicLinkData as below:
private fun decideNextDestination() {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(this) { pendingDynamicLinkData ->
val deepLink = pendingDynamicLinkData?.link
if (deepLink == null) navigateToMain() else deepLinkToDestination(deepLink)
}
.addOnFailureListener(this) { navigateToMain() }
}
Then in the deepLinkToDestination method, build an intent and add a bundle with the deeplink URI to pass along (Android Uri implements parcelable so can pass with no issues):
private fun deepLinkToDestination(deepLink: Uri) {
val bundle = Bundle().apply { putParcelable(DEEP_LINK_PARAM_KEY, deepLink) }
val intent = Intent(this, NavHostActivity::class.java).apply { putExtras(bundle) }
startActivity(intent)
finish()
}
Then in onCreate of the destination activity, grab the deep link, cast to Uri and navigate using implicit deep links (see docs https://developer.android.com/guide/navigation/navigation-deep-link#implicit) as below:
private fun handleDeepLink() {
val deepLink = intent.extras?.getParcelable(DEEP_LINK_PARAM_KEY) as? Uri
deepLink?.let { navController.safeNavigateToDeepLink(deepLink) }
}
I created an extension function for NavController, safeNavigateToDeepLink(deepLink), to check if the navGraph has reference to that deep link (as suggested in the navigation component source code), then if this can't be found, to navigate to a default destination:
fun NavController.safeNavigateToDeepLink(uri: Uri) {
if (graph.hasDeepLink(uri)) navigate(uri)
else safeNavigateTo(R.id.home)
}
If it helps anyone else, the other extension function in there is the below, which just checks that an action to navigate to that destination can be found before navigating:
fun NavController.safeNavigateTo(id: Int) {
val action = currentDestination?.getAction(id)
action?.let { navigate(id) }
}
I have searched the Flutter documentation and googled this, but with zero result. I am developing my first Flutter app for android and I would like to create a custom quick settings tile for it. I am targeting Nougat and above. I know it's possible in Java and Kotlin (e.g. https://android.jlelse.eu/develop-a-custom-tile-with-quick-settings-tile-api-74073e849457), but how about Dart/Flutter?
You can do it natively (makes more sense since it is an Android-only feature).
Every Flutter contains an android and ios folder. Inside of those folders, you will find the wrapper apps for Android and iOS.
Just open the Android project in Android Studio and follow the tutorial you linked.
You can create Quick setting tile natively
Go to android/app/src/main/kotlin/package_name(folder)
Create a service class MyTileService.kt
MyTileService.kt
#RequiresApi(Build.VERSION_CODES.N)
//Quick Tile feature can be used Build.VERSION_CODES.N or Greater
class MyTileService : TileService() {
// Called when the user adds your tile.
override fun onTileAdded() {
super.onTileAdded()
}
// Called when your app can update your tile.
override fun onStartListening() {
super.onStartListening()
val tile = qsTile // this is getQsTile() method form java, used in Kotlin as a property
tile.label = "Set Alarm"
tile.state = Tile.STATE_ACTIVE
tile.icon = Icon.createWithResource(this, R.drawable.baseline_alarm_24)
tile.updateTile() // you need to call this method to apply changes
}
// Called when your app can no longer update your tile.
override fun onStopListening() {
super.onStopListening()
}
// Called when the user taps on your tile in an active or inactive state.
override fun onClick() {
super.onClick()
try{
// to open flutter activity
val newIntent= FlutterActivity.withNewEngine().dartEntrypointArgs(listOf("launchFromQuickTile")).build(this)
newIntent.flags= Intent.FLAG_ACTIVITY_NEW_TASK
startActivityAndCollapse(newIntent)
}
catch (e:Exception){
Log.d("debug","Exception ${e.toString()}")
}
}
// Called when the user removes your tile.
override fun onTileRemoved() {
super.onTileRemoved()
}
}
In AndroidManifest.xml
<service
android:name=".MyTileService"
android:exported="true"
android:icon="#drawable/baseline_alarm_24" //your_vector_icon
android:label="Set Alarm" //tile lable
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action
android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
running your app will create a quick setting tile for your app
if not showing -> try to edit the notification panel icons (xiaomi/mi devices)
onClick on this quick settings icon we want to show our flutter screen
Inside main.dart
Future<void> main(List<String> arguments) async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp(
msg: arguments.isNotEmpty ? arguments[0] : null,
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key, this.msg}) : super(key: key);
final String? msg;
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: msg == "launchFromQuickTile" ? Create.routeName :
Home.routeName,
routes: //your routes,
);
}
}
For more information about quick settings tile
In the below shown diagram, I am having 3 modules(as android library) which extends the base "common components module" and all this 3 modules will be added to a single android application. All 3 modules are independent modules but when it comes as an application, it would require to share some data, launch other module and requires more inter-communication.
So can anyone let me know how we can implement the "Data Sharing Layer" and "Navigation Controller" in this kind of architecture?
Example: Module1 -> Login, Module2 -> Profile Management etc and there could be "n" number of modules based on the application need.
What you are looking for is basically a clean approach on how to communicate with other classes. There is not really a difference in whether or not they are in different modules.
The following sample describes how a LoginActivity could navigate to some profile activity. This is just a basic sample to be improved with what you actually need and intend to do!
Define your interfaces
Write interfaces of what you need. Your Login should be able to open a profile page? Well this sounds like it needs a LoginNavigator!
interface LoginNavigator {
void showProfile();
}
Include those interfaces in your shared components. There is not really a possibility to go without defining interfaces. You can make them more abstract or more fine grained, this is entirely up to you.
Declare your dependencies
Remember how your Login needs a LoginNavigator? The real problem is on how to supply it to your class. You should have a look at dependency injection, since there are frameworks liks dagger-2 that (could) make this easier. For now, we define an interface for a common component, so that we can retrieve the dependencies we need.
interface NavigatorProvider {
LoginNavigator provideNavigator();
}
You may guess it—this method is used to get the actual LoginNavigator that you can use to get the implementation of that interface. Usually you would just declare this dependency in the constructor, but since android is somewhat special you need to get it from somewhere yourself.
Provide your dependencies
The easiest way to go is to just have your application implement this interface (or hold an object that does).
class MyApp extends Application implements NavigatorProvider {
LoginNavigator provideNavigator() {
return new LoginNavigator() {
void showProfile() {
// just some sample code. You should probably not use an
// anonymous class
startActivity(new Intent(this, MyProfileActivity.class));
}
};
}
}
Again, you could also return an object that is implementing this interface. This is just a basic sample.
Use the interface. (And don't care about the implementation)
Now the dependency injection is nearly complete. We have an interface that we need, we have some way to provide the dependency, all that's left is to get it and use it.
class LoginActivity extends Activity {
LoginNavigator mNavigator;
void onCreate() {
// get the dependency
mNavigator = ((NavigatorProvider) getApplicationContext()).provideNavigator();
// use it where needed. (again, just sample code)
findShowProfileView().setOnClickListener(new OnClickListener() {
void onClick(View view) {
mNavigator.showProfile();
}
});
}
}
Now the dependency is provided, and ready to be used.
What this sample shows is how to basically use interfaces to decouple logic. You will still need some point of entry, since android does not allow to implement your own constructors—this is why the application class is used.
I found that solution using Local Broadcast which is implemented in Application Class and send event on Local Broadcast which is received in Application Class.
class AppApplication : Application() {
override fun onCreate() {
super.onCreate()
registerBroadcast()
}
private fun startProfileActivity() {
val intent = newIntent<MyProfileActivity>(this)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
this.startActivity(intent)
}
private fun registerBroadcast() {
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver,IntentFilter(BROADCAST_VIEW_PROFILE))
}
private fun unregisterBroadcast() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(broadCastReceiver)
}
private val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
BROADCAST_VIEW_PROFILE -> {
startProfileActivity()
}
}
}
}
override fun onTerminate() {
super.onTerminate()
unregisterBroadcast()
}
}
When you send the event in an Application like this
private fun viewProfileEventSend() {
// Send Broadcast for view profile to `APP`
val intent = Intent(BROADCAST_VIEW_PROFILE)
LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent)
}
Because your module doesn't need to get the instance of Application or any interface.