Paginated ListView

A hero image illustrating a Paginated ListView widget with infinite scroll loading.
An advanced list widget that efficiently loads large datasets in chunks through pagination.

The Paginated ListView is a specialized list widget designed for handling large datasets by loading data in pages or chunks. Instead of loading all items at once, it fetches data incrementally as the user scrolls, providing a smooth infinite scroll experience.

This widget is essential for building feeds, search results, product listings, and any interface where the total dataset could be very large or infinite.

Paginated ListView automatically handles the complexity of pagination, including loading states, error handling, and triggering new page loads when the user scrolls near the end of the list.

Use Cases

Paginated ListView is ideal for:

  • Social Media Feeds: Load posts, comments, or notifications incrementally as users scroll.

  • E-commerce Product Listings: Display thousands of products without loading everything upfront.

  • Search Results: Show search results page by page, improving initial load time.

  • News Articles: Load articles in batches for better performance.

  • User Directories: Display large lists of users, contacts, or team members.

  • Transaction History: Show financial transactions or order history with pagination.

  • Image Galleries: Load images progressively to reduce initial bandwidth usage.


How to Set Up a Paginated ListView

Setting up a Paginated ListView involves three main steps:

Step 1: Create a Paginated API Call

  1. Navigate to the API section in your project.

  2. Create a new API call that supports pagination.

  3. Configure the API endpoint with pagination parameters (see Pagination Variables section below).

  4. Test the API to ensure it returns paginated data correctly.

Step 2: Add the Paginated ListView Widget

  1. Drag the Paginated ListView widget onto your canvas.

  2. In the properties panel, select your paginated API in the API Data Source field.

  3. Configure the pagination type and variables (explained below).

Step 3: Design the Item Template

  1. Add a child widget to serve as the template for each list item.

  2. Use ${currentItem} to access data for each item.

  3. Design your item layout (e.g., a container with text, images, buttons).

Step 4: Configure Loading Indicators

  1. Add a firstPageLoadingIndicator widget (e.g., a circular progress indicator).

  2. Add a newPageLoadingIndicator widget (e.g., a smaller loading spinner).

  3. These will be shown while data is being fetched.

A diagram showing the setup flow for Paginated ListView with API connection.
Connect your paginated API, design the item template, and configure loading states.

Pagination Variables

Pagination variables control how the Paginated ListView communicates with your API to fetch successive pages of data. There are three types of pagination supported:

1. Page-Based Pagination

In page-based pagination, you request data using a page number.

How it works:

  • The API accepts a page parameter (e.g., page=1, page=2, page=3).

  • Each request returns a specific page of results.

  • The response typically includes the current page number and total pages.

API Example:

GET /api/products?page=1&limit=20
GET /api/products?page=2&limit=20

Configuration:

Property
Description

First Page Key

Expression that returns the starting page number. Typically 1.

Next Page Key

Expression to extract the next page number from the API response. Example: ${response.body.nextPage} or ${sum(response.body.currentPage, 1)}.

Transform Items

Expression to extract the array of items from the response. Example: ${response.body.data} or ${response.body.products}.

Example Response Structure:

{
  "currentPage": 1,
  "totalPages": 10,
  "nextPage": 2,
  "data": [
    {"id": 1, "name": "Product 1"},
    {"id": 2, "name": "Product 2"}
  ]
}

When to use: When your backend API uses page numbers and returns structured pagination metadata.


2. Offset-Based Pagination

In offset-based pagination, you request data using an offset value (how many items to skip) and a limit (how many items to return).

How it works:

  • The API accepts offset and limit parameters.

  • offset tells the API how many records to skip.

  • limit specifies how many records to return in this batch.

  • You calculate the next offset by adding the limit to the current offset.

API Example:

GET /api/users?offset=0&limit=20   // First page: items 1-20
GET /api/users?offset=20&limit=20  // Second page: items 21-40
GET /api/users?offset=40&limit=20  // Third page: items 41-60

Configuration:

Property
Description

First Page Key

The starting offset value. Typically 0.

Next Page Key

Expression to calculate the next offset. Example: ${sum(response.body.offset, response.body.limit)} or ${sum(currentOffset, 20)}.

Transform Items

Expression to extract the items array. Example: ${response.body.results}.

Example Response Structure:

{
  "offset": 0,
  "limit": 20,
  "total": 150,
  "results": [
    {"id": 1, "username": "user1"},
    {"id": 2, "username": "user2"}
  ]
}

