Build an inventory management app on Retool Mobile

Build a mobile app for workers to browse and manage warehouse inventory.

πŸŽ‰

beta

Retool Mobile is currently in private beta. Sign up to join the waitlist β†’

Retool Mobile enables you to build native mobile apps that interact with your data and deploy them to your workforce.

Use case

A common use case for Retool Mobile is inventory management, enabling workers to look up product information in any warehouse, wherever they are. This tutorial explains how to build an inventory management mobile app for users to:

  • Browse, filter, and view details of products across all warehouses.
  • Browse and view details of warehouses, and look up available products at the location.
  • Quickly look up product information by scanning a barcode.

Inventory management mobile appInventory management mobile app

Prerequisites

This tutorial demonstrates a real-world use case of Retool Mobile and uses sample data from a PostgreSQL database. Retool cloud organizations include a basic PostgreSQL database named managed_db that you can interact with for testing purposes.

Before you begin, download the provided sample data in CSV format. Each CSV file corresponds to a database table.

TableDescriptionDownload CSV
inventory_management_itemsInventory products with brand information.Download
inventory_management_warehousesWarehouse information.Download
inventory_management_warehouseItemsWarehouse inventory information.Download

Use the Data Editor to create the required tables, using the provided names, and then upload each CSV. Once complete, you can use the same queries and resource used by this tutorial.

Retool Data EditorRetool Data Editor

Much of what this tutorial covers can apply to other resources and data types. You can also use your own SQL database that's connected to Retool.

Get started

πŸ‘

You can download a JSON export of this mobile app and import it into Retool.

Once you're ready to begin, sign into Retool and click Create > Mobile app. Set the name to Inventory Management.

View and filter products

This part of the tutorial explains how to build functionality to display a list of products and enable workers to filter by search term, brand, and warehouse location.

1. Create a mobile app

Sign into Retool and click Create > Mobile app. Set the name to Inventory Management.

2. Configure the Products screen

Retool Mobile organizes mobile components into separate screens and creates pathways for your users to follow. Users navigate between screens using the tab bar or by interactions, such as pressing a button or selecting an item from a list.

The primary screen for this mobile app is used to display a list of products. Select Screen 1(screen1) in the Screens section of the mobile panel (left) to display its settings in the right panel.

  • The screen's name (screen1) is used internally when you configure settings or write queries. Click on screen1 in the Inspector to edit this and set it to Products.
  • The screen’s title is displayed in at the top of your app and in the tab bar at the bottom, if visible. In the Inspector, set the value for Title to Products.

Set the screen name and titleSet the screen name and title

You can also set the icon to use in the tab bar at the bottom of the app. Click the screen in the Tab section of the Inspector and select an icon.

3. Retrieve product and warehouse data

Inventory data for this app comes from a PostgreSQL database. You write queries to interact with connected data sources using SQL.

This mobile app uses three queries to retrieve data about inventory, warehouses, and brands. Click + New in the bottom panel to create the first query, then select the managed_db resource and use the following SQL statement:

select id, title from inventory_management_items;

Save the query as getAllItems.

Query to get all itemsQuery to get all items

Repeat the process to create queries for getWarehouse and getBrands.

select * from inventory_management_warehouses;
select distinct brand from inventory_management_items order by 1 desc;

4. Assemble components for the Products screen

You add mobile components to each screen of a mobile app to build the interface by clicking + in the Components section of the left panel. You then configure component properties using the Inspector.

Add the following mobile components to the Products screen, then configure their respective names and settings:

  • Text Input
    • Name: Products_Searchinput
    • Placeholder: Search by name...
    • Label: Search
  • List Collection
    • Name: Products_ProductCollection
  • Fab
    • Name: Products_Fab
    • Text: Find by Barcode
  • Scanner
    • Name: Products_Scanner
    • Hidden: true

Assemble componentsAssemble components

5. Configure filter options

