📱

React NativeのFlexboxを理解する

に公開

この記事はReact Native 全部俺 Advent Calendar 11目の記事です。

https://adventar.org/calendars/10741

このアドベントカレンダーについて

このアドベントカレンダーは @itome が全て書いています。

基本的にReact NativeおよびExpoの公式ドキュメントとソースコードを参照しながら書いていきます。誤植や編集依頼はXにお願いします。

React NativeのFlexboxを理解する

React Nativeでアプリを作る際、画面のレイアウトはFlexboxを使って実現します。Web開発者にとっては馴染み深いこの仕組みですが、React Nativeならではの特徴や注意点もあります。今回は実践的なコード例を交えながら、React NativeのFlexboxについて詳しく見ていきましょう。

React Nativeのスタイリングシステム

React Nativeでは、CSSは直接書けませんが、CSSライクな記法でスタイルを定義することができます。スタイルの定義にはStyleSheet.create()を使用します。

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    fontSize: 16,
    color: '#333',
  },
});

このように定義したスタイルは、コンポーネントのstyleプロパティに渡して使用します。StyleSheet.create()を使うことで、型チェックやパフォーマンスの最適化が行われます。

Flexboxの仕組み

React NativeのFlexboxは、内部でYogaというライブラリを使って実装されています。Yogaは、FacebookがC++で実装したFlexboxのレイアウトエンジンで、クロスプラットフォームで動作します。

YogaはFlexboxの仕様に準拠していますが、ネイティブアプリのレイアウトに最適化されているため、Webのデフォルト値とは異なる点があります。

CSSとの主な違い

  1. flexDirectionのデフォルト値がrowではなくcolumn
  2. alignContentのデフォルト値がstretchではなくflex-start
  3. flexShrinkのデフォルト値が1ではなく0
  4. flexプロパティが単一の数値のみをサポート
  5. すべてのViewコンポーネントにデフォルトでdisplay: flexが適用される

実践的なFlexboxの使い方

要素を横に並べる

import { View, StyleSheet } from 'react-native';

const Row = () => (
  <View style={styles.row}>
    <View style={styles.box} />
    <View style={styles.box} />
    <View style={styles.box} />
  </View>
);

const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    gap: 8,
  },
  box: {
    width: 50,
    height: 50,
    backgroundColor: 'blue',
  },
});

要素を縦に並べる

import { View, StyleSheet } from 'react-native';

const Column = () => (
  <View style={styles.column}>
    <View style={styles.box} />
    <View style={styles.box} />
    <View style={styles.box} />
  </View>
);

const styles = StyleSheet.create({
  column: {
    flexDirection: 'column',
    gap: 8,
  },
  box: {
    width: 50,
    height: 50,
    backgroundColor: 'blue',
  },
});

要素を水平方向に中央配置

import { View, StyleSheet } from 'react-native';

const CenterHorizontal = () => (
  <View style={styles.container}>
    <View style={styles.box} />
  </View>
);

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    width: '100%',
  },
  box: {
    width: 50,
    height: 50,
    backgroundColor: 'blue',
  },
});

要素を垂直方向に中央配置

import { View, StyleSheet } from 'react-native';

const CenterVertical = () => (
  <View style={styles.container}>
    <View style={styles.box} />
  </View>
);

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    height: '100%',
  },
  box: {
    width: 50,
    height: 50,
    backgroundColor: 'blue',
  },
});

要素を均等に配置

import { View, StyleSheet } from 'react-native';

const EqualSpace = () => (
  <View style={styles.container}>
    <View style={styles.box} />
    <View style={styles.box} />
    <View style={styles.box} />
  </View>
);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
  },
  box: {
    flex: 1,
    height: 50,
    backgroundColor: 'blue',
    margin: 4,
  },
});

比率を指定して配置

import { View, StyleSheet } from 'react-native';

const RatioSpace = () => (
  <View style={styles.container}>
    <View style={[styles.box, { flex: 2 }]} />
    <View style={[styles.box, { flex: 1 }]} />
    <View style={[styles.box, { flex: 1 }]} />
  </View>
);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
  },
  box: {
    height: 50,
    backgroundColor: 'blue',
    margin: 4,
  },
});

要素を重ねて配置

Flexboxの範疇を少し超えますが、レイアウトでよく使う技術として、position: 'absolute'を使った要素の重ね合わせがあります。

import { View, StyleSheet } from 'react-native';

const OverlayLayout = () => (
  <View style={styles.container}>
    <View style={styles.background} />
    <View style={styles.overlay} />
  </View>
);

const styles = StyleSheet.create({
  container: {
    width: '100%',
    height: 200,
  },
  background: {
    width: '100%',
    height: '100%',
    backgroundColor: 'blue',
  },
  overlay: {
    position: 'absolute',
    top: 20,
    left: 20,
    width: 100,
    height: 100,
    backgroundColor: 'rgba(255, 0, 0, 0.5)',
  },
});

まとめ

React NativeのFlexboxは、Webのものと似ていながらも独自の特徴を持っています。主な違いは:

  • デフォルト値が異なる(flexDirection: columnなど)
  • すべての要素がデフォルトでflex container
  • flexプロパティの制限

これらの違いを理解した上で使用することで、効率的にレイアウトを組むことができます。また、実際のアプリ開発では、これらの基本的なテクニックを組み合わせることで、複雑なレイアウトも実現できます。

レイアウトで困ったときは、まずFlexboxのプロパティを見直してみましょう。多くの場合、適切なプロパティの組み合わせで望んだレイアウトを実現できるはずです。

明日はReact Nativeのジェスチャーハンドリングについて紹介します。

Discussion