expo / router

[ARCHIVE]: Expo Router has moved to expo/expo -- The File-based router for universal React Native apps
https://docs.expo.dev/routing/introduction/
1.36k stars 113 forks source link

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

Closed MatLish00010 closed 1 year ago

MatLish00010 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

mskitcompl commented 1 year ago

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

Nziranziza commented 1 year ago
...TransitionPresets.ModalPresentationIOS,

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

emersonlaurentino commented 1 year ago

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 commented 12 months ago

@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 commented 11 months ago

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

evasif commented 11 months ago
...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 commented 11 months ago

@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 commented 10 months ago

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 commented 10 months ago

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 commented 9 months ago

@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 commented 8 months ago

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

arvl130 commented 8 months ago

@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 commented 7 months ago

Is this still so for expo router 3?

emporteme commented 6 months ago
StackNavigationOptions

Is this still so for expo router 3?

I think so

Rajeshchalasani9 commented 6 months ago

@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 commented 5 months ago

@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 commented 5 months ago

@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💪

const { Navigator } = createStackNavigator();

export const JsStack = withLayoutContext< StackNavigationOptions, typeof Navigator, StackNavigationState, StackNavigationEventMap

(Navigator);

CarlosDCorrea commented 4 months ago

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 commented 4 months ago

@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 commented 2 months ago

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 commented 2 months ago

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 commented 1 month ago

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 commented 1 month ago

Thanks to everyone!. Working fine on my end.

Specifically to: @EvanBacon and @tittobreno