When to use: Common with REST APIs and databases that support SQL-style LIMIT/OFFSET queries. Ideal for predictable datasets where you can calculate exact positions.


3. Cursor-Based Pagination

In cursor-based pagination, you request data using a cursor token that points to the next set of results.

How it works:

  • The API returns a cursor (or token) pointing to the next page.

  • You pass this cursor in the next request to get the following batch.

  • Cursors are typically opaque strings or tokens.

  • More efficient than offset-based for large, frequently changing datasets.

API Example:

GET /api/posts?cursor=initial
GET /api/posts?cursor=eyJpZCI6MTAwfQ==
GET /api/posts?cursor=eyJpZCI6MjAwfQ==

Configuration:

Property
Description

First Page Key

The initial cursor value. Can be null, "", or "initial" depending on your API.

Next Page Key

Expression to extract the next cursor from the response. Example: ${response.body.nextCursor} or ${response.body.pagination.next}.

Transform Items

Expression to extract the items array. Example: ${response.body.items}.

Example Response Structure:

{
  "nextCursor": "eyJpZCI6MTAwfQ==",
  "hasMore": true,
  "items": [
    {"id": 50, "title": "Post Title 1"},
    {"id": 51, "title": "Post Title 2"}
  ]
}

When to use:

  • Large datasets where items are frequently added/deleted (social feeds, real-time data).

  • When you need consistent results even if data changes between requests.

  • APIs that use cursor/token-based pagination (Facebook, Twitter, Stripe, etc.).


Properties

Data Source Configuration

Property
Description

API Data Source

(Required) The paginated API call to use for fetching data. This API should support pagination parameters.

Data Source

Optional static data or expression for testing. When using an API, this is typically not needed.

Transform Items

(Required) An expression that extracts the array of items from the API response. Example: ${response.body.data}, ${response.body.results}, or ${response.body.items}.

Pagination Control

Property
Description

First Page Key

(Required) The initial value for the pagination parameter. For page-based: 1. For offset-based: 0. For cursor-based: null or initial token.

Next Page Key

(Required) Expression to extract or calculate the key for the next page from the API response. This determines what value gets sent in the next API request.

Scrolling Behavior

Property
Description

Allow Scroll

If true, the list will be scrollable. Default is true.

Scroll Direction

The direction in which the list scrolls. Can be Vertical (default) or Horizontal.

Reverse

If true, reverses the order in which items are displayed. For vertical lists, the first item appears at the bottom instead of the top. For horizontal lists, the first item appears on the right instead of the left. The scroll direction remains the same.

Initial Scroll Position

The initial scroll position when the list loads. Can be Start (default) or End.

Shrink Wrap

If true, the list sizes itself to fit its children. Use with caution as it can impact performance.

Loading Indicators

Property
Description

Show First Page Progress Indicator

If true, displays the firstPageLoadingIndicator widget while the initial page is loading.

Show New Page Progress Indicator

If true, displays the newPageLoadingIndicator widget while loading subsequent pages.


Children Slots

The Paginated ListView widget has three child slots that you must configure:

1. children (Item Template)

Purpose: The widget template used to render each item in the list.

Configuration:

  • Add a single child widget (e.g., Container, Card, or custom component).

  • This widget will be repeated for each item in the data.

  • Use ${currentItem} to access the data for each item.

  • Use ${currentIndex} to access the item's position in the list.

Example: A Container with:

  • Text widget displaying ${currentItem.title}

  • Image widget showing ${currentItem.imageUrl}

  • Button with onClick action

2. firstPageLoadingIndicator

Purpose: Widget displayed while the first page of data is being loaded (initial load).

Configuration:

  • Typically a centered circular progress indicator.

  • Can include loading text, skeleton screens, or placeholder content.

  • Shown only on the very first load when no data is available yet.

Example Widgets:

  • Circular Progress Indicator

  • Linear Progress Bar

  • Skeleton loader (multiple placeholder containers)

  • Custom loading animation

3. newPageLoadingIndicator

Purpose: Widget displayed at the bottom of the list while loading additional pages.

Configuration:

  • Usually a smaller progress indicator.

  • Appears at the end of the list when the user scrolls near the bottom.

  • Should be subtle so it doesn't disrupt the user's scrolling experience.

Example Widgets:

  • Small Circular Progress Indicator

  • Horizontal loading bar

  • Text: "Loading more..."

  • Custom pagination loader

A diagram showing firstPageLoadingIndicator for initial load and newPageLoadingIndicator for subsequent loads.
Use appropriate loading indicators for different loading states.