The app will allow workers to filter products with a search term, and select a brand and warehouse using a dedicated screen.

First, add a screen. Set the Title to Filters and its name to filters. This screen won't be accessed using the tab bar so toggle Add to tab bar off.

Add the following mobile components to the Filters screen, then configure their respective names and settings:

  • Text Input
    • Name: Filters_ProductSearchInput
    • Placeholder: Search by name...
    • Default value: {{ Products_SearchInput.value }} (automatically populates the value based on any search already entered in the Products screen)
    • Label: Search
  • Select
    • Name: Filters_BrandSelect
    • Default value: {{ getBrands.data.brand }}
    • Label: {{ self.values }}
  • Select
    • Name: Filters_WarehouseSelect
    • Default value: {{ {{ [null, ...getWarehouses.data.id] }} }} (Set an empty value as the first option, followed by a list of warehouse IDs)
    • Label: {{ [null, ...getWarehouses.data.street] }} (Display an empty label for the first option, followed by a list of warehouse street addresses that correspond to the ID values)
  • Button
    • Name: Filters_ClearButton
    • Text: Clear filters

Filter screenFilter screen

This button uses Event handlers to reset the filters to their default values. Add an event handler to the button that resets Filters_ProductSearchInput to its default value:

  • Action: Control component
  • Component: Filters_ProductSearchInput
  • Method: Reset value

Configure event handlersConfigure event handlers

Repeat this to add event handlers for Filters_BrandSelect and Filters_WarehouseSelect.

Workers will access these filters from an action button at the top of the Products screen. Select the Products screen, then add a Right action button from the Actions section of the Inspector:

  • Action: Navigation
  • Method: Navigate to screen
  • Screen: filters

Return to the Filters screen and add an action button that navigates back to the Products screen.

Configure the action buttonConfigure the action button

6. Retrieve inventory data

The app now has the necessary screens, components, and queries in place to display and filter products. The next step is to connect everything together with a query.

This query creates an initial dataset that combines product, warehouse, and brand data. It then returns a list of products that takes into account any specified filters. In can also return individual products with an id that matches a scanned barcode.

πŸ“˜

Although this query is more complex, it's more efficient as it reduces the number of queries to a data source.

-- sub-query to selects the data to use
with "itemsWithWH" as (
  select
    inventory_management_items.id,
    inventory_management_items.title,
    inventory_management_items."reviewsCount",
    inventory_management_items."thumbnailImage",
    inventory_management_items."price/value",
    inventory_management_items."price/currency",
    inventory_management_items."brand",
    array_agg(distinct wh."warehouseId") as warehouses
  from inventory_management_items
  join "inventory_management_warehouseItems" as wh
  on wh."itemId" = inventory_management_items.id
  group by 1, 2, 3, 4, 5, 6, 7
)
select * from "itemsWithWH"
where
  title ilike {{ '%' + (Products_SearchInput.value || Filters_ProductSearchInput.value) + '%' }}
  and ({{ !Filters_BrandSelect.value }}
    or brand = {{ Filters_BrandSelect.value }})
  and ({{ !Filters_WarehouseSelect.value }}
    or {{ Filters_WarehouseSelect.value || 0 }} = any(warehouses))
  and ({{ !Products_Scanner.data }} or id = {{ Products_Scanner.data ? Products_Scanner.data[0] : 0 }})
order by "reviewsCount" desc

Query to get itemsQuery to get items

This query can automatically run if it hasn't already done so whenever a worker navigates to the Products screen. Add an event handler to the screen from the Screen Events section of the Inspector and configure with the following settings:

  • Event: Visible
  • Action: Trigger query
  • Query: getItems
  • Method: Trigger
  • Only run when: {{ !getItems.data.id.length }}

The Only run when condition only allows the action to trigger if has a truthy value is set. !getItems.data.id.length evaluates as true if there is no query data already available.

Configure a screen event handlerConfigure a screen event handler

