Ben
Ben

Reputation: 3357

Vertically align Pressable inside a Text component

<View
  style={{
    flexDirection: "row",
  }}
>
  <Text
    style={{
      flex: 1,
    }}
  >
    By continuing, you agree to our{" "}
    <Pressable
      onPress={...}
    >
      <Text>
        Terms of Service
      </Text>
    </Pressable>
  </Text>
</View>

"Terms of Service" is printed higher than "By continuing, you agree to our". How do I vertically align them? Or more precisely - how do I get the Pressable Text to vertically align to the bottom? enter image description here

Upvotes: 11

Views: 5190

Answers (6)

yassire mtioui
yassire mtioui

Reputation: 41

A crazy trick that I just did (and if you are not using translations), I used the text as an SVG component that I exported from Figma.

Upvotes: 1

user56reinstatemonica8
user56reinstatemonica8

Reputation: 34084

This is a bug in React Native itself. There are several open reports of this bug on React Native's GitHub, but the chances of it being fixed don't look good:


In React Native >= 0.65, if your inline pressable element uses only text styles, you can work around this issue by using <Text> with onPress (and onPressIn and onPressOut to style the pressed state). Crude example:

/**
 * Like a simplified Pressable that doesn't look broken inline in `Text` on Android
 */ 
const TextButton = ({ children, onPress, style, ...rest } => {
  const [pressed, setPressed] = useState(false)
  const onPressIn = () => setPressed(true)
  const onPressOut = () => setPressed(false)
  return (
    <Text
      onPress={onPress}
      onPressIn={onPressIn}
      onPressOut={onPressOut}
      style={typeof style === 'function' ? style({ pressed }) : style}
      {...rest}
    >
      {typeof children === 'function' ? children({ pressed }) : children}
    </Text>
  )
}

Beware, however, that there are also bugs around selecting interactive elements nested inside text using accessibility tools. If you can simply avoid nesting the interactive element in text, and have it on its own line, that's probably better: link-like interactive nested text isn't well supported in React Native currently.


On older versions of React Native before 0.65, Text didn't support onPressIn or onPressOut:

  • If you don't need Pressable's pressed state, use Text instead of Pressable (as the asker did: https://stackoverflow.com/a/66590787/568458)
  • If you do need pressed state, Text doesn't support the required onPressIn/Out handlers. However, TouchableWithoutFeedback does support those, and works by injecting props into its child so the Text will remain Text with no wrapping View. Wrap a Text in TouchableWithoutFeedback and pass the touchable onPress with onPressIn and onPressOut handlers and store the pressed state yourself.
/**
 * Like a simplified Pressable that doesn't look broken inline in `Text` on Android
 */ 
const TextButton = ({ children, onPress, style, ...rest } => {
  const [pressed, setPressed] = useState(false)
  const onPressIn = () => setPressed(true)
  const onPressOut = () => setPressed(false)

  // TouchableWithoutFeedback modifies and returns its child; this returns `Text`
  // plus press in/out events attached that aren't supported by Text directly.
  return (
    <TouchableWithoutFeedback
      onPress={onPress}
      onPressIn={onPressIn}
      onPressOut={onPressOut}
    >
      <Text
        style={typeof style === 'function' ? style({ pressed }) : style}
        {...rest}
      >
        {typeof children === 'function' ? children({ pressed }) : children}
      </Text>
    </TouchableWithoutFeedback>
  )
}

Warning: if you're also using React Native Web and React Navigation, don't use the TouchableWithoutFeedback approach on Web, use pure Pressable on web, because React Navigation's navigate functions don't reliably work when passed to Touchable*s on Web due to a quirk of how the event handlers are set up (but they do work in Pressable), and this issue doesn't exist on Web.

Upvotes: 14

Yannick Schr&#246;der
Yannick Schr&#246;der

Reputation: 378

I found a very hackidy-hack solution...

<Text selectable={true}>
     <Text>if you click</Text>
     <TouchableOpacity
       style={{flexDirection: 'row'}}
       onPress={() => Linking.openURL("https://www.google.com")}
     >
         <Text
           style={{
             alignSelf: 'flex-end',
             marginBottom: -4,
           }}
         >
             here
         </Text>
     </TouchableOpacity>
     <Text>, it will open google</Text>
</Text>

Upvotes: 2

KUMAR SAURAV
KUMAR SAURAV

Reputation: 331

By default the flexDirection is column. Change it to flexDirection:"row"

Upvotes: 0

Ben
Ben

Reputation: 3357

Ended up doing this differently, using the onPress property of the <Text> component and finally wrapping all <Text> components in another <Text> component to have a proper line break:

<View>
  <Text>
    <Text>
      By continuing, you agree to our{" "}
    </Text>
    <Text onPress={() => {...}}>
      Terms of Service
    </Text>
    <Text>
      {" "}and our{" "}
    </Text>
    <Text onPress={() => {...}}>
      Privacy Policy
    </Text>
  </Text>
</View>

Upvotes: 3

Erhan Yaşar
Erhan Yaşar

Reputation: 864

The snippet below should work but hard to understand without giving a shot. If you can provide screenshots I can help more in sake of getting a more proper result.

<View>
  <Text style={{
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center'
  }}>
    By continuing, you agree to our{" "}
    <Pressable
      onPress={() => {
        navigate("LegalStack", { screen: "Terms" });
      }}
    >
      <Text style={{margin: 'auto'}}>
        Terms of Service
      </Text>
    </Pressable>
  </Text>
</View>

Upvotes: 1

Related Questions