Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

presentation: 'modal' does't work for Stack in Android #640

Closed
MatLish00010 opened this issue Jun 6, 2023 · 30 comments
Closed

presentation: 'modal' does't work for Stack in Android #640

MatLish00010 opened this issue Jun 6, 2023 · 30 comments

Comments

@MatLish00010
Copy link

MatLish00010 commented Jun 6, 2023

Which package manager are you using? (Yarn is recommended)

yarn

Summary

On android modal screen looks like simplify screen, but on IoS everything is fine

Minimal reproducible example

`
<Stack.Screen
mode="modal"
name="myModal"
screenOptions={{headerShown: true, presentation: 'modal'}}

    options={{
       presentation: 'modal'
   }}

/>

`

  "expo-router": "^1.5.3",
  "expo": "~48.0.11",
  "react": "18.2.0",
@sapjax
Copy link

sapjax commented Jun 7, 2023

The Modal presentation in iOS is iOS only.
you can use JS Stack https://expo.github.io/router/docs/migration/react-navigation/native-stack
and set the screen option:

  <JsStack.Screen
      key={name}
      name={name}
      options={{
        ...TransitionPresets.ModalPresentationIOS,
        presentation: 'modal'
      }}
    />

@EvanBacon
Copy link
Contributor

To clarify, use JS stack:

import {
  // Import the creation function
  createStackNavigator,
  // Import the types
  StackNavigationOptions,
} from "@react-navigation/stack";

import { withLayoutContext } from "expo-router";

const { Navigator } = createStackNavigator();

// This can be used like `<JsStack />`
export const JsStack = withLayoutContext<
  StackNavigationOptions,
  typeof Navigator
>(Navigator);

Then

import { TransitionPresets } from '@react-navigation/stack';

// Later ...
<JsStack name="..." options={{
        ...TransitionPresets.ModalPresentationIOS,
        presentation: 'modal'
      }}>

@cseelus
Copy link

cseelus commented Aug 10, 2023

Any chance this could be implemented directly with Expo-Router, without using JsStack?

When using JsStack, one has to use different option names (animationEnabled vs animation, …) and has some glitches like different letter spacing for the header title, at least with our app.

@farideliyev
Copy link

It would be better to be able use it with just presentation: "modal", rather than weird workaround

@mskitcompl
Copy link

@EvanBacon how can I do it with Expo v2 and SDK 49, I'm getting dependency conflict

@Nziranziza
Copy link

...TransitionPresets.ModalPresentationIOS,

The modal does not close on Android when I swipe it down

@emersonlaurentino
Copy link

It would be better to be able use it with just presentation: "modal", rather than weird workaround

yes, agree. @EvanBacon any chance for this?

@DarlonHenrique
Copy link

DarlonHenrique commented Sep 29, 2023

@EvanBacon the solution you provide, do not make the android have the same result of IOS, it's keep the Android Version having the same result as before, actually I'm in expo router v2 and expo 49

"expo-router": "2.0.0"
"react": "18.2.0",
"react-native": "0.72.4",

I'm using Expo Go to test it in a physical iPhone 7 with IOS 15.7.6, and it's working fine
I'm also using a physical Samsung Galaxy S20 with Android 13 and One UI 5.1 to test in Android with Expo Go

My expo go version on android is 2.29.8
my expo go client version on IOS is 1017565

that's my code piece in my app/_layout.tsx

<Stack>
  <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
  <JsStack.Screen
    name="filters"
    options={{
      ...TransitionPresets.ModalPresentationIOS,
      presentation: "modal",
    }}
  />
</Stack>

and that's the code I'm using to provide the JsStack:

import {
	// Import the creation function
	createStackNavigator,
	// Import the types
	StackNavigationOptions
} from '@react-navigation/stack'

import { withLayoutContext } from 'expo-router'

const { Navigator } = createStackNavigator()

// This can be used like `<JsStack />`
export const JsStack = withLayoutContext<
	StackNavigationOptions,
	typeof Navigator
>(Navigator)

the same code you provide above
I'm open to help solve that issue, appreciate it

@AsyncAdept
Copy link

AsyncAdept commented Oct 8, 2023

Hi @EvanBacon,
I'm experiencing the exact same problem @DarlonHenrique mentioned using expo-router v2. Is there any solutions ?

@evasif
Copy link

evasif commented Oct 11, 2023

...TransitionPresets.ModalPresentationIOS,

The modal does not close on Android when I swipe it down

If you add gestureEnabled: true to the options the swipe down works for me on Android

@evasif
Copy link

evasif commented Oct 11, 2023

@EvanBacon We have used provided example in a typescript project and we get a type error for the withLayoutContext function that it expects 4 type arguments not 2, it works if the ignore the typescript error but it is not a really nice solution imo. So it would be great if it would be possible to have the modal work on android with expo-router directly so we could skip this workaround💪

