How to do it...

Let's try to enhance our app a bit. And, to complete what we earlier saw about adaptive and responsive displays, we are going to provide a different layout for portrait and landscape orientations. We won't need media queries or column based layouts; we'll make do with simple styling. 

Let's begin by creating styles for the <Main> component. We'll be using the <DeviceHandler> we developed earlier; both components will be connected to the store. I didn't want to do specific versions for tablets and handsets, but I wanted to display a different layout for portrait and landscape orientations. For the former, I basically used what I had developed earlier, but for the latter, I decided to split the screen in half, displaying the countries selector on the left and the regions list on the right. Oh, and you may notice that I opted to use inline styles, even if it's not the preferred option; since components are usually short, you may place styles right in the JSX code without losing clarity. It's up to you to decide whether you like it or not:

// Source file: src/regionsStyledApp/main.component.js

/* @flow */

import React from "react";
import { View, StatusBar } from "react-native";

import {
ConnectedCountrySelect,
ConnectedRegionsTable,
ConnectedDeviceHandler
} from ".";
import type { deviceDataType } from "./device";

/* eslint-disable react-native/no-inline-styles */

export class Main extends React.PureComponent<{
deviceData: deviceDataType
}> {
render() {
if (this.props.deviceData.isPortrait) {
.
. // portrait view
.
} else {
.
. // landscape view
.
}
}
}

When the device is in portrait orientation, I created a <View>, occupying all the screen (flex:1) and setting its components vertically using flexDirection:"column", although this is actually the default value, so I could have omitted this. I didn't specify a size for the <CountrySelect> component, but I set the <RegionsTable> to occupy all possible (remaining) space. The detailed code is as follows:

// Source file: src/regionsStyledApp/main.component.js

return (
<View style={{ flex: 1 }}>
<StatusBar hidden />
<ConnectedDeviceHandler />
<View style={{ flex: 1, flexDirection: "column" }}>
<View>
<ConnectedCountrySelect />
</View>
<View style={{ flex: 1 }}>
<ConnectedRegionsTable />
</View>
</View>
</View>
);

For the landscape orientation, some changes were required. I set the direction for the contents of the main view to horizontal (flexDirection:"row") and I added two equal-sized views within. For the first, with the country list, I set its contents vertically and centered, because I thought it looked better that way, instead of appearing at the top. I didn't do anything in particular for the regions list that occupies the right side of the screen:

// Source file: src/regionsStyledApp/main.component.js

return (
<View style={{ flex: 1 }}>
<StatusBar hidden />
<ConnectedDeviceHandler />
<View style={{ flex: 1, flexDirection: "row" }}>
<View
style={{
flex: 1,
flexDirection: "column",
justifyContent: "center"
}}
>
<ConnectedCountrySelect />
</View>
<View style={{ flex: 1 }}>
<ConnectedRegionsTable />
</View>
</View>
</View>
);

If you want a component to occupy a larger piece of space, increase its flex value; flex implies that components will flexibly expand or shrink according to the available space, which is shared among all components in direct proportion to their flex values. If I had wanted the countries list to occupy one third of the screen, leaving the other two thirds to the regions list, I would have set flex:1 for it, and flex:2 for the regions. Of course, you could also set heights and widths directly (in either DIP values or as percentages), as you could have done with CSS.

As for distributing children in a view, apart from "center", which centers all children in the parent view, you also have several other options:

  • "flex-start" places them together, at the start of the parent view; here, it's top, given the vertical alignment
  • "flex-end" would have behaved similarly, but placed the children at the end (here, the bottom) of the parent view
  • "space-between" splits the extra space equally between the children components
  • "space-around" also splits extra space equally, but includes space at the start and at the end of the parent view
  • "space-evenly" splits all space equally between children and dividing spaces

After setting how the components will be laid out in the main flex direction, you can use alignItems to specify how the children will be aligned along the secondary flex direction (if flexDirection is "row", then the secondary direction will be "column", and vice versa). Possible values are "flex-start", "center", and "flex-end", with similar meaning to what was just given, or you could use "stretch", which will occupy all possible space.

If you want to experiment with these options, go to https://facebook.github.io/react-native/docs/flexbox and modify the code examples. You'll immediately see the effects of your changes, which is the easiest way to understand the effects and implications of each option.

Now, let's style the regions table. For this, I had to make some changes, starting with the need for a <ScrollView> instead of a plain <View>, given that the list may be too long to fit in the screen. Also, to show you some styles and constants, I decided to go with separate style files. I started by creating a styleConstants.js file, which defines a color constant and a simple, full-sized style:

// Source file: src/regionsStyledApp/styleConstants.js

/* @flow */

import { StyleSheet } from "react-native";

export const styles = StyleSheet.create({
fullSize: {
flex: 1
}
});

export const lowColor = "lightgray";

The interesting thing here, rather than the (assumedly quite Spartan) fullSize style, is the fact that you can export styles, or define simple JS constants that will be used elsewhere. In the regions list, I imported both the style and the color:

// Source file: src/regionsStyledApp/regionsTable.component.js

/* @flow */

import React from "react";
import PropTypes from "prop-types";
import { View, ScrollView, Text, StyleSheet } from "react-native";

import type { deviceDataType } from "./device";

import { lowColor, fullSizeStyle } from "./styleConstants";

const ownStyle = StyleSheet.create({
grayish: {
backgroundColor: lowColor
}
});

export class RegionsTable extends React.PureComponent<{
deviceData: deviceDataType,
list: Array<{
regionCode: string,
regionName: string
}>
}> {
static propTypes = {
deviceData: PropTypes.object.isRequired,
list: PropTypes.arrayOf(PropTypes.object).isRequired
};

static defaultProps = {
list: []
};

render() {
if (this.props.list.length === 0) {
return (
<View style={ownStyle.fullSize}>
<Text>No regions.</Text>
</View>
);
} else {
const ordered = [...this.props.list].sort(
(a, b) => (a.regionName < b.regionName ? -1 : 1)
);

return (
<ScrollView style={[fullSizeStyle, ownStyle.grayish]}>
{ordered.map(x => (
<View key={`${x.countryCode}-${x.regionCode}`}>
<Text>{x.regionName}</Text>
</View>
))}
</ScrollView>
);
}
}
}

There are some interesting details here in the preceding block of code:

  • As I said before, I'm using a <ScrollView> component to enable the user can browse through lists that are longer than the available space. A <FlatList> component would also have been a possibility, though for relatively short and simple lists as here, it wouldn't have made much of a difference.
  • I used the imported color to create a local style, grayish, which I used later.
  • I directly applied the imported fullSize style to the regions' <ScrollView>.
  • I applied more than one style to the second <ScrollView>; if you provide an array of styles, they get applied in the order of appearance. In this case, I got a full-sized gray area. Note that the color is only applied if some regions are present; otherwise, the color is unchanged.

Note that the style can be created dynamically, and that allows for interesting effects. To use an example based upon one in RN's documentation at https://facebook.github.io/react-native/docs/stylesheet, you could have a title changing style depending on a prop. In the following code, the style for the title would change depending on this.props.isActive:

<View>
<Text
style={[
styles.title,
this.props.isActive
? styles.activeTitle
: styles.inactiveTitle
]}
>
{this.props.mainTitle}
</Text>
</View>

You could produce even more interesting results; remember that you have the full power of JS available to you, and that a style sheet can be created on the fly, so you actually have limitless possibilities.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.185.180