2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React Native】React Native PaperでUI作成

Last updated at Posted at 2025-04-22

React Native Paperとは

GoogleのMaterial Designガイドラインに基づいた
React Native用のUIコンポーネントライブラリです。
美しく一貫性のあるデザインを簡単に実装できるよう設計されている。

React Nativeの標準コンポーネントとの違い

標準コンポーネント

  • 基本的な機能のみを提供
  • デザインは最小限でプラットフォーム固有の見た目
  • UIのスタイリングは全て自分で行う

React Native Paper

  • デザイン済の高機能コンポーネント
  • Material Designガイドラインに従った一貫したデザイン
  • テーマ機能によるアプリ全体の色や字体の統一
  • アニメーションや状態管理が組み込み済み

React Native Paperを使うメリット

開発の効率化:
1つのコードベースでiOSとAndroidの両方に対応できるのと、
デザイン済のコンポーネントを使うことでUI開発の時間が大幅に短縮される。

デザインの一貫性:
すべてのコンポーネントが Material Design に従っているため、
統一感のあるデザインが簡単に実現できます

テーマ機能:
アプリ全体のカラーテーマやフォントを簡単に変更できる。

ダークモード対応:
標準でダークモードをサポートしている。

アクセシビリティ
アクセシビリティ機能が標準で組み込まれている。

利用準備

1.インストール

npm install react-native-paper 

2.開発環境
Expo: 52.0.46
react-native: 0.76.9
react-native-paoer: 5.13.1

3.レイアウトコンポーネント追記

_layout.tsx
const Layout = () => {
    return (
        <PaperProvider>
            <Stack screenOptions={{ headerShown: false }} />
        </PaperProvider>
    )
}

export default Layout

4.実装例

paperui_sample.tsx
import { router } from "expo-router"
import { StatusBar } from "expo-status-bar"
import { useState } from "react"
import { StyleSheet, View } from "react-native"
import { ScrollView } from "react-native-gesture-handler"
import { ActivityIndicator, Appbar, Avatar, Banner, Button, Checkbox, Divider, RadioButton, SegmentedButtons, Snackbar, Switch, Text, TextInput, useTheme } from "react-native-paper"


