Screen Layouts


Each screen can have multiple layouts. App owners can choose which screen layout they want to use in their app (e.g. for news from politics they might want to use a layout with smaller images, and for fashion they might want large images). As screen layouts are just plain screens, they can contain different logic than the screen they are altering and can be easily used for A/B testing.

We’ll create a different layout in the Restaurants extension from the My First Extension tutorial, which you can get here. The Restaurants extension has a List screen (for listing all the restaurants) and a Details screen (for details of one particular restaurant).

Let’s add one additional screen that will represent an alternative layout for the List screen where we’ll use smaller images as shown here:

Switch over to the extension folder:

$ cd restaurants

Create an additional screen:

$ shoutem screen add SmallList
Screen `SmallList` is created in file `app/screens/SmallList.js`!
File `app/extension.js` was modified.
File `extension.json` was modified.

Again, notice how we didn’t create a Shortcut. That’s because SmallList isn’t the starting screen of the extension, List is. Now extend the List screen and override its renderRow method. We’re going to use the Row component from UI toolkit. This is the complete code for SmallList.js file with the main parts being highlighted.

#file: app/screens/SmallList.js
import React from 'react';
import {
  TouchableOpacity
} from 'react-native';
import {
  Image,
  Row,
  View,
  Subtitle,
  Caption,
  Divider,
  Icon
} from '@shoutem/ui';

import {
  List
} from './List';

import {
  find,
  getCollection
} from '@shoutem/redux-io';
import { connect } from 'react-redux';
import { navigateTo } from '@shoutem/core/navigation';
import { ext } from '../extension';

export class SmallList extends List {

  // overriding the renderRow function
  renderRow(restaurant) {
    const { navigateTo } = this.props;

    return (
      <TouchableOpacity onPress={() => navigateTo({
          screen: ext('Details'),
          props: { restaurant }
        })}>
        <Row>
          <Image style={{width:90, height:70}} source={{ uri: restaurant.image && restaurant.image.url }} />
          <View styleName="vertical">
            <Subtitle>{restaurant.name}</Subtitle>
            <Caption>{restaurant.address}</Caption>
          </View>
          <Icon name="right-arrow" styleName="disclosure"/>
        </Row>
        <Divider styleName="line" />
      </TouchableOpacity>
    );
  }
}

export default connect(
    (state) => ({
    restaurants: getCollection(state[ext()].allRestaurants, state)
  }),
  { navigateTo, find }
)(SmallList);

The Restaurants extension uses CMS settings page, so app owners can manage data in the app. We need to give them option to chose which layout they want to use. To do that, we’re going to use the layout settings page from the shoutem-layouts extension.

The layout settings page resolves which screens have multiple layouts and shows the layout selectors for them. Example of two layout selectors for Shoutem’s News RSS extension is shown below.

For the layout settings page to be able to resolve which screens should show layout selectors, we need to add the extends property to screens which act as additional layouts, referencing the “default” layout. Screen layouts inherit all the properties from extending screens and can override them.

Reference the layout settings page in the adminPages shortcut and extend the screens in extension.json.

#file: extension.json
{
  "name": "restaurants",
  "version": "0.0.1",
  "platform": "1.0.*",
  "title": "Restaurants",
  "description": "List of restaurants",
  "screens": [{
    "name": "List",
    "title": "List with large images",
    "image": "./server/assets/large-image-list.png"
  }, {
    "name": "Details"
  }, {
    "name": "SmallList",
    "title": "List with small images",
    "image": "./server/assets/small-image-list.png",
    "extends": "@.List"
  }],
  "shortcuts": [{
    "name": "Restaurants",
    "title": "Restaurants",
    "description": "Allow users to browse through list of restaurants",
    "screen": "@.List",
    "adminPages": [{
      "page": "shoutem.cms.CmsPage",
      "title": "Content",
      "parameters": {
        "schema": "@.Restaurants"
      }
    }, {
      "page": "shoutem.layouts.LayoutPage",
      "title": "Layout"
    }]
  }],
  "dataSchemas": [{
    "name": "Restaurants",
    "path": "server/data-schemas/Restaurants.json"
  }]
}

We also included additional properties like title and image to screens which will be shown in the layout selector, so they look nicer. Download this file. It includes screen images that we can use as a preview of the layout in the layout selector. Extract the folder and place it in the server folder of the extension. Here you can find the list of all available properties in extension.json.

If we wanted to show a layout selector for Details screen, we would need to add a navigatesTo property to the List screen. That way, the layout settings page could calculate the screen hierarchy starting from the screen referenced in the shortcut. An example of this can be found in the extension.json file of Shoutem’s News RSS extension.

Ok, now you’re done! Let’s push the extension.

$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!

Note

If you don’t have this extension installed on any app, you can install with shoutem install. Add the screen through the builder, import the CSV file as described in the bottom of Using cloud storage chapter and run the preview. If you don’t have any restaurants set up, you won’t see anything in the preview.

By default, the layout will be the List screen. Switch to Layout and select List with small images. This is the result you should get:

Great job! Now you know how to create additional layouts for your extension.

Since we built this extension, we can add additional layouts to it directly. However, sometimes we want to add additional layouts for extensions from another developer - essentially we want to modify the extensions. You can find out how to do this in the Modifying Extensions tutorial.