7. Display inventory data

Select the Products_ProductCollection mobile component and set the Data source to getItems. The component automatically configures list options by dynamically mapping values from the query data. Use the Mapped options settings to configure what values to use by referencing item. For instance, setting Title to {{ item.name }} displays each item name as the list option's title.

SettingsValue
Title{{ item.name }}
Body{{ item.brand }} {{ item['price/currency'] }}{{item['price/value']}}
Caption{{item.sales}}
Source{{ item.thumbnailImage }}

Configure the product listConfigure the product list

With this step complete, workers can now browse a list of products and filter results based on search term, brand, and warehouse location.

View selected product details

This part of the tutorial explains how to display details of a selected product. This involves temporarily saving the selected product, retrieving all data for the selected product, then populating components in a screen with the details.

1. Store the selected product in temporary state

Data about the selected product will be stored in temporary state. Click Create new in the Temporary State section of the left panel to create a temporary state named selectedItemState.

Add another event handler to Products_ProductCollection to save the selected item to temporary state:

  • Event: Press
  • Action: Set temporary state
  • State: selectedItemState
  • Method: Set value
  • Value: {{ self.selectedItem }}

Temporary stateTemporary state

When a worker selects a product from the list, the event handler saves the selected item's data to temporary state.

2. Retrieve details of the selected product

selectedItem only contains some information about the product. Write a query to retrieve all information about the selected product from the database.

select * from inventory_management_items where id = {{ selectedItemState.value.id }}

getItemDetailgetItemDetail

All information about the selected product is then available at getItemDetail.data. For example, getItemDetail.data.name returns the product's name.

3. Configure the Product details screen

Add a productDetail screen. This screen is used to present information about the selected product and will appear when a worker selects a product from the list.

2. Assemble components for the Product details screen

This screen makes use of numerous components to display different information. Container components are also used to group other components together and control their nested layout.

Add components and configure their respective settings based on the component tree structure below. Components nested within Containers are indented.

productDetails screen
β”‚
β”œβ”€β”€ Image
β”‚   β”‚ Name: ProductDetail_Image
|   | Source: {{ getItemDetail.data.thumbnailImage[0] }}
β”‚
β”œβ”€β”€ Heading
β”‚   β”‚ Name: ProductDetail_Heading
β”‚   β”‚ Value: {{ getItemDetail.data.title[0] }}
β”‚
β”œβ”€β”€ WebView
β”‚   β”‚ Name: ProductDetail_WebView
|   | Url: {{ getItemDetail.data['url'][0] }}
β”‚
└── Container
    β”‚ Name: ProductDetail_BasicDetailContainer
    β”‚ Direction: Row (right arrow)
    β”‚
    β”œβ”€β”€ Text
    β”‚   β”‚ Name: ProductDetail_BasicKeyText
    β”‚   β”‚ Value: Price
    β”‚            Brand
    β”‚            Rating
    β”œβ”€β”€ Text
    β”‚   β”‚ Name: ProductDetail_BasicValueText
    β”‚   β”‚ Value: {{ getItemDetail.data['price/currency'][0] }}
    β”‚            {{ getItemDetail.data['price/value'][0] }}
    β”‚            {{ getItemDetail.data.brand[0] }}
    β”‚            {{ getItemDetail.data.stars[0]}}
    β”‚
    └── Container
        β”‚ Name: ProductDetail_LongFormContainer
        β”‚ Direction: Column (down arrow)
        β”‚
        β”œβ”€β”€ Container
        β”‚   β”‚ Name: ProductDetail_ReturnPolicyContainer
        β”‚   β”‚ Direction: Column (down arrow)
        β”‚   β”‚
        β”‚   β”œβ”€β”€ Text
        β”‚   β”‚   β”‚ Name: ProductDetail_ReturnPolicyHeader
        β”‚   β”‚   β”‚ Value: Return Policy
        β”‚   β”‚   β”‚
        β”‚   └── Text
        β”‚      β”‚ Name: ProductDetail_ReturnPolicyText
        β”‚      β”‚ Value: {{ getItemDetail.data.returnPolicy[0] || "None"}}
        β”‚
        └── Container
            β”‚ Name: ProductDetail_DescriptionContainer
            β”‚ Direction: Column (down arrow)
            β”‚
            β”œβ”€β”€ Text
            β”‚   β”‚ Name: ProductDetail_DescriptionHeader
            β”‚   β”‚ Value: Description
            β”‚   β”‚
            └── Text
                β”‚ Name: ProductDetail_DescriptionText
                β”‚ Value: {{ getItemDetail.data['features/0'][0] }}
                         {{ getItemDetail.data['features/1'][0] }}
                         {{ getItemDetail.data['features/2'][0] }}
                         {{ getItemDetail.data['features/3'][0] }}

