Skip to content

Month: August 2020

How to build a wallpaper app in react native for beginners

React Native is a JavaScript framework to build native applications for Android and iOS. If you have the experience of developing mobile apps, you will find React Native surprisingly easy to work and create native apps.

Using the wallpaper app is a great way to make your screen personalized, and if you are into creating a special one by your programming skills, you are on the right track on this page. We are delighted that you have joint us in completing this app if you are new here.

In this article, I will create a wallpaper app using React Native that looks almost similar on both Android and iOS. It has a drawer, search functionality, as well as different tabs of Community, Explore, and Favorite (picture 1, inside the colored shape). So, join me if you want to create your React Native wallpaper app. This article covers everything you need, and the source code is on https://github.com/mdeveloper20/react-native-wallpaper . However, if you are a beginner and need detailed information, watch this series of YouTube videos on my channel about creating this wallpaper app.

Ok, Let’s kick start our project by introducing the useful tools of which we will take advantage. The first one would be the React Native website https://reactnative.dev/ .

https://expo.io/ is another useful tool enabling us to share our app with our friends in order to debug the created application without sending it to the app store. You can download it from google play store. Then, we need to install “Expo CLI” through either of the following codes by running it in terminal:

npm install expo-cli –global

yarn global add expo-cli

And “Expo font” through:

expo install expo-font

yarn add expo-font

Since this tutorial excludes the back-end side, I will use unsplash docs just as the API in this practice to get the required data and pictures (respecting the Unsplash policy).

Also, every necessary element of the user interface is taken from https://nativebase.io/ . You can install it using:

npm install native-base --save

The last tool is the BlueStacks that we will use to run the application.

Ok! Now every needed tool is ready for us to start coding.

  • App.js file:

Here is App.js in a simple form including a “homepage” component.  

import React from 'react';
import HomePage from './src/HomePage';

export default class App extends React.Component {
  constructor(props) {
    super(props);
  }

render() {

    return (
        <HomePage />
        );
     } 
  }

Both “npm start” and “expo start” commands in TERMINAL could be used to run the expo.

  • HomePage component:

As it is shown in picture 1, the homepage component has a header holding three tabs, as well as a search icon, and the capability of adding favorite pictures to the Favorite tab. So, we need to define states as:

state = {
        favorites: [],
        isExploreLoading: false,
        searchBar: false,
        query: ''
    }

Our favorite pictures on which we click will be added to the “favorites” array. “isExploreLoading” and “searchBar” will be used to render different parts of the application, and “query” is defined to provide search functionality for the users by typing image name.

As we said, we will use the native base library to create the user interface inside the “render”. You can scroll down the “component” section of this website to see different elements and choose a header. We will modify it in our application. According to the documentation, every element should be wrapped inside a “container” while using NativeBase. So, import the “Header” from NativeBase and locate it inside a container as following: (the … inside the code will be completed step by step in this article)

render() {

        return (
            <Container>
                <Header hasTabs searchBar={this.state.searchBar} >
                 …
                             </Header>
                <Tabs >
                 …
                 </Tabs>
            </Container >
        );
    }

1- Header:

1-1- searchBar:

The searchBar is displayed with an “input”, a “close” icon inside a transparent button, and a “search” icon if it’s been clicked. (The following picture)

and here is the “if condition” while we click on search: (Like me, you may prefer to use Ionicons.)

{this.state.searchBar ?
                        <>
                            <Item>
                                <Icon name="ios-search" />
                                <Input placeholder="Search" onChangeText={this.onQueryChange} />
                                <Button transparent onPress={() => this.onSearchClick()}>
                                    <Icon name="close" />
                                </Button>
                            </Item>
                            <Button transparent>
                                <Text>Search</Text>
                            </Button>

                        </> :

Also, the “onQueryChange” and “onSearchClick” functions outside of the render enable us to search for images by their name:

onQueryChange = (query) => {
        this.setState({ query })
    }

onSearchClick = () => {
        this.setState(state => ({ searchBar: !state.searchBar, query: '' }))
    }

When the search is not been clicked, we have a drawer and a menu icon inside a transparent button on its left side (picture 1). Also, there is a title next to the menu “My Wallpaper App”, and a “search icon” inside a transparent button on the right side. The “else condition” is:

                        <>
                            <Left>
                                <Button transparent onPress={() => this.props.openDrawer()}>
                                    <Icon name='menu' />
                                </Button>
                            </Left>
                            <Body>
                                <Title>My Wallpaper App</Title>

                            </Body>
                            <Right>

                                <Button transparent onPress={() => this.onSearchClick()}>

                                    <Icon name='search' />
                                </Button>
                            </Right>
                        </>
                    }

