onLayout is only called on the initial render and if the element changes. The element most likely isn't changing while you're scrolling, so its position on screen isn't measured again.
The best way I know to handle this is to store the ref of the list item, and run its measure method when it's touched. You can read about measure here: https://reactnative.dev/docs/direct-manipulation#measurecallback
You'd have to make a new component instead of writing it inline in the FlatList render method, something like:
const ListButton = ({ text, onPress }) => {
const buttonRef = useRef(null);
const onPressWithMeasure = () => {
buttonRef.current?.measure((x, y, width, height, pageX, pageY) => {
console.log(x, y, width, height, pageX, pageY);
onPress();
});
};
return (
<TouchableOpacity onPress={onPressWithMeasure} ref={buttonRef}>
<Text>{text}</Text>
</TouchableOpacity>
);
};
And then in your FlatList:
<FlatList
keyExtractor={(item) => item.id}
data={data}
renderItem={({ item }) => (
<ListButton text={item.text} onPress={() => {}} />
)}
/>
In the comments a method was requested to set the layout of the individual button based on its width. This can be accomplished using local state and onLayout:
const ListButton = ({ text, onPress }) => {
const [totalWidth, setTotalWidth] = useState(0); // new
...
return (
<TouchableOpacity
onPress={onPressWithMeasure}
ref={buttonRef}
onLayout={({ nativeEvent }) => setTotalWidth(nativeEvent.layout.width)} // new
style={{ marginLeft: totalWidth }} // new
>
<Text>{text}</Text>
</TouchableOpacity>
);
};