Product detail component structureProduct detail component structure

3. Display selected product details

Return to the Products screen, select Products_ProductCollection and add an event handler to navigate to the selected products:

  • Event: Press
  • Action: Navigation
  • Method: Navigate to screen
  • Screen: productDetails

Event handler to open the Product details screenEvent handler to open the Product details screen

View warehouses

The final part of the tutorial explains how to display a list of warehouses and select one to view more details. It uses the same principles previously covered.

1. Configure the warehouses screen

Add a warehouses screen with a Title of Warehouses. Similar to the Products screen, this screen will contain a list of warehouses from which to select.

2. Assemble the warehouse list interface

Add a Card Collection mobile component to the Warehouses screen named Warehouses_Collection. Set the Data source to be the getWarehouses query, then configure Mapped options to display warehouse location data in the list:

SettingValueDescription
Title{{ item.street }}The street address.
Body{{ item.district }}, {{ item.state }}The city and state.
Source{{ item.thumbnailImage }}A thumbnail image.

Configure mapped optionsConfigure mapped options

3. Configure the warehouse details screen

Add a warehouseDetails screen with a Title of Warehouse details. This screen will present information about the selected warehouse.

4. Assemble the warehouse details interface

The components in this screen use data from Warehouses_Collection.selectedItem to display information about the selected warehouse. Add the following mobile components and configure them to make use of the relevant data from the selected item.

  • Image
    • Name: WarehouseDetail_Image
    • Value: {{ Warehouses_Collection.selectedItem.thumbnailImage }}
  • Heading
    • Name: WarehouseDetail_LocationHeading
    • Text: {{ Warehouses_Collection.selectedItem.street }}, {{ Warehouses_Collection.selectedItem.state }}
  • Heading
    • Name: WarehouseDetail_AssetsHeader
    • Text: Assets
  • Text Input
    • Name: WarehouseDetail_ProductSearchInput
  • Card Collection
    • Name: WarehouseDetail_ProductCollection.

Warehouse details screenWarehouse details screen

The List Collection mobile component can display a list of products at the selected warehouse. This requires a query that joins item and warehouse data together, then returns products only at the selected warehouse.

select
  inventory_management_items.id,
  inventory_management_items.title,
  inventory_management_items."reviewsCount",
  inventory_management_items."thumbnailImage",
  inventory_management_items."savedPrice",
  inventory_management_items."brand",
  wh.quantity
from inventory_management_items
join "inventory_management_warehouseItems" as wh
on wh."itemId" = inventory_management_items.id
where
  wh."warehouseId" = {{ Warehouses_Collection.selectedItem.id }}
  and title ilike {{ '%' + WarehouseDetail_ProductSearchInput.value + '%' }}
order by "quantity" desc

Warehouse items queryWarehouse items query

Wrap up

You have now built a inventory management mobile app that enables workers to browse, filter, and view details of products and warehouses.

By applying the lessons learned here and following the same patterns, you could extend the mobile app's functionality with features like editing or adding products and warehouses.


Did this page help you?