@GabrielGalatti
Copy link

Hi, I'm with the same issue. I have tried to use presentation: modal in the same stack and in another stack, but I still have the same result. Any updates?

@arvl130
Copy link

arvl130 commented Nov 9, 2023

I don't think pinging the guy again and again is going to help get this issue resolved.

From the Material UI documentation, it looks like Android simply doesn't support swipeable full-screen dialogs the same way iOS has.

So there simply isn't any solution other than to implement your own with the JSStack navigator, like shown already. More information can be found here.

@ntuong196
Copy link

ntuong196 commented Dec 12, 2023

@EvanBacon We have used provided example in a typescript project and we get a type error for the withLayoutContext function that it expects 4 type arguments not 2, it works if the ignore the typescript error but it is not a really nice solution imo. So it would be great if it would be possible to have the modal work on android with expo-router directly so we could skip this workaround💪

Reference the implementation of withLayoutContext. You can customise MaterialBottomTabs like this to use with Expo Router:

import {
	createMaterialBottomTabNavigator,
	MaterialBottomTabNavigationEventMap,
	MaterialBottomTabNavigationOptions,
} from 'react-native-paper/react-navigation'

import { NavigationState, ParamListBase } from '@react-navigation/native'

import { withLayoutContext } from 'expo-router'

const { Navigator } = createMaterialBottomTabNavigator()

export const MaterialBottomTabs = withLayoutContext<
	MaterialBottomTabNavigationOptions,
	typeof Navigator,
	NavigationState<ParamListBase>,
	MaterialBottomTabNavigationEventMap
>(Navigator)

@fukemy
Copy link

fukemy commented Jan 12, 2024

I want to use native stack not js stack, any solution?

@arvl130
Copy link

arvl130 commented Jan 12, 2024

@fukemy Android does not support modal screens, so no. The only solution is to emulate the iOS behavior with JS stack. Check my comment above.

@DomGarza
Copy link

Is this still so for expo router 3?

@emporteme
Copy link

StackNavigationOptions

Is this still so for expo router 3?

I think so

@Rajeshchalasani9
Copy link

@EvanBacon the solution you provide, do not make the android have the same result of IOS, it's keep the Android Version having the same result as before, actually I'm in expo router v2 and expo 49

"expo-router": "2.0.0"
"react": "18.2.0",
"react-native": "0.72.4",

I'm using Expo Go to test it in a physical iPhone 7 with IOS 15.7.6, and it's working fine I'm also using a physical Samsung Galaxy S20 with Android 13 and One UI 5.1 to test in Android with Expo Go

My expo go version on android is 2.29.8 my expo go client version on IOS is 1017565

that's my code piece in my app/_layout.tsx

<Stack>
  <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
  <JsStack.Screen
    name="filters"
    options={{
      ...TransitionPresets.ModalPresentationIOS,
      presentation: "modal",
    }}
  />
</Stack>

and that's the code I'm using to provide the JsStack:

import {
	// Import the creation function
	createStackNavigator,
	// Import the types
	StackNavigationOptions
} from '@react-navigation/stack'

import { withLayoutContext } from 'expo-router'

const { Navigator } = createStackNavigator()

// This can be used like `<JsStack />`
export const JsStack = withLayoutContext<
	StackNavigationOptions,
	typeof Navigator
>(Navigator)

the same code you provide above I'm open to help solve that issue, appreciate it

Any luck ? I am also having the same issue.

@Marco-Veio
Copy link

Marco-Veio commented Mar 28, 2024

@EvanBacon the solution you provide, do not make the android have the same result of IOS, it's keep the Android Version having the same result as before, actually I'm in expo router v2 and expo 49

"expo-router": "2.0.0"
"react": "18.2.0",
"react-native": "0.72.4",

I'm using Expo Go to test it in a physical iPhone 7 with IOS 15.7.6, and it's working fine I'm also using a physical Samsung Galaxy S20 with Android 13 and One UI 5.1 to test in Android with Expo Go

My expo go version on android is 2.29.8 my expo go client version on IOS is 1017565

that's my code piece in my app/_layout.tsx

<Stack>
  <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
  <JsStack.Screen
    name="filters"
    options={{
      ...TransitionPresets.ModalPresentationIOS,
      presentation: "modal",
    }}
  />
</Stack>

and that's the code I'm using to provide the JsStack:

import {
	// Import the creation function
	createStackNavigator,
	// Import the types
	StackNavigationOptions
} from '@react-navigation/stack'

import { withLayoutContext } from 'expo-router'

const { Navigator } = createStackNavigator()

// This can be used like `<JsStack />`
export const JsStack = withLayoutContext<
	StackNavigationOptions,
	typeof Navigator
