Flash Client Sdk
  • Development
    • Flash Client
  • Understanding Configurability
  • Getting Started
    • Components
    • Inflaters
Powered by GitBook
On this page
  • Overview
  • Available Layouts in Mobile App Development
  • Dynamic Layout Creation with Inflaters
  • Usage of Inflaters in a Component
  • Input Props and Data Binding Transformation
  • components
  • Data Prop
  • Style Prop
  • Overrides Prop
  • Nested Scrollable Items
  • Inflater Implementations
Export as PDF
  1. Getting Started

Inflaters

Overview

In mobile development, creating responsive and dynamic user interfaces is a fundamental challenge. Developers typically rely on various layout patterns to organize and present content effectively. However, as applications grow in complexity, there's a growing need for UIs that can adapt in real time without requiring app updates.

Key challenges include:

  • Dynamically reordering components

  • Adding new components on the fly

  • Changing layout or placement without releasing a new version

The Flash framework addresses these needs by leveraging JSON-based layout configurations sent from the server.

To support these dynamic capabilities and maintain a robust UI system, we introduce Inflaters. Inflaters are responsible for understanding and rendering the JSON-based layout data received from the server. They are core components in the Flash system that enable powerful layout-driven configurations, such as reordering components or adding new ones dynamically.

This document explores Inflaters in more detail.


Available Layouts in Mobile App Development

Common layout types in mobile apps include:

  • Tab layouts

  • Stack layouts

  • List layouts

  • Scroll layouts

These help structure content and enhance user experience. However, adapting these layouts dynamically based on server data is where the real challenge lies.


Dynamic Layout Creation with Inflaters

Inflaters are responsible for interpreting the layout structure defined in server responses and rendering components accordingly. This allows UIs to be updated or changed remotely—without the need for app redeployment.

Flash provides several generic inflaters:

  • TabLayoutInflater: Renders child components as tabs

  • StackLayoutInflater: Renders components in a vertical stack

  • ListInflater: Renders components in a scrollable list (vertical, horizontal, or nested)

  • ScrollInflater: Renders components inside a scrollable container

These inflaters enable the Flash Client SDK to support flexible, layout-driven UIs across a variety of use cases.


Usage of Inflaters in a Component

As explained in the Flash Components section, every Flash-compatible component manages its own configuration. Inflaters play a key role when it comes to enabling dynamic layout capabilities, such as reordering elements or adding new ones.

Components that require any of these layout capabilities should use inflaters to interpret and apply the layout configuration provided by the server. The specific inflater used depends on the desired layout behavior.

To demonstrate this, we walk through an example of enhancing SportsListComponent using ListInflater, enabling it to support reordering and component insertion.

Example: Regular FlatList Component

import React from 'react';
import { FlatList, View, Text, Pressable } from 'react-native';
import { ConfigurableProps } from '../../@types'; // Update the import path as needed

const SportsListComponent = (props: ConfigurableProps) => {
  const { components, style, data, onItemClick } = props;

  const renderItem = ({ item, index }) => {
    const child = components.find(child => child.name === item.componentName);
    if (!child) return null;

    const Component = getComponent(child.name);
    return (
      <Pressable onPress={() => onItemClick && onItemClick(item, index, child.name)}>
        <Component {...child.props} data={item.data} style={child.style} />
      </Pressable>
    );
  };

  return (
    <View style={style}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={(item, index) => `${item.componentName}-${index}`}
      />
    </View>
  );
};

export default SportsListComponent;

// Helper function to get the component by name
const getComponent = (name) => {
  // Define your component mapping here
  const components = {
    ComponentA: ComponentA,
    ComponentB: ComponentB,
    // Add other components as needed
  };

  return components[name] || DefaultComponent;
};

// Define your default component if no match is found
const DefaultComponent = (props) => (
  <Text>Component not found</Text>
);

// Example components
const ComponentA = (props) => (
  <Text style={props.style}>{props.data}</Text>
);

const ComponentB = (props) => (
  <Text style={props.style}>{props.data}</Text>
);