Make sure that you have imported “body” from “native-base” library.

1-2- Tabs:

According to the picture 1, there are three tabs right below the header component inside the container.

<Tabs >

     <Tab heading={<TabHeading><Text>Community</Text></TabHeading>}>
          <CommunityTab />
     </Tab>

     <Tab heading={<TabHeading><Text>Explore</Text></TabHeading>}>
          <ExploreTab />
     </Tab>

     <Tab heading={<TabHeading><Text>Favorites</Text></TabHeading>}>
          <FavoritesTab />
     </Tab>
</Tabs>

As you see, each tab has a specific component that we should import to the homepage component and call them inside the related tabs.

For now, we define these components in their simple form, and in the upcoming parts, we will complete them.

import React, { Component } from "react";
import {Text} from "react-native";


class CommunityTab extends Component {
    state = {};

    render() {
        <Text>Community Tab </Text>;
    }
}
export default CommunityTab;

We can create two other components by substituting their name in the above code.

Congrats! You’ve finished the first step of creating your App. In the next section, we will proceed with developing our app by creating the navigation bar (drawer).

  • Menu icon

We created a drawer at the top left of our application, and we will create what is inside the menu when it is clicked (picture 3)

Ok! Let’s start by creating a “SideBar” file and “index.js” inside it. Soon, we will connect it to the “App.js”. Like before, all the elements should be wrapped inside a “container:

import React, { Component } from 'react';
import { Container, Text, ListItem, Left, Icon, Body, Content } from 'native-base';
import { Grid, Row } from 'react-native-easy-grid';

export default class SideBar extends Component {
    render() {
        return <Container>
            <Grid >
                
              ...

            </Grid>
        </Container>

    }
}

Where we’ve already installed React Native Easy Grid by:

npm install react-native-easy-grid --save

As you see in the picture 4, we have two rows in this section inside the colored shapes.

picture 4

One of them holds the app name “My Wallpaper App”.

<Row style={styles.box} >
                    <Text My Wallpaper App</Text>
                </Row>

The other one includes three items inside a “container” with which the user is directed to other parts of the app. So, import the “ListItem” from native-base and:

<Row>
                    <Content>
                        <ListItem icon>
                            <Left>
                                <Icon active name="ios-arrow-dropright" />

                            </Left>
                            <Body>
                                <Text >Home Page</Text>
                            </Body>
                        </ListItem>
                        <ListItem icon>
                            <Left>
                                <Icon active name="ios-arrow-dropright" />

                            </Left>
                            <Body>
                                <Text >Contact Us</Text>
                            </Body>
                        </ListItem>
                        <ListItem icon>
                            <Left>
                                <Icon active name="ios-arrow-dropright" />

                            </Left>
                            <Body>
                                <Text >More Apps</Text>
                            </Body>
                        </ListItem>
                    </Content>
                </Row>

Here we aligned the rows to the left and then added “Home Page”, “Contact Us” and “More Apps” inside the body.

Connect the sideBar to the App.js:

First, import the component into the App.js file. Then, we will easily add the sideBar to it by wrapping it inside a “Drawer” component and passing the props:

<Drawer
          ref={(ref) => { this._drawer = ref; }}
          content={<SideBar />} >
          <Container>
            <HomePage openDrawer={this.openDrawer.bind(this)} />

          </Container>
        </Drawer>

and the functions outside the render with which the drawer is being opened and closed are:

closeDrawer() {
    this.drawer._root.close()
  };
  openDrawer = () => {
    this._drawer._root.open()
  };

Add style:

Good! If you run the code, you must have a simple page without any style, and if you have any problem concerning the coding, you may find this YouTube video tutorial or this Github source useful.

It’s time to take one step further and write a style for sidebar component and make the page beautiful. For this purpose we need to import StyleSheet by:

import { StyleSheet } from 'react-native';

and this is my style:

const styles = StyleSheet.create({
    box: {
        height: 200,
        alignItems: 'flex-end',
        backgroundColor: '#202991',
        padding: 20,
        marginBottom: 30
    },
    appName: {
        color: 'white',
        fontSize: 25
    },
    text: {
        color: 'black'
    },

});