>(Navigator)

the same code you provide above I'm open to help solve that issue, appreciate it

Try changing your app/_layout.tsx:

<JsStack>
  <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
  <JsStack.Screen
    name="filters"
    options={{
      ...TransitionPresets.ModalPresentationIOS,
      presentation: "modal",
      gestureEnabled: true,
    }}
  />
</JsStack>

This worked for me

@tittobreno
Copy link

tittobreno commented Apr 14, 2024

@EvanBacon We have used provided example in a typescript project and we get a type error for the withLayoutContext function that it expects 4 type arguments not 2, it works if the ignore the typescript error but it is not a really nice solution imo. So it would be great if it would be possible to have the modal work on android with expo-router directly so we could skip this workaround💪

  • you can type it like this below, it's working for me
import { Stack, withLayoutContext } from "expo-router";
import {
  createStackNavigator,
  StackNavigationEventMap,
  StackNavigationOptions,
  TransitionPresets,
} from "@react-navigation/stack";
import { ParamListBase, StackNavigationState } from "@react-navigation/native";

const { Navigator } = createStackNavigator();

export const JsStack = withLayoutContext<
  StackNavigationOptions,
  typeof Navigator,
  StackNavigationState<ParamListBase>,
  StackNavigationEventMap
>(Navigator);

@CarlosDCorrea
Copy link

The expo team should update the docs then, i´ve been trying to see how to solve it just because the docs say it supports android

(property) presentation?: "modal" | "transparentModal" | "containedModal" | "containedTransparentModal" | "fullScreenModal" | "formSheet" | "card" | undefined
How should the screen be presented.

Supported values:

"card": the new screen will be pushed onto a stack, which means the default animation will be slide from the side on iOS, the animation on Android will vary depending on the OS version and theme.
"modal": the new screen will be presented modally. this also allows for a nested stack to be rendered inside the screen.
"transparentModal": the new screen will be presented modally, but in addition, the previous screen will stay so that the content below can still be seen if the screen has translucent background.
"containedModal": will use "UIModalPresentationCurrentContext" modal style on iOS and will fallback to "modal" on Android.
"containedTransparentModal": will use "UIModalPresentationOverCurrentContext" modal style on iOS and will fallback to "transparentModal" on Android.
"fullScreenModal": will use "UIModalPresentationFullScreen" modal style on iOS and will fallback to "modal" on Android.
"formSheet": will use "UIModalPresentationFormSheet" modal style on iOS and will fallback to "modal" on Android.
Only supported on iOS and Android.

@MarlonX19
Copy link

@EvanBacon We have used provided example in a typescript project and we get a type error for the withLayoutContext function that it expects 4 type arguments not 2, it works if the ignore the typescript error but it is not a really nice solution imo. So it would be great if it would be possible to have the modal work on android with expo-router directly so we could skip this workaround💪

  • you can type it like this below, it's working for me
import { Stack, withLayoutContext } from "expo-router";
import {
  createStackNavigator,
  StackNavigationEventMap,
  StackNavigationOptions,
  TransitionPresets,
} from "@react-navigation/stack";
import { ParamListBase, StackNavigationState } from "@react-navigation/native";

const { Navigator } = createStackNavigator();

export const JsStack = withLayoutContext<
  StackNavigationOptions,
  typeof Navigator,
  StackNavigationState<ParamListBase>,
  StackNavigationEventMap
>(Navigator);

I was getting the same typescript error and you literally saved me with that! Thanks, man!

@yourjhay
Copy link

yourjhay commented Jul 4, 2024

You should update the expo router docs. It is really misleading. It mentioned that android is supported when clearly it's not.. and instead developers lead to using this workaround instead.

@geoffcfchen
Copy link

geoffcfchen commented Jul 20, 2024

someone created an iOS Modal Sheet Animation for Android. It may be very useful and people may be interested.
https://github.com/ElSierra/ios-sheet-for-android-react-native

I would love to know what you guys think about it.

@jonmcwest
Copy link

We tried using the JS Stack solution, but our modal is around 3 nested navigators deep.. The IOS modal displays over all of the nested navigator headers but the JSStack modal only displays within the parent stack..

Has anyone encountered / solved this?

@boclar
Copy link

boclar commented Aug 20, 2024

Thanks to everyone!. Working fine on my end.

Specifically to: @EvanBacon and @tittobreno

@devcyjung
Copy link

I find this still clunky on Android and even worse on Web so instead I use Tamagui's Sheet component. Works well on all 3 platforms

@hichemfantar
Copy link

For anyone who tried the jstack with expo router, is it a stable setup?

@umrashrf
Copy link

umrashrf commented Mar 7, 2025

I used Platform.OS === 'android' to render JsStack for Android and Stack for iOS because JsStack doesn't work on iOS.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests