はじめに
React NativeのUIコンポーネントライブラリとしてよく知られているNativeBaseですが、Vue Nativeでも簡単に組み込んで利用することができます。
個人的に開発しているVue NativeアプリケーションでもNativeBaseを利用しているのですが、StyleProvider
を用いたテーマの適用で少しハマったので、その時の解消法を紹介します!
環境構築
初めにNativeBaseを利用したVue Nativeアプリケーションの環境構築を行います。
Vue Nativeプロジェクトの作成
公式ドキュメントを参考に以下のコマンドで作成します。
$ npm install -g vue-native-cli
$ vue-native init <projectName> // Initializes crna project
$ vue-native init <projectName> --no-crna // Initializes react-native project
$ cd <project-name>
また、Vue Nativeプロジェクトの作成が完了したら以下のコマンドでアプリケーションを起動することができます。
$ npm start
NativeBaseインストール
こちらも公式ドキュメントを参考に以下のコマンドよりインストールを行います。
$ npm install native-base --save
インストール完了後、以下のようにGlobalにNativeBaseのコンポーネントを登録し、Vue Nativeアプリケーション内から利用することができます。
import Vue from "vue-native-core";
import { VueNativeBase } from "native-base";
// registering all native-base components to the global scope of the Vue
Vue.use(VueNativeBase);
簡単なVue Nativeアプリケーションの作成
環境構築が完了したら、NativeBaseを利用した簡単なVue Nativeアプリケーションを作成します。
作成にあたっては、以下のサンプルアプリケーションを参考に行います。
https://github.com/GeekyAnts/KitchenSink-Vue-Native
<template>
<app-loading v-if="!isAppReady" />
<app v-else />
</template>
<script>
import Vue from 'vue-native-core';
import { VueNativeBase } from 'native-base';
import { AppLoading } from 'expo';
import App from '../App.vue';
// ここでNativeBaseのコンポーネントをGlobalに登録
Vue.use(VueNativeBase);
export default {
components: { App, AppLoading },
data: function() {
return {
isAppReady: false,
};
},
created: function() {
this.loadFonts();
},
methods: {
loadFonts: async function() {
try {
this.isAppReady = false;
await Expo.Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
Ionicons: require('@expo/vector-icons/fonts//Ionicons.ttf'),
});
this.isAppReady = true;
} catch (error) {
console.log('some error occured', error);
this.isAppReady = true;
}
},
},
};
</script>
また、今回スクリーンはHomeのみ実装し、NativeBaseのnb-text
コンポーネントを表示します。
<template>
<view class="container">
<nb-button block primary>
<nb-text>Primary</nb-text>
</nb-button>
</view>
</template>
<script>
export default {
props: {
navigation: {
type: Object,
},
},
};
</script>
<style>
.container {
background-color: white;
align-items: center;
justify-content: center;
flex: 1;
}
.text-color-primary {
color: blue;
}
</style>
Simulatorでアプリケーションを起動すると、ボタンが中央に表示されたHomeスクリーンが表示されます。

現状ではデフォルトのデザインが適用されていますが、これからアプリケーションのテーマをカスタマイズしていきます!
StyleProviderを利用してMaterial Designのテーマを適用
NativeBaseではStyleProvider
を利用してアプリケーションのテーマをカスタマイズすることが出来ます。
https://docs.nativebase.io/Customize.html#Customize
そこで今回はMaterial Designのテーマ利用してカスタマイズします。
native-base-theme取得
カスタマイズにあたって、初めに以下のコマンドを実行し、native-base-theme
を取得します。
# 以下のコマンドでnative-base-themeを生成
$ node node_modules/native-base/ejectTheme.js
するとRootディレクトリ配下に、native-base-theme
ディレクトリが生成されます。
native-base-theme
にはcomponent
とvariables
ディレクトリが含まれており、今回はディレクトリをsrc/theme
配下に移動させました。
# 生成したファイル群をsrc/theme配下に移動
$ mv native-base-theme src/theme
StyleProviderでカスタマイズ
native-base-theme
を取得したら、StyleProvider
コンポーネントを利用してアプリケーションのテーマをカスタマイズします。
カスタマイズの方法は、テーマを反映させたいコンポーネントをStyleProvider
コンポーネントでラップしてあげることで実現できます。
その際、適用したいテーマをvariables
からimportし、theme
メソッドを介してStyleProvider
のstyle
プロパティにて指定します。
StyleProvider
コンポーネントもVue.use(VueNativeBase);
により、nb-style-provider
としてGlobalなVue.jsコンポーネントで定義されているため、以下のように実装しました。
<template>
<app-loading v-if="!isAppReady" />
<!-- nb-style-providerコンポーネントを追記し、Material Designのstyleを指定 -->
<nb-style-provider v-else :style="getTheme(material)">
<app />
</nb-style-provider>
</template>
<script>
// ・・・省略
// getTheme, materialをimport
import getTheme from '../theme/components';
import material from '../theme/variables/material';
// ・・・省略
export default {
components: { App, AppLoading },
data: function() {
return {
isAppReady: false,
// getTheme, materialをそれぞれdataに追加
getTheme: getTheme,
material: material,
};
},
// ・・・省略
};
</script>
Warningが発生
上記実装を行ったところ、以下のようなWarningが発生しました。