const PaperUISample = () => {
    const theme = useTheme()
    const [text, setText] = useState('')
    const [isLoading, setIsLoading] = useState(false)
    const [switchValue, setSwitchValue] = useState(false)
    const [checkboxStatus, setCheckboxStatus] = useState(false)
    const [radioValue, setRadioValue] = useState('')
    const [segmentValue, setSegmentValue] = useState('')
    const [snackbarVisible, setSnackbarVisible] = useState(false)
    const [bannerVisible, setBannerVisible] = useState(false)

    return (
        <View style={styles.container}>
            <StatusBar style="auto" />
            
            {/* 画面上部のナビゲーションバー */}
            {/* BackActionは戻るボタン */}
            {/* Contentはタイトル(画面名など)を表示 */}
            <Appbar.Header>
                <Appbar.BackAction onPress={() => router.back()} />
                <Appbar.Content title="UI Sample" />
            </Appbar.Header>
            
            {/* バナー表示 */}
            {/* visible:表示/非表示制御、actions:ボタンの配列、icon:バナーに表示するアイコン */}
            <Banner
                visible={bannerVisible}
                actions={[
                    {
                        label: '確認',
                        onPress: () => setBannerVisible(false)
                    },
                    {
                        label: '閉じる',
                        onPress: () => setBannerVisible(false)
                    }
                ]}
                icon={() => (
                    <Avatar.Icon size={40} icon='alert' />
                )}
            >
                バナーを開くとこちらの情報が表示されます。
            </Banner>

            {isLoading ? (
                <View style={styles.loadingContainer}>
                    {/* インジケーター */}
                    {/* size:'small' or 'large'で指定、animating:アニメーション有無、color:インジケーターの色 */}
                    <ActivityIndicator size="large" animating={true} color={theme.colors.primary} />
                    <Text style={[styles.loadingText, { color: theme.colors.primary }]} variant='labelLarge' >loading...</Text>
                    <Button
                        mode="outlined"
                        onPress={() => setIsLoading(false)}
                    >
                        戻る
                    </Button>
                </View>
            ) : (
                <ScrollView style={styles.scrollView}>
                    {/* テキスト入力 */}
                    <View style={styles.section}>
                        {/* ラベル表示 */}
                        {/* variant:ラベルのフォントサイズ */}
                        <Text variant='titleMedium' style={styles.sectionTitle}>
                            テキスト入力
                        </Text>

                        {/* テキスト入力フィールド */}
                        <TextInput
                            label='テキスト入力できます'
                            value={text}
                            onChangeText={text => setText(text)}
                        />
                    </View>
                    <Divider style={styles.divider} />
                    <View style={styles.section}>
                        <Text variant='titleMedium' style={styles.sectionTitle}>基本コンポーネント</Text>
                        <View style={styles.row}>
                            <Text variant='titleMedium'>スイッチ</Text>
                            
                            {/* on/offを切り替えるスイッチ */}
                            <Switch
                                value={switchValue}
                                onValueChange={() => setSwitchValue(!switchValue)}
                            />
                        </View>
                        <View style={styles.row}>
                            <Text variant="titleMedium">チェックボックス</Text>
                            {/* チェックボックス */}
                            <Checkbox
                                status={checkboxStatus ? "checked" : "unchecked"}
                                onPress={() => setCheckboxStatus(!checkboxStatus)}
                            />
                        </View>

                        <Text variant='titleMedium' style={styles.sectionTitle} >ラジオボタン</Text>

                        {/* ラジオボタン */}
                        {/* Group:ラジオボタングループ化 */}
                        {/* 各RadioButtonには一意のvalueを指定 */}
                        <RadioButton.Group
                            onValueChange={value => setRadioValue(value)}
                            value={radioValue}
                        >
                            <View style={styles.radioContainer}>
                                <View style={styles.radioRow}>
                                    <RadioButton value="1" />
                                    <Text>Radio 1</Text>
                                </View>
                                <View style={styles.radioRow}>
                                    <RadioButton value="2" />
                                    <Text>Radio 2</Text>
                                </View>
                            </View>
                        </RadioButton.Group>

                        <Text variant='titleMedium' style={styles.sectionTitle}>セグメントボタン</Text>

                        {/* セグメント選択UI */}
                        <SegmentedButtons
                            value={segmentValue}
                            onValueChange={setSegmentValue}
                            buttons={[
                                { value: 'walk', label: '散歩', icon: 'walk' },
                                { value: 'train', label: '電車', icon: 'train' },
                                { value: 'car', label: '', icon: 'car' }

                            ]}
                        />
                    </View>

                    <Divider style={styles.divider} />

                    <View style={styles.section}>
                        <Text variant='titleMedium' style={styles.sectionTitle}>アクション</Text>

                        {/* ボタン */}
                        {/* mode: テキスト、塗りつぶし、枠線付きなど */}
                        <Button
                            mode='contained'
                            onPress={() => setIsLoading(true)}
                            style={styles.button}
                        >
                            読み込み開始
                        </Button>
                        <Button
                            mode='contained'
                            onPress={() => setBannerVisible(true)}
                            style={styles.button}
                        >
                            バナー表示
                        </Button>
                        <Button
                            mode='contained'
                            onPress={() => setSnackbarVisible(true)}
                            style={styles.button}
                        >
                            スナックバー表示
                        </Button>
                    </View>
                </ScrollView>
            )}

            {/* スナックバー */}
            {/* visible:表示/非表示を制御、onDismiss:非表示にする時の関数、action:アクションボタン、duration:表示時間(ミリ秒) */}
            <Snackbar
                visible={snackbarVisible}
                onDismiss={() => setSnackbarVisible(!snackbarVisible)}
                action={{
                    label: '閉じる',
                    onPress: () => setSnackbarVisible(!snackbarVisible)
                }}
                duration={10000}
            >
                エラーや正常終了などのメッセージ表示に使います
            </Snackbar>
        </View>
    )



}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    loadingContainer: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        padding: 16
    },
    loadingText: {
        marginVertical: 16
    },
    scrollView: {
        flex: 1,
        padding: 16
    },
    section: {
        marginBottom: 16
    },
    sectionTitle: {
        marginBottom: 8
    },
    divider: {
        marginVertical: 16
    },
    row: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        paddingVertical: 8
    },
    radioContainer: {
        flexDirection: 'row',
        flexWrap: 'wrap',
        marginBottom: 16
    },
    radioRow: {
        flexDirection: 'row',
        alignItems: 'center',
        width: '40%'
    },
    button: {
        marginVertical: 8
    }

})

export default PaperUISample

実行結果(Androidエミュレータ)

当方の開発環境では、デフォルトカラー黄色で定義しているため、
全般的に黄色メインで画面表示されます。
(デフォルトは紫色になっております。)

初期表示

スクリーンショット 2025-04-22 18.19.53.png

画面入力後

スクリーンショット 2025-04-22 18.20.52.png

ローディング

スクリーンショット 2025-04-22 18.22.37.png

バナー表示

スクリーンショット 2025-04-22 18.23.53.png

スナックバー表示

スクリーンショット 2025-04-22 18.24.34.png
ダイアログやモーダルなどのコンポーネントもありますので、
また時間があれば追記していきたいと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?