Complete Setup Example

Here's a comprehensive example of setting up a Paginated ListView for a product catalog using page-based pagination:

1. API Configuration

Endpoint: https://api.example.com/products Method: GET Query Parameters:

  • page: Variable - ${pageNumber} (integer)

  • limit: Fixed value - 20

2. Paginated ListView Properties

  • API Data Source: getProducts (your API call)

  • First Page Key: 1

  • Next Page Key: ${response.body.pagination.nextPage}

  • Transform Items: ${response.body.data}

  • Show First Page Progress Indicator: true

  • Show New Page Progress Indicator: true

3. Children Configuration

Item Template (children):

  • Container with padding

    • Row

      • Image: ${currentItem.imageUrl}

      • Column

        • Text: ${currentItem.name}

        • Text: $${currentItem.price}

First Page Indicator (firstPageLoadingIndicator):

  • Column (centered)

    • Circular Progress Indicator

    • Text: "Loading products..."

New Page Indicator (newPageLoadingIndicator):

  • Container (centered, small padding)

    • Circular Progress Indicator (smaller size)


Accessing Item Data

Inside the children template, you have access to special variables:

Variable
Description
Example

currentItem

The data object for the current list item.

${currentItem.name}, ${currentItem.id}

currentIndex

The zero-based index of the current item in the list.

${currentIndex} returns 0, 1, 2...

For object arrays:

${currentItem.title}
${currentItem.user.name}
${currentItem.price}

For simple arrays:

${currentItem}  // The value itself

Handling End of Data

The Paginated ListView needs to know when there are no more pages to load. This is determined by the Next Page Key expression:

When to stop loading:

  • When Next Page Key returns null

  • When Next Page Key returns undefined

  • When Next Page Key is empty or invalid

Examples:

Page-based with hasMore flag:

${if(response.body.hasMore, response.body.nextPage, null)}

Offset-based with total count:

${if(lt(sum(response.body.offset, response.body.limit), response.body.total), sum(response.body.offset, 20), null)}

Cursor-based:

${response.body.nextCursor}  // API returns null when no more data

Make sure your API properly signals when there are no more results by returning null for the next page/cursor, or by providing a flag like hasMore: false.


Error Handling

When the API call fails (network error, timeout, or API error):

  • The loading indicator disappears.

  • The user can pull to refresh to retry.

  • Already loaded items remain visible.

  • Consider adding error state widgets for better UX.

Best Practice: Add error handling in your API call configuration and show appropriate error messages to users.


Best Practices

  • Choose the right page size: Balance between fewer requests (larger pages) and faster initial load (smaller pages). 20-50 items per page is common.

  • Show loading states clearly: Users should always know when data is being fetched.

  • Handle errors gracefully: Network issues are common on mobile. Provide retry mechanisms.

  • Test with slow networks: Ensure loading indicators work properly even with slow connections.

  • Optimize item templates: Keep item widgets lightweight for smooth scrolling performance.

  • Use cursor-based pagination for real-time feeds where data changes frequently.

  • Implement pull-to-refresh: Allow users to manually refresh the list (use Refresh Indicator widget).

  • Consider caching: For offline support, cache paginated data locally.


Guides

For comprehensive examples and real-world implementations, see:

Paginated ListView Infinite Scroll

This guide includes:

  • Social Media Feed - Build infinite scroll feeds with cursor-based pagination

  • E-Commerce Product Catalog - Create product listings with offset-based pagination

  • Search Results - Implement search with page-based pagination

  • Performance optimization tips and best practices


Default Properties

The Paginated ListView widget supports the Layout and Appearance sections from Default Properties. This includes:

  • Padding and Margin

  • Background Color

  • Border and Border Radius

  • Visibility

The widget does not support the Interactions section (onClick) directly. To make items tappable, add interaction handlers to the child template widgets.


Summary

Paginated ListView is the essential widget for efficiently displaying large datasets through pagination. By loading data incrementally, it ensures fast initial load times and smooth infinite scroll experiences.

Feature
Paginated ListView

Data Loading

Incremental/on-demand

Pagination Types

Page-based, Offset-based, Cursor-based

Performance

Lazy rendering + paginated data fetching

Best For

Large datasets, infinite scroll, API-driven content

Loading States

First page indicator + new page indicator

Required Configuration

API Data Source, Transform Items, First/Next Page Keys

Key Advantage

Reduces initial load time and memory usage for large lists

Last updated