Warning: Failed prop type: Invalid prop
style
of typearray
supplied toStyleProvider
, expectedobject
.
内容としては、StyleProvider
はstyle
プロパティにObject型を期待しているようですが、nb-style-provider
コンポーネントから渡していたgetTheme(material)
がArray型で渡されてしまっていたようです。
ただgetTheme(material)
の返却値を確認してみてもObject型で返却されているため、内部的にVue.jsコンポーネントからReactコンポーネントへ変換される時にstyle
プロパティがArray型として変換されているのかも。。。
(ここはまだちゃんと調べられていないため、間違っていたらすみません)
修正方法を探していたところ、GithubのIssueにも同様のコメントをしている方を発見。
https://github.com/GeekyAnts/KitchenSink-Vue-Native/issues/6
上記Issueのコメントを確認してみると以下のような回答がされておりました。
You'll have to create a separate react component to use theme for now and then import that theme as root component within your vue file.
現状、StyleProvider
を利用してテーマを適用するためにはReactコンポーネントを実装して、Vue.jsのファイルからimportして利用する必要があるようでした。
NativeBase用のプラグインを実装
上記を踏まえて、nb-style-provider
コンポーネントは利用せず、新たにMaterial Designのテーマを適用したReactコンポーネント(StyleProviderMaterial
)を実装します。
ただ、他のNativeBaseコンポーネントがVue.jsのGlobalなコンポーネントとして定義されているのに対し、StyleProviderMaterial
だけ毎回importして利用するのは面倒臭いし、違和感がある。。。
そこで、NativeBase用のプラグインを自作し、Vue.use()
の際、ReactコンポーネントStyleProviderMaterial
をVue.jsコンポーネントnb-style-provider-material
としてGlobalに登録するようにしました。
Vue.jsのプラグインについてはこちら。
import React, { Component, Children } from 'react';
import { VueNativeBase, StyleProvider } from 'native-base';
import getTheme from '../theme/components';
import material from '../theme/variables/material';
// ReactコンポーネントとしてMaterial esignのテーマを指定したStyleProviderを生成
class StyleProviderMaterial extends Component {
render() {
const { children } = this.props;
return (
<StyleProvider style={getTheme(material)}>
{Children.only(children)}
</StyleProvider>
);
}
}
// Vue.jsプラグインの作成
const NativeBasePlugin = {
install(Vue, options) {
// ついでにVueNativeBase.install(Vue)の呼び出しもこちらで行う
Vue.use(VueNativeBase);
// 上記で作成したReactコンポーネントをVue.jsのglobalなコンポーネントとして設定
Vue.component('nb-style-provider-material', StyleProviderMaterial);
},
};
export default NativeBasePlugin;
nb-style-provider-materialの利用
作成したプラグインを呼び出し、Warningが発生していたnb-style-provider
をnb-style-provider-material
に置き換えます。
<template>
<app-loading v-if="!isAppReady" />
<!-- plugins/native-base.jsでglobalに定義したnb-style-provider-materialコンポーネント -->
<nb-style-provider-material v-else>
<app />
</nb-style-provider-material>
</template>
<script>
import Vue from 'vue-native-core';
import { AppLoading } from 'expo';
import App from '../App.vue';
// getTheme, materialのimportを削除し、NativeBaseのプラグインをimport
import NativeBase from '../plugins/native-base.js';
// プラグインのNativeBase.install(Vue)を呼び出し
Vue.use(NativeBase);
export default {
components: { App, AppLoading },
data: function() {
return {
isAppReady: false,
// getTheme, materialの定義を削除
};
},
// ・・・省略
};
</script>
Material Designテーマの適用
改めてSimulatorを確認したところ、Warningが解消され、Material Designのテーマが適用されていることが確認できました!
