Saturday, November 28, 2020

Build Weather App in React Native 2021

                                                          Weather App

        In last article we seen Image gallery app. In this article we see how to build weather app. We have an App component that represents the entire screen and contains the weather information displayed to the use. Inside of this component, we have a SearchInput component that allows us to search for different cities. App is the only component created with a default application using a blank template. 

Lets take a look at its file:

_______________________________________________________

import React from 'react';

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

 

export default class App extends React.Component {

 render() {

 return (

 <View style={styles.container}>

 <Text>Open up App.js to start working on your app!</Text>

 </View>

 );

 }

 }

 const styles = StyleSheet.create({

 container: {

 flex: 1,

 backgroundColor: '#fff',

 alignItems: 'center',

 justifyContent: 'center',

 },

});

_______________________________________________________________________________________________

We can also attach methods as properties to classes and the same applies to component classes in react native. We can see we already have one for this component,

the render method:

render() {

return (

  <View style={styles.container}>

<Text>Open up App.js to start working on your app!</Text>

</View>

);

}

_______________________________________________________________________________________________

What we see on our device when launching our device matches what we see described in this method. The render() technique is the main required strategy for a React Native part. React Native uses the return an incentive from this technique to determine what to deliver for the component. When we use React Native, we represent different parts of our application as components. This means we can build our app using different reusable pieces of logic with each piece displaying a specific part of our UI. Let’s break down what we already have in terms of components:


  • Our entire application is rendered with App as our top-level component. Although created automatically as part of setting up a new Expo CLI project, this component is a custom component responsible for rendering what we need in our application
  • The View component is used as a layout container.
  • Within View, we use the Text component to display lines of text in our application. Unlike App, both View and Text are built-in React Native components that are imported and used in our custom component.

Our App component uses and returns an HTML-like structure. This is JSX, which is an extension of JavaScript that allows us to use an XML-like syntax to define our UI structure.

JSX

When we build an application with React Native, components ultimately render native views which are displayed on our device. As such, the render() method of a component needs to describe how the view should be represented. In other words, React Native allows us to describe a component’s iOS and Android representation in JavaScript. JSX was created to make the JavaScript representation of components easier to understand. It allows us to structure components and show their hierarchy visually in markup. Consider this JSX snippet: 

 <View>

<Text style={{ color: 'red' }}>

Hello, friend! I am a basic React Native component.

</Text>

</View>

In here, we’ve nested a Text component within a View component. Notice how we use braces ({}) around an object ({ color: 'red' }) to set the style property value for Text. In JSX, braces are a delimiter, signaling to JSX that what resides in-between the braces is a JavaScript expression. The other delimiter is using quotes for strings, like this:

<TextInput placeholder="This is a string" />


Props

Props allow us to pass parameters to components to customize their features. Here, View is used to layout the entire content of the screen. We only have a single prop attached, style, that allows us to pass in style parameters to adjust how our View component is rendered on our devices. Each built-in component provided by React Native has its own set of valid props that we can use for customization Like our View component, many components in React Native accept a style prop. However, we can take a look at our styles object at the bottom of App.js and get an idea of how it works:

     To build our weather app, we’ll start with layout and styling. Once we have some of the essence of our weather app in place we can begin to explore strategies for managing data. we want our app to display the city, temperature, and weather conditions as separate text fields. Although we’ll eventually interface with a weather API in order to retrieve actual data.

Adding styles:

To get a better handle on styling, let’s try adding an object with a  color attribute to one of the text fields:

<View style={styles.container}>

<Text style={{ color: 'red' }}>

Open up App.js to start working on your app!

</Text>

</View>


Although we can style our entire component this way, a lot of inline styles (or style attributes defined directly within the delimeter of the style prop) used in a component can make things harder to read and digest. We can solve this by leveraging React Native’s Stylesheet API to separate our styles from our component. With Stylesheet, we can create styles with attributes similar to CSS stylesheets. We can see that Stylesheet is already imported at the top of the file. It’s used to declare our first style, styles.container, which we use for View. We can add a new style called red to our styles:

