react-nativelayoutreact-native-button

how to align the text of a button that may have icons


I am programming in react native and I am facing an overflow problem. I have a button component that can have a width of 100% or the size of the component, depending on the parameter (fillContent parameter). When the button has no icons or a short text, everything works fine. The problem arises when there is a very long text and icons at the same time because apparently, the text is pushing the icons out, as if it had a kind of 'superiority' over the icons where they respect the size of the text. I want it to be the other way around, with the icons inside the button occupying their correct size, and the text readjusting accordingly. It is also worth noting that there should be a 'gap: 10' so that the text does not stick to the icons.

button: (it can be contained, outlined, or text button, I want the same rule for all three)

export default function Button({
  style,
  children,
  color,
  disabled = false,
  variant,
  size = "large",
  fillContent,
  prefixIcon,
  suffixIcon,
  prefixIconColor,
  suffixIconColor,
  ...props
}: ButtonProps) {

  var useColor = disabled
    ? Colors.buttonDisabled
    : color
      ? Colors[color]
      : Colors.primary;
  var usePadding = size === "medium" ? 8 : 16;
  console.log("PROP: ", fillContent)

    if (variant == "contained") {
    return (
        <TouchableOpacity
          style={[
            globalStyles.buttonContained,
            { backgroundColor: useColor,  padding: usePadding, width: fillContent ? undefined : "100%", maxWidth: '100%' },
            style,
          ]}
          {...props}
        >
            {prefixIcon ? <IconStyled icon={prefixIcon} color={prefixIconColor ? prefixIconColor : 'primary'}/> : null}
            {children}
            {suffixIcon ? <IconStyled icon={suffixIcon} color={suffixIconColor ? suffixIconColor : 'primary'}/> : null}

      </TouchableOpacity>      
    );
  } else if (variant == "outlined") {
    return (
        <TouchableOpacity
        style={[
          globalStyles.buttonOutlined,
          { borderColor: useColor, padding: usePadding, width: fillContent ? undefined : "100%" },
          style,
        ]}
        {...props}
      >
        {prefixIcon ? <IconStyled icon={prefixIcon} color={prefixIconColor ? prefixIconColor : 'primary'}/> : null}
        {children}
        {suffixIcon ? <IconStyled icon={suffixIcon} color={suffixIconColor ? suffixIconColor : 'primary'}/> : null}
      </TouchableOpacity>
    );
  }
  return (
      <TouchableOpacity style={globalStyles.buttonText} {...props}>
        {prefixIcon ? <IconStyled icon={prefixIcon} color={prefixIconColor ? prefixIconColor : 'primary'}/> : null}
        {children}
        {suffixIcon ? <IconStyled icon={suffixIcon} color={suffixIconColor ? suffixIconColor : 'primary'}/> : null}
      </TouchableOpacity>    
  );
}

globalStyles:

buttonContained: {
        ...defaultStyles.buttonDefault,
        backgroundColor: Colors.primary,
    },
    buttonOutlined: {
        ...defaultStyles.buttonDefault,
        borderColor: Colors.primary,
        borderWidth: 1,
    },
buttonText: {
        ...defaultStyles.buttonDefault,
        alignSelf: 'flex-start'
    },

defaultStyles:

buttonDefault: {
        borderRadius: 8,
        width: "100%",
        padding: 8,
        height: "auto",
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'row',
        gap: 10
      }

How can I ensure that, respecting the button's padding, the icons occupy their space in the button correctly, and the text adjusts to the remaining size?

Note: I tried adding 'flex: 1', but since there is an option for fillContent, this would make the button wider than it should be, as when fillContent is true, it should be the size of the content, whether it's a large or small text.

image of the problem:

enter image description here

what I wantt to do:

enter image description here


Solution

  • I found a solution using 'flexShrink':

    {prefixIcon ? <IconStyled icon={prefixIcon} color={prefixIconColor ? prefixIconColor : 'primary'} style={{ flex: 0 }}/> : null}
    <View style={{ flexShrink: 1 }}>{children}</View>
    {suffixIcon ? <IconStyled icon={suffixIcon} color={suffixIconColor ? suffixIconColor : 'primary'} style={{ flex: 0 }}/> : null}
    

    And it turned out just the way it should!