FlatListで2カラムリストを作るときのstyle
現状
Yogaにgridがサポートされてないからきつい
単純にやると以下のように書ける
numColumns
でカラム数を指定し、
flex
は0.5にすることで半分より伸びないようにする
ts<FlatList
data={[{ key: 'a' }, { key: 'b' }, { key: 'c' }]}
numColumns={2}
renderItem={({ item }) => (
<View style={{ flex: 0.5, backgroundColor: 'red' }}>
<Text>{item.key}</Text>
</View>
)}
columnWrapperStyle={{
gap: 8, // 1行の中に並ぶアイテム同士のgap
}}
contentContainerStyle={{
gap: 8, // 行間
}}
/>
しかし、これだと奇数の場合に微妙にstyleが壊れる
微妙に、cの横幅が長いのが分かるだろう
flex: 0.5
はa,b,cのいずれにも効いているが、a,bは良い感じに縮み、 gap: 8
を保っている
しかし、cはgapの相手がいないため、 flex: 0.5
のみが優先され、他よりもちょっと伸びる
無理やり実現する方法はいくつか考えられるがどれも微妙
widthで指定する
Flexを棄てるということ
変える場所
widthを使って、画面幅を見ながら数px単位で合わせる
データはそのままでいい
Emptyで端数を埋める
変える場所
data
に端数の分だけ undefined
等を埋める
renderItem
内でEmptyと出し分ける
styleはそのままで良い
FlatListをwrapするようなComponentを作っておけば良い
カラム数が多い場合は無駄にEmptyがたくさん表示されうる
wrapperのイメージ
tsexport function GridFlatList<T>({ ...props }: Props<T>) {
const numColumns = props.numColumns || 1;
const data = props.data || [];
const paddedData = padEmpty(data, numColumns);
return (
<FlatList
{...props}
data={paddedData}
keyExtractor={(item, index) => {
if (isEmpty(item)) {
return `empty-${index}`;
}
return props.keyExtractor
? props.keyExtractor(item, index)
: index.toString();
}}
renderItem={({ item, ...rest }) => {
if (isEmpty(item)) {
return <View />;
}
if (props.renderItem == null) {
return null;
}
return props.renderItem({ item, ...rest });
}}
/>
);
}
const empty = Symbol('empty');
const isEmpty = (item: any): item is symbol => item === empty;
const padEmpty = <T,>(
data: ArrayLike<T>,
numColumns: number,
): (T | symbol)[] => {
const dataArray = Array.from(data);
const rest = data.length % numColumns;
if (rest === 0) {
return dataArray;
}
return [...dataArray, ...range(numColumns - rest).map(() => empty)];
};