Example: SportsListComponent Flash-Compatible with ListInflater

import React from 'react';
import { ListInflater } from './ListInflater'; // Ensure ListInflater is imported correctly
import { ConfigurableProps } from '../../@types'; // Update the import path as needed

const SportsListComponent = (props: ConfigurableProps) => {
  return (
    <FlatListInflater
      components={props.components}
      style={props.style}
      data={props.data}
    />
  );
};

export default SportsListComponent;

Example Implementation of ListInflater

To illustrate how inflaters work, let's look at an example of a ListInflater.

import React, {useCallback} from 'react'

import {FlatList, ViewStyle} from 'react-native'

import {Component, PropData, FlatListInflaterProps} from '../../types/types'
import {getComponent} from '../../utils/render-utils'

/**
 * `FlatListInflater` component for rendering a dynamic list of components using `FlatList`.
 *
 * @template T - The type of data that each component will use, extending `PropData`.
 */
export const FlatListInflater = React.memo(
  <T extends PropData>({
    components,
    style,
    data,
    flatListProps, // Optional FlatList props without 'data' and 'renderItem'
  }: FlatListInflaterProps<T>): JSX.Element | null => {
    /**
     * Creates a React component based on the provided configuration and data.
     * The component configuration and optional data are passed to the appropriate UI component.
     *
     * @param {Component} component - The configuration object for the component.
     * @param {T | null} dataItem - The data item associated with the component, or `null` if no data is available.
     * @param {number | string} index - The unique index or key for the component, used for identifying the component in the list.
     *
     * @returns {JSX.Element} - The dynamically rendered component.
     */
    const createComponent = useCallback(
      (component: Component, dataItem: T | null, index: number | string) => {
        const RenderComponent = getComponent(component.name)

        return (
          <RenderComponent
            components={component.components} // Pass any child components
            style={component.styles} // Apply the component's styles
            overrides={component.overrides || {}} // Apply overrides if provided
            // @ts-ignore
            data={dataItem || {}} // Pass the component-specific data or an empty object if no data
            key={`${component.name}-${index || 'default'}-${
              component.id || Math.random()
            }`} // Unique key for the component
          />
        )
      },
      [], // Empty dependency array ensures the function is memoized and doesn't change between renders
    )

    /**
     * Memoized function to render each item in the `FlatList`.
     * Retrieves the associated data for the component and calls `createComponent`.
     *
     * @param {Object} item - The component configuration for the current list item.
     *
     * @returns {JSX.Element} - The dynamically rendered list item.
     */
    const renderItem = useCallback(
      ({item}: {item: Component}) => {
        // Retrieve the data for the component based on `dataId` or `name`
        const componentData = data
          ? item.dataId && item.dataId.trim().length > 0
            ? data[item.dataId]
            : data[item.name]
          : null

        // Create the component with the associated data
        return createComponent(item, componentData as T, 'single')
      },
      [data, createComponent], // Dependencies include `data` and `createComponent`
    )

    /**
     * Key extractor for `FlatList`, providing a unique key for each component.
     * Ensures that each component has a stable, unique key for efficient rendering.
     *
     * @param {Component} item - The component configuration.
     * @param {number} index - The index of the component in the list.
     *
     * @returns {string} - The unique key for the component.
     */
    const keyExtractor = useCallback(
      (item: Component, index: number) =>
        `${item.name}-${item.id || Math.random()}-${index}`,
      [], // Memoized function to ensure it doesn't change between renders
    )

    // If there are no components to render, return null to avoid rendering an empty list
    if (!components || components.length === 0) {
      return null
    }

    /**
     * Render the `FlatList` with the provided components and optional props.
     *
     * - `data`: The array of components to render.
     * - `renderItem`: The function used to render each item in the list.
     * - `keyExtractor`: Function that extracts unique keys for each item.
     * - `contentContainerStyle`: Optional style applied to the `FlatList` content container.
     * - `flatListProps`: Additional props to customize `FlatList` behavior, such as scrolling, layout, etc.
     */
    return (
      <FlatList
        data={components}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        contentContainerStyle={style as ViewStyle}
        testID="list-inflater-flatlist" // <-- Add this line for testing purposes
        {...flatListProps}
      />
    )
  },
)

