Home > database >  Stick two flex elements with dynamic growing in react-native
Stick two flex elements with dynamic growing in react-native

Time:01-10

With react-native, I'm looking forward having a TextInput stuck with a MaterialIcons.Button within the same line.

I want the whole elements be centered horizontally but cannot achieve this with the following code:

import React from 'react';
import {
  StyleSheet, TextInput, View,
} from 'react-native';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';

const WordInput = () => {
  return (
    <View style={styles.container}>
      <View style={styles.textInputContainer}>
        <TextInput
          textAlign="left"
        />
      </View>
      
      <View style={styles.arrowButtonContainer}>
        <MaterialIcons.Button
          name="arrow-forward-ios"
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    flex: 1,
  },
  textInputContainer: {
    flex: 1,
    alignItems: 'flex-end',
    justifyContent: 'center',
  },
  arrowButtonContainer: {
    flex: 1,
    alignItems: 'flex-start',
    justifyContent: 'center',
  },
});

Here is the associated expo snack link.

The problem is that when I type text inside the TextInput, the Button doesn't move at all. I would like it to dynamically shift to the right when the TextInput's width grow. The overall should be horizontally centered.

Does anyone know how I can proceed?

Thanks!

CodePudding user response:

Unfortunately <TextInput> doesn't support any sort of "auto-growing" behaviour by default, but you could implement something yourself using a hidden <Text> layer that has the same font styling and sizing rules as your <TextInput>. If you then render your input value to that <Text> element, you can measure the layout of that element and apply the measured width to your <TextInput> component.

import { useState } from 'react';
import { StyleSheet, TextInput, View, Dimensions, Text } from 'react-native';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';

export default function App() {
  const [value, setValue] = useState();
  const [containerWidth, setContainerWidth] = useState(
    Dimensions.get('window').width
  );
  const [textWidth, setTextWidth] = useState(0);
  const [buttonWidth, setButtonWidth] = useState(0);

  const inputWidth = Math.min(textWidth, containerWidth - buttonWidth);

  return (
    <View style={styles.root}>
      <View
        style={styles.inner}
        onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}>
        <Text
          style={styles.hiddenText}
          onLayout={(e) => setTextWidth(e.nativeEvent.layout.width)}
          numberOfLines={1}
          accessibilityElementsHidden
          importantForAccessibility="no-hide-decendants">
          {value}
        </Text>
        <TextInput
          textAlign="left"
          placeholder="enter text"
          style={[styles.input, { width: inputWidth }]}
          onChangeText={setValue}
        />
        <View onLayout={(e) => setButtonWidth(e.nativeEvent.layout.width)}>
          <MaterialIcons.Button name="arrow-forward-ios" />
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
    justifyContent: 'center',
  },
  inner: {
    flexDirection: 'row',
    justifyContent: 'center',
    borderWidth: 1,
    borderColor: 'red',
  },
  input: {
    borderWidth: 1,
    borderColor: 'green',
    minWidth: 100,
  },
  hiddenText: {
    position: 'absolute',
    top: 0,
    left: 0,
    opacity: 0,
  },
});

Here's an expo snack example.

  • Related