const styles = StyleSheet.create({

container: {

flex: 1,

backgroundColor: '#fff',

alignItems: 'center',

justifyContent: 'center',

},

red: {

color: 'red',

},

});

We’ll then have Text use this style:

<View style={styles.container}>

<Text style={styles.red}>

Open up App.js to start working on your app!

</Text>

</View>

If we save our file and take a look at our app, we can see that the end result is the same. Now let’s add some appropriate styles and text fields in order to display some weather data for a location. To add multiple styles to a single component, we can pass in an array of styles: 

<Text style={[styles.largeText, styles.textStyle]}>

 San Francisco

 </Text>

 <Text style={[styles.smallText, styles.textStyle]}>

 Light Cloud

 </Text>

 <Text style={[styles.largeText, styles.textStyle]}>24°</Text>

It is important to mention that when passing an array, the styles at the end of the array take precedence over earlier styles, in case of any repeated attributes. We can see that we’re referencing three new styles; textStyle, smallText, and largeText. Let’s define these within our styles object:

const styles = StyleSheet.create({

 container: {

 flex: 1,

 backgroundColor: '#fff',

 alignItems: 'center',

 justifyContent: 'center',

 },

 textStyle: {

 textAlign: 'center',

 fontFamily:

 Platform.OS === 'ios' ? 'AvenirNext-Regular' : 'Roboto',

 },

 largeText: {

 fontSize: 44,

 },

smallText: {

 fontSize: 18,

 },

  • textStyle specifies an alignment (center) as well as the fontFamily. Notice how we use Platform to define platform specific fonts for both iOS and Android. We do this because both operating systems provide a different set of native fonts. 
  • smallText and largeText both specify different font sizes.
Platform is a built-in React Native API. We’ll need to make sure to import it:

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

Let’s take a look at our application now:


 Platform specific properties

The Platform API allows us to conditionally apply different styles or properties in our component based on the device’s operating system. The OS attribute of the object returns either ios or android depending on the user’s deviceAlthough this is a relatively simple way to apply different properties in our application based on the user’s device, there may be scenarios where we may want our component to be substantially different between operating systems. We can also use the Platform. select method that takes the operating system as keys within an object and returns the correct result based on the device:

textStyle: {

 textAlign: 'center',

 ...Platform.select({

 ios: {

 fontFamily: 'AvenirNext-Regular',

 },

 android: {

 fontFamily: 'Roboto',

 },

 }),

 },

Separate files Instead of applying conditional checks using Platform.OS a number times throughout the entire component file, we can also leverage the use of platform specific files instead. We can create two separate files to represent the same component each with a different extension: .ios.js and .android.js. If both files export the same component class name, the React Native packager knows to choose the right file based on the path extension.

Text input

We now have text fields that display the location, weather condition, and temperature. The next thing we need to do is provide some sort of input to allow the user to search for a specific city. Again, we’ll continue using hardcoded data for now. We’ll only begin using an API for real data once we have all of our components in place. React Native provides a built-in TextInput component that we can import into our component that allows us to accept user input. 

<Text style={[styles.largeText, styles.textStyle]}>

San Francisco

</Text>

<Text style={[styles.smallText, styles.textStyle]}>

Light Cloud

</Text>

<Text style={[styles.largeText, styles.textStyle]}>24°</Text>

<TextInput

autoCorrect={false}

placeholder="Search any city"

placeholderTextColor="white"

style={styles.textInput}

clearButtonMode="always"/>

There are a number of props associated with TextInput that we can use. Here we’re specifying a placeholder, its color, as well as a style for the component itself. Let’s create its style object, textInput, underneath our other styles:

smallText: {

fontSize: 18,

},

textInput: {

backgroundColor: '#666',

color: 'white',

height: 40,

width: 300,

marginTop: 20,

marginHorizontal: 20,

paddingHorizontal: 10,

alignSelf: 'center',

},

Now let’s take a look at our application:

We can see that the text input has a default underline on Android. We’ll go over how to remove this in a bit. We’ve also specified the clearButtonMode prop to be always. This shows a button on the right side of the input field when characters are inserted that allows us to clear the text. This is only available on iOS.


 We can now type into the input field! However one thing you may have noticed is that when you focus on the input field with a tap, the keyboard pops up and covers it on Android and comes quite close on iOS.


               Since the virtual keyboard can cover roughly half the device screen, this is a common problem that occurs when using text inputs in an application. Fortunately, React Native includes KeyboardAvoidingView, a component that solves this problem by allowing us to adjust where other components render in relation to the virtual keyboard. Let’s import and use this component instead of View:

render() {

return (

<KeyboardAvoidingView

style={styles.container}

behavior="padding"

> 

<Text style={[styles.largeText, styles.textStyle]}>

San Francisco

</Text>

<Text style={[styles.smallText, styles.textStyle]}>

Light Cloud

</Text>

<Text style={[styles.largeText, styles.textStyle]}>24°</Text>

<TextInput

autoCorrect={false}

placeholder="Search any city"

placeholderTextColor="white"

style={styles.textInput}

clearButtonMode="always"

/>

</KeyboardAvoidingView>

);

}


KeyboardAvoidingView accepts a behavior prop with which we can customize how the keyboard adjusts. It can change its height, position or bottom padding in relation to the position of the virtual keyboard. Here, we’ve specified padding. And finally, it’s important to double check our imports to make sure we have everything that we’re using

import React from 'react';

 import {

 StyleSheet,

 Text,

 KeyboardAvoidingView,

 Platform,

 TextInput,

 } from 'react-native';


Now tapping the text input will shift our component text and input fields out of the way of the software keyboard.


Custom components

we’ve explored how to add styling into our application, and we’ve included some built-in components into our main App component. We use View as our component container and import Text and TextInput components in order to display hardcoded weather data as well as an input field for the user to change locations. It’s important to re-iterate that React Native is component-driven. We’re already representing our application in terms of components that describe different parts of our UI without too much effort, and this is because React Native provides a number of different built-in components that you can use immediately to shape and structure your application. However, as our application begins to grow, it’s important to begin thinking of how it can further be broken down into smaller and simpler chunks. We can do this by creating custom components that contain a small subset of our UI that we feel fits better into a separate, distinct component file. This is useful in order to allow us to further split parts of our application into something more manageable, reusable and testable. Although our application in its current state isn’t extremely large or unmanageable, there’s still some room for improvement. The first way we can refactor our component is to move our TextInput into a separate component to hide its implementation details from the main App component. Let’s create a components directory in the root of the application with the following file:

── components/

 - SearchInput.js

All the custom components we create that we use in our main App component will live inside this directory. For more advanced apps, we might create directories within components to categorize them more specifically. Since this app is pretty simple, let’s use a flat components directory. The SearchInput will be our first custom component so let’s move all of our code for TextInput from App.js to SearchInput.js

import React from 'react';

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

 

 export default class SearchInput extends React.Component {

 render() {

 return (

 <View style={styles.container}>

 <TextInput

 autoCorrect={false}

 placeholder={this.props.placeholder}

 placeholderTextColor="white"

 underlineColorAndroid="transparent"

 style={styles.textInput}

clearButtonMode="always"

 />

 </View>

 );

 }

 }

 

 const styles = StyleSheet.create({

 container: {

 height: 40,

 width: 300,

 marginTop: 20,

 backgroundColor: '#666',

 marginHorizontal: 40,

 paddingHorizontal: 10,

 borderRadius: 5,

 },

 textInput: {

 flex: 1,

 color: 'white',

 },

 });

Let’s break down what this file contains: 

• We export a component named SearchInput. 

• This component accepts a placeholder prop. 

• This component returns a React Native TextInput with a few of its properties specified wrapped within a View. 

• We’ve applied the appropriate styles to our view container including a borderRadius. 

• We also added underlineColorAndroid="transparent" to remove the dark underline that shows by default on Android.

Custom props

 As you may recall, in App.js we set the placeholder prop for TextInput to “Search any city.” That renders the text input with a placeholder: For SearchInput, we could hardcode a string again for placeholder. We can also create props for custom components that we build as well. That’s what we do here in SearchInput. The component accepts the prop placeholder. In turn, SearchInput uses this value to set the placeholder prop on TextInput. The way data flows from parent to child in React Native is through props. When a parent renders a child, it can send along props the child depends on. A component can access all its props through the object this.props. If we decide to pass down the string "Type Here" as the placeholder prop, the this.props object will look like this: { "placeholder": "Type Here" } In here, we’ll set up App to render SearchInput which means that App is the parent of SearchInput. Our parent component will be responsible for passing down the actual value of placeholder. 

Importing components

In order to use SearchInput in App, we need to import the component first. We can remove the TextInput logic from App.js and have App use SearchInput instead:

import React from 'react';

import {

StyleSheet,

Text,

KeyboardAvoidingView,

Platform,

} from 'react-native';

import SearchInput from './components/SearchInput';

export default class App extends React.Component {

render() {

return (

<KeyboardAvoidingView

style={styles.container}

behavior="padding"

> 

<Text style={[styles.largeText, styles.textStyle]}>

San Francisco

</Text>

<Text style={[styles.smallText, styles.textStyle]}>

Light Cloud

</Text>

<Text style={[styles.largeText, styles.textStyle]}>24°</Text>

<SearchInput placeholder="Search any city" />

</KeyboardAvoidingView>

);

}

}

    By moving the entire TextInput details into a separate component called SearchInput, we’ve made sure to not have any of its specific implementation details showing in the parent component anymore. We can also remove the text input’s styling defined within the styles object. React Native was built in order to allow us to layout our entire application in terms of self-contained components, and that means we should separate parts of our application into distinct units with custom functionality attached to them. This allows us to build a more manageable application that’s easier to control and understand. 

Background image

we can make our application more visually appealing by displaying a background image that represents the current weather condition. we’ve included a number of images for various weather conditions. If you inspect the weather/assets directory, you’ll find images like clear.png, hail.png, and showers.png. If you’re following along, copy these two folders over from the sample code into your project: 

1. weather/assets 

2. weather/utils

With the assets and utils folders copied over, let’s update our App component:

import React from 'react';

import {

StyleSheet,

View,

ImageBackground,

Text,

KeyboardAvoidingView,

Platform,

} from 'react-native';

import getImageForWeather from './utils/getImageForWeather';

import SearchInput from './components/SearchInput';

export default class App extends React.Component {

render() {

return (

<KeyboardAvoidingView

style={styles.container}

behavior="padding"

> 

<ImageBackground

source={getImageForWeather('Clear')}

style={styles.imageContainer}

imageStyle={styles.image}

> 

<View style={styles.detailsContainer}>

<Text style={[styles.largeText, styles.textStyle]}>

San Francisco

</Text>

<Text style={[styles.smallText, styles.textStyle]}>

Light Cloud

</Text>

<Text style={[styles.largeText, styles.textStyle]}>

24°

</Text>

<SearchInput placeholder="Search any city" />

</View>

</ImageBackground>

</KeyboardAvoidingView>

);

}

}


In this component, we’re importing a getImageForWeather method from our utils directory which returns a specific image from the assets directory depending on a weather type. For example, getImageForWeather('Clear') returns the following image:





Previous Post
Next Post

post written by:

Hello guys, myself Pooja Tirmare. I want to share my knowledge with people so they can learn react native and make beautiful applications.