Type Definitions

/**
 * Represents the base configurable properties for an inflater component.
 *
 * Inflaters are used to dynamically manage and render components in different configurations.
 *
 * - `child`: An optional array of child components. This allows inflaters to manage nested components.
 * - `style`: An optional object defining the styling for the inflater component.
 * - `overrides`: An optional object for overriding the styles and props of child components within the inflater.
 * - `data`: An optional generic type (`T`) representing the data required by the inflater component.
 *
 * @template T - The type of the data prop. Defaults to `never` if not specified.
 */
export type BaseInflaterProps<T extends PropData | never = never> = {
  readonly components?: Array<Component>
  readonly style?: ViewStyle
  readonly overrides?: Overrides
  readonly data?: T
}

/**
 * Extends the base inflater properties with additional properties specific to `FlatListInflater`.
 *
 * - `flatListProps`: Optional props that will be passed to the underlying `FlatList` component.
 *   These props allow for customizing the behavior and appearance of the `FlatList`, such as enabling horizontal scrolling,
 *   setting the number of columns, or handling scroll events.
 *   Note that the `data` and `renderItem` props are managed internally by `FlatListInflater` and cannot be overridden.
 *
 * @template T - The type of the data prop used in the list, extending `PropData`.
 */
export type FlatListInflaterProps<T extends PropData> = BaseInflaterProps<T> & {
  readonly flatListProps?: Omit<FlatListProps<Component>, 'data' | 'renderItem'>
}

/**
 * Extends the base inflater properties with additional properties specific to TabInflater.
 *
 * This type does not add any new properties but maintains the structure of the base inflater.
 *
 * @template T - The type of the data prop used in the tabs.
 */
export type TabInflaterProps<T extends PropData> = BaseInflaterProps<T>

/**
 * Extends the base inflater properties with additional properties specific to StackInflater.
 *
 * This type does not add any new properties but maintains the structure of the base inflater.
 *
 * @template T - The type of the data prop used in the stack.
 */
export type StackInflaterProps<T extends PropData> = BaseInflaterProps<T>

/**
 * Extends the base inflater properties with additional properties specific to ScrollInflater.
 *
 * - `scrollViewProps`: An optional object containing props to be spread into the root `ScrollView`.
 *
 * @template T - The type of the data prop used in the scroll inflater.
 */
export type ScrollInflaterProps<T extends PropData> = BaseInflaterProps<T> & {
  readonly scrollViewProps?: ScrollViewProps
}

Input Props and Data Binding Transformation

components

Inflaters receive a list of components from the server as part of the layout configuration. These components define the structure and content that should be rendered dynamically.

Data Prop

Data from existing APIs is transformed to match the format expected by inflaters. This transformation ensures that inflaters can properly associate data with each component and render them accordingly.

Style Prop

The style prop applies styling to the parent container of the inflater. This enables layout-level styling while maintaining flexibility for child components.

Overrides Prop

The overrides prop allows components to override specific layout or style properties of nested children. This adds a layer of customization on top of the default configuration.


Nested Scrollable Items

Inflaters also support nested scrollable elements. If a child item needs to scroll (horizontally or vertically), its layout should include the appropriate configuration from the server. This enables complex nested structures—like scrollable carousels inside a vertical list.


Inflater Implementations

Below is a summary of the current inflater types supported by the Flash framework:

  • ListInflater: Uses FlatList to dynamically render lists of child components.

  • ScrollInflater: Uses ScrollView to enable vertical or horizontal scrolling of child components.

  • TabInflater: Organizes child components into swipeable or clickable tabs.

  • StackInflater: Renders components in a vertically stacked format.

These inflaters provide the flexibility required to design dynamic, data-driven layouts with minimal hardcoding.

PreviousComponents

Last updated 1 month ago