Developers can customize the code generation process by providing a mapping between a UI Package and an existing code component instead of the generated code. This is beneficial when the existing implementation has features that cannot be achieved by the generated code such as animation or complex behavior (such as a drop down menu).
Developers specify how to map components using a mapping file. A mapping file tells the code generator, at minimum, how to reach the target composable function so that the right client code can be created.
Here is an example:
In Figma, a designer creates a Card component that contains an instance of a Play Bar component, packages both components, and sends them to a developer.
When the developer imports the UI Packages from Figma, two directories are
created in ui-packages
: card
and play_bar
. When they build the project,
two composable functions are created: Card
and PlayBar
. Typically, because
Card contains a Play Bar instance in Figma, in code the Card
composable function contains a call to the PlayBar
composable.
However, the designer and developer want Card
to instead use an existing
composable, MyExistingPlaybar
, which has functionality that is hard to
describe in Figma. So the developer adds a mapping file called play_bar.json
that maps the play_bar
UI Package to MyExistingPlaybar
:
{
"target": "MyExistingPlaybar",
"package": "com.example.myApp"
}
Now, when the developer builds the project, Card
calls MyExistingPlaybar
instead of PlayBar
. Note that MyExistingPlaybar
must have the same
parameters as PlayBar
(although there can be a few differences, as described
in Additional Directives below).
Mapping file
In your Android Studio projects, mapping files are added under
ui-package-resources/mappings
next to the ui-packages
folder. Relay looks
for mapping files during build.
Generate a mapping file
Relay can generate a mapping file for any imported UI Package. Follow these steps:
Right-click on the package folder or any file inside the target
ui-package
folder. Select Generate mapping file.Configure the following options in the dialog:
File location: Sets the location for the generated mapping file.
Target composable: Sets the custom composable that is used instead in place of the generated composable. You have the option of using an existing composable or creating a new one from the dialog. Creating a new composable creates a composable with the same parameters as defined in the UI package.
- Generated file: Sets the
generateImplementation
andgeneratePreview
options in the mapping file. See Mapping file contents below for more details.
Click Generate mapping file. A new mapping file is created inside of
ui-package-resources/mapping
folder with the specified configurations.
You can also open the Generate mapping file dialog from the Relay package module UI using these steps:
Click any file for a UI package inside the target
ui-package
folder.If the Relay tool window does not open automatically, click the Relay icon to open the window.
Click the Generate mapping file button under Package Options.
Mapping file name
The name of a given mapping file must match the name of the UI Package folder
for the component it replaces. So play_bar.json
maps the UI Package in the
ui-packages/mappings
folder to an existing code component.
Mapping file contents
The mapping file contains the following properties:
target: (Required) The name of your custom composable function. By default, this is the name of the function created by generated code.
"target" : "CustomComposableName"
package: (Required) Name of the package that your custom composable sits in. By default, this is the package of the function created by generated code.
"package" : "com.example.podcastapp.ui.components"
generateImplementation: (Optional) true or false. If true, an implementation of this UI Package is still created in the generated code file. If false, the implementation isn't created. By default, this is true.
"generateImplementation" : true
generatePreviews: (Optional) true or false. If true, a preview of the mapped custom component is created in the generated code file. If false, no preview is created. By default, this is true.
"generatePreviews" : true
Mapped variants
If a Figma component has variants, then the generated composable contains enum parameters that encode the variant (as described in the Handling Design Variants tutorial). If you want to map a Figma component with variants to existing code, it must be mapped to a composable that takes the same parameters as the generated composable. For example, for a Figma component called Chip with a variant whose property is ChipType, Chip's generated composable signature looks like this:
@Composable
fun Chip(
modifier: Modifier = Modifier,
chipType: ChipType = ChipType.Red,
chipText: String
) { ... }
If you want to have the Chip Figma component map to an existing MyChip
composable, then the signature for MyChip
must have the same signature as the
generated composable (assuming no additional directives are specified).
Conceptually, this suggests that the existing code component is capable of the
same design variants as the Figma component.
Additional directives
For example, if the composable function you want to target has the following signature:
@Composable
fun MyChip(
modifier: Modifier = Modifier,
chipType: ChipType = ChipType.Red,
description: String // instead of chipText
) { ... }
You can add a fieldMappings
block to the mapping file that affects how
parameters are mapped. In this case, it contains a mapping from the chipText
parameter in the Chip
to the description
parameter in MyChip
.
{
"target": "MyChip",
"package": "com.example.myApp",
"fieldMappings": [
{
"type": "parameter",
"source": "chipText",
"target": "description"
}
]
}
The types for the fieldMappings
block include:
parameter
: Maps a UI Package field to a code parameter.source
: Name of parameter as specified in the UI Package.target
: Name of parameter as specified in the target code component.
lambda
: Maps a UI Package field to a content lambda.source
: Name of parameter as specified in the UI Package.target
: Name of parameter as specified in the target code component.
modifier
: Maps a UI Package field to a modifier method.source
: Name of parameter as specified in the UI Package.method
: Method on the Modifier object that should be invoked in generated code.parameter
: Name of parameter within the specified Modifier method.library
: The qualified package name to import to access the Modifier method.scope
: One of two values to indicate the scope of the Modifier:any
: The modifier can be used in any receiver scope.relay
: The modifier must be used in the receiver scope of Relay'sRelayContainer
object.