You may also try your style. It could be more beautiful than mine, even. BTW, don’t forget to add your style to your components:

<Row style={styles.box} >
                    <Text style={styles.appName}>My Wallpaper App</Text>
                </Row>
                <Row>
                    <Content>
                        <ListItem icon>
                            <Left>
                                <Icon active name="ios-arrow-dropright" />

                            </Left>
                            <Body>
                                <Text style={styles.text}>Home Page</Text>
                            </Body>
                        </ListItem>
                        <ListItem icon>
                            <Left>
                                <Icon active name="ios-arrow-dropright" />

                            </Left>
                            <Body>
                                <Text style={styles.text}>Contact Us</Text>
                            </Body>
                        </ListItem>
                        <ListItem icon>
                            <Left>
                                <Icon active name="ios-arrow-dropright" />

                            </Left>
                            <Body>
                                <Text style={styles.text}>More Apps</Text>
                            </Body>
                        </ListItem>
                    </Content>
                </Row>

Well done! Pat yourself on the back for the next part of the App that you have completed. Next section, covers the “community” tab of three tabs of the “homepage” section

  • Community tab

Previously we created a simple community tab, and now we will complete it (colored shaped inside the following picture).

First of all, I import the “view” component, working as a container, from react-native and set a “style” for it will be complete later. Also, I need to import “FlatList” from react-native since I want to show items in a list. So, we have:

render() {

        return (
            <View style={styles.MainContainer} >

                <FlatList
                    data={this.state.users}
                    renderItem={this.renderRow}
                    keyExtractor={(i) => i.id}
                    onEndReachedThreshold={0.5}
                    onEndReached={() => !this.state.isLoading &amp;&amp; this.loadData(this.state.page + 1)}

                />
            </View>
        );
    }

in which the state of “users” are passed to “data” as props, the “keyExtractor” is used to specify a key for every element to prevent getting the warning, “onEndReached” displays new items while scrolling, and “renderItem” is a function outside of the render to execute every list item:

renderRow = ({ item }) => {

        return <ListItem thumbnail>
            <Left>
                <Thumbnail square source={{ uri: item.avatar }} />
            </Left>
            <Body>
                <Text>{item.name}</Text>
                <Text note numberOfLines={1}>{item.bio}</Text>
            </Body>
            <Right>
                <Button transparent>
                    <Text>View</Text>
                </Button>
            </Right>
        </ListItem>
    }

Do not forget to import “Left” and “Body” from native-base.

Now let’s go to the beginning of the loop to create the “style”. So, import the “StyleSheet” from react-native and define a container inside “const style”:

const styles = StyleSheet.create({
    MainContainer: {
        justifyContent: "center",
    }
})

Outside of the render define state as:

constructor(props) {
        super(props);

        this.state = {
            users: [],
            isLoading: true,
            page: 1

        }

    }

Also, call data through:

componentDidMount =async () => {

         await this.loadData(1);
         this.loadData(2)

    }

in which the “load Data” accepts the “page” as the variable. As we said before, since this tutorial does not cover backend, data is taken from unsplash.com using “fetch”. Then it will be converted to the JSON using “await” and will be set as the state in “setState”:

loadData = async (page) => {
        this.setState({
            isLoading: true
        });

        const response = await fetch(`https://api.unsplash.com/search/users?client_id=${accessKey}&amp;page=${page}&amp;query=community`);

        const data = await response.json();

        this.setState(state => ({
            users: [...state.users, ...data.results.map(i => ({
                id: i.id,
                name: i.first_name,
                avatar: i.profile_image?.medium,
                bio: i.bio
            }))],
            page,
            isLoading: false
        }));
    }

“map” is used to remove unnecessary elements in “setState”.

Congrats! You are one step closer to completing your specific app. Next sections are dedicated to “Explore” and “Favorites” tabs, respectively.

  • Explore tab

Let’s complete “Explore” tab by editing render function using the “View” container and set a style.

render() {

        return (
            <View style={styles.MainContainer} >

                <FlatList
                    data={this.state.images}
                    renderItem={this.renderRow}
                    numColumns={3}
                    keyExtractor={(i) => i.id}
                    onEndReachedThreshold={0.5}
                    onEndReached={() => !this.state.isLoading &amp;&amp; this.loadData(this.state.page + 1)}
                    onRefresh={() => this.onRefresh()}
                    refreshing={this.state.isRefreshing}
                />
            </View>
        );
    }

Where state of images is passed to the data props inside the “FlatList”. “FlatList” shows one item in every row by default, but it can be adjusted using “numColums”. “onEndReached” loads more data while the user scrolls and request more data from the server. “onRefresh” function, outside of render, helps us to get the first data after scrolling down:

onRefresh = () => {
        this.setState({
            images: [],
            isRefreshing: true
        }, () => this.loadData(1));
    }

In which the loader animation would be displayed while the page is getting refreshed.

Also, the “renderRow” function is specified to render every item:

renderRow = ({ item }) => {

        return <ImageItem addToFavorites={this.props.addToFavorites} item={item} />
    }

You may want to write the “ImageItem” inside the explore tab; However, I prefer to make a cleaner code using an “ImageItem” component as following:

import React, { Component } from 'react';
import { Text, Thumbnail, View, Icon } from 'native-base';
import { TouchableWithoutFeedback } from 'react-native';

class ImageItem extends Component {
    state = {
        isFavorite: false
    }

    render() {
        const { item } = this.props;
        return (<TouchableWithoutFeedback onPress={() => {
            this.props.addToFavorites(item);
            this.setState({
                isFavorite: !this.state.isFavorite
            })

        }}
        >
            <View style={{ flex: 1, flexDirection: 'column', margin: 1 }}>
                <Thumbnail style={styles.imageThumbnail} source={{ uri: item.url }} />
                <View style={styles.imageOverlay}>
                    {

                        this.state.isFavorite &amp;&amp; <Icon name='heart' style={{ fontSize: 50, color: 'red' }} />
                    }

                    {item.description &amp;&amp; <Text numberOfLines={1} style={styles.imageOverlayText}>{item.description}</Text>}
                </View>
            </View>
        </TouchableWithoutFeedback >);
    }
}

export default ImageItem;

Render first get the “item” from “props”. You may also use “TouchableOpacity” instead of “TouchableWithoutFeedback” to inform the user whether the item has been clicked or not by showing an animation. After setting props and state inside the “onPress” function, we will show our image within the “TouchableWithoutFeedback” using the “View” component. “Thumbnail” specifies images downloaded from the server, and the second “View” component adds a “heart” item and a description to the favorite image.

The only thing that is remained is the “style” for the “ImageItem” component. Here is what I wrote.

const styles = StyleSheet.create({

    imageThumbnail: {
        justifyContent: 'center',
        alignItems: 'center',
        width: '100%',
        height: 200,
        borderRadius: 0
    },

    imageOverlay: {
        position: 'absolute',
        width: '100%',
        height: '100%',
        justifyContent: 'flex-end',
        alignItems: 'center',
        backgroundColor: 'rgba(0,0,0,0.1)'
    },

    imageOverlayText: {
        color: 'white',
        textAlign: 'center',
        padding: 4,
        width: '100%',
        textShadowColor: 'rgba(0, 0, 0, 0.75)',
        textShadowOffset: { width: 1, height: 1 },
        textShadowRadius: 10,
        borderRadius: 10,
        backgroundColor: 'rgba(0, 0, 0, 0.75)',

    }
});

Note: Do not forget to import StyleSheet from react-native.

Back to the “Explore” tab, it’s time to define the “state” inside the “class component”:

state = {
        images: [],
        isLoading: true,
        isRefreshing: false,
        page: 1
    }

and the “componentDidMount” to load data from the first page:

componentDidMount() {
        this.loadData(1);
    }

On the other hand, if the user types something new, we need to get new data according to the new query. So:

componentDidUpdate(prevProps, prevState) {
        if (prevProps.query !== this.props.query) {
            this.loadData(1);
        }
    }

in which the “loadData” function in both of them is:

loadData = async (page) => {
        this.setState({
            isLoading: true
        });
        const query = this.props.query;
        this.props.setExploreLoader(true);

        const response = await fetch(`https://api.unsplash.com/search/photos?client_id=${accessKey}&amp;page=${page}&amp;query=${query ? query : 'wallpaper'}`);

        const data = await response.json();
        const newImages = data.results.map(i => ({
            id: i.id,
            url: i.urls.small,
            description: i.description,
            isFavorite: false
        }));

        this.setState(state => ({
            images: page === 1 ? newImages : [...state.images, ...newImages],
            page,
            isLoading: false,
            isRefreshing: false
        }));
        this.props.setExploreLoader(false);
    }

The “loadData“ function gets the query from the server after setting the state. Also, we have defined setExploreLoader function in order to show loading in the parent component, which will be used in the home component later on. Then, we need to call a fetch for a response and get data from it.

The map is applied to remove unnecessary data of images.

Also, the favorite list would not include an image unless the user clicks on it. So, “isFavorite” state is false for all the images at first.

Now, it’s time to set the new states in the “loadData” function. For the images, the new items should be displayed if we are on the first page; otherwise, both current items and new items should be displayed. “setExploreLoader” is false to hide loader from the home component.

Add Explore to the homepage

In this part, we will add this component to its parent component. So, open your homepage, and add the following function to the class component to pass state from child to the parent component:

setExploreLoader = (isExploreLoading) => {
        this.setState({
            isExploreLoading
        })
    }

The following changes should be added to the Explore tab inside the render, too, to show the loader animation in this tab bar, and also pass as a prop to the Explore tab

<Tab heading={<TabHeading>
        <Text>Explore</Text>
             {this.state.isExploreLoading &amp;&amp; <ActivityIndicator size='small' />}

             </TabHeading>}>
             <ExploreTab query={this.state.query} setExploreLoader={this.setExploreLoader} addToFavorites={this.addToFavorites} />
</Tab>

Do not forget to import “ActivityIndicator” in the heading lines:

import { ActivityIndicator } from "react-native";

and finally here is my designed style for “Explore” tab via importing StyleSheet from react-native.

const styles = StyleSheet.create({

    MainContainer: {
        justifyContent: 'center',
        flex: 1,
    },

});

  • Favorite Tab:

Using the “View” component again, we will start upgrading the favorite component inside the render.

render() {

        return (
            <View style={styles.MainContainer} >

                <FlatList
                    data={this.props.favorites}
                    renderItem={this.renderRow}
                    keyExtractor={(i) => i.id}
                    numColumns={2}
                    ListEmptyComponent={this.renderEmptyContainer()}

                />
            </View>
        );
    }

“FlatList” displays the items in which the props of favorites are passed to the “data”.

“renderRow” outside of the render gets an item and returns a “Thumbnail” as before:

renderRow = ({ item }) => {

        return <View style={{ flex: 1, flexDirection: 'column', margin: 1 }}>
            <Thumbnail style={styles.imageThumbnail} source={{ uri: item.url }} />

        </View >
    }

“ListEmptyComponent” shows the user a custom message if there is no data to display.

renderEmptyContainer = () => {
        return <View style={styles.emptyList} ><Text>The list is empty</Text></View>
}

and this is my style:

const styles = StyleSheet.create({

    MainContainer: {
        justifyContent: 'center',
        flex: 1,
    },

    imageThumbnail: {
        justifyContent: 'center',
        alignItems: 'center',
        width: '100%',
        height: 600,
        borderRadius: 0
    },
    emptyList: {
        marginTop: 100,
        justifyContent: 'center',
        alignItems: 'center'
    }

});

Connect Favorites to the homePage

Now we should pass the props from the homepage component to the favorite component. So, open your homepage component and take a look at the favorite state that we defined before, and pass it to the FavoritesTab using below code:

                        <FavoritesTab favorites={this.state.favorites} />

We should also pass “addToFavorites” function from the homepage to the “Explore” tab, and from the “Explore” tab to the “ImageItem” (“addToFavorites” was defined inside the “ImageItem” inside the “renderRow” function, and was passed to the explore tab, before).

“addToFavorites” function inside the homepage component gets item and callback:

addToFavorites = (item, cb) => {
        const favorites = Object.assign([], this.state.favorites);
        const index = favorites.findIndex(f => f.id === item.id);

        if (index === -1) {
            favorites.push(item);

        } else {
            favorites.splice(index, 1);

        }
        this.setState({
            favorites
        });

    }

const favorites assigns the favorites state to a variable, and the next const defines the index of the favorite picture. If the index is -1, it is not in the favorite array, and it should be added into it.

That’s it! Now go to the explore tab and click on some photos. You will find them in the favorite tab then.

Well done! Your wallpaper app is finished. Enjoy it. I hope that you have found it useful!

You may also be interested in other topics and series of programming. Here is my Youtube channel in case you are curious about developing your programming skills. Join us in the community of developers to make impressive progress.