How to handle dynamic layout in Protopie

Simon Schmidt
idealo Tech Blog
Published in
8 min readApr 4, 2024

--

two cell stacks, the height difference between the center cell highlighted

Did you ever miss the auto layout in Protopie? You are not alone. If you want to create a large stack of tiles with dynamic content and different heights, you are in for a bumpy ride until their auto layout feature is done (they changed the feature request to ‘in work’ recently, yay). Here is my approach.

Topics that will be explained in this post:

  • Storing local JSON and how to read it
  • Applying ‘manual’ auto layout
  • Building and stacking the list

Storing local JSON and how to read it

There are multiple blog articles that explain how to get live data into Protopie. In this case, for reproducibility purposes and because live data/Connect does not work with the protopie app, we will fall back to a JSON we download beforehand and store inside the prototype. It still considerably speeds up work if you want to test a vast amount of requests in a new layout, as replacing the JSON is a simple copy and paste.

Here is the JSON I will use for demonstration purposes: https://gist.github.com/simonschmidt-idealo/dd3db92d9f0b488c995e9c2278412dd5

And here is the finished pie for you to compare against: https://cloud.protopie.io/p/ce216a2c7d7c998b08a3c135

{
"data": {
"search": {
"items": [
{
"category": "Lautsprecher",
"name": "Sonos Era 100 Black",
"mainDetails": [
{
"value": "2.0 Stereo"
},
{
"value": "Bluetooth 5.0"
},
{
"value": "mitFreisprechfunktion"
},
{
"value": "Line-In (3,5mm Klinke)"
}
],
"price": 21900,
"image": "https://ip.keycdn.com/example.jpg?width=300&quality=70"
},
// excerpt, you need the full json linked above

Layout first

We start by building our tile according to the data provided. In this case, we have a category, name, userReviews, main details and price. Checking the complete JSON we see the main details having 1 to 4 entries which we need to accomodate for. Those will serve as our example of dynamic content.

Let’s start with creating a tile component and a simple layout. Here we add all the necessary text fields and all possible 4 main detail entries.

a simple cell layout in a component
Simple Cell Layout

Naming in Protopie

I recommend naming all layers consistently to avoid confusion. The same is true for variables. Keep in mind both are case-sensitive when used in functions. This is a source for mistakes, you can make your life easier by sticking to lowercase.

Make sure to set Text Resize to Auto Width or Auto Height, depending on how you want your text field to scale depending on data length.

Text Resize Protopie select
You probably want your content to adjust boundaries dynamically

When your layout is complete, add the data.

Creating data variables

Storing data inside data variables made error searches more efficient to me in contrast to parsing the data right away into the text field. Create a data variable for each data snippet we need and make sure to set them all to type text, otherwise, they won’t accept the data. Create another variable for the JSON, in my example, called currentJSON, and paste the complete JSON inside the tiny text field.

Yes, all that JSON fits in there

Make sure you add all those variables inside the component and not in the scene, otherwise, the component cannot access them.

Parsing the JSON

If you are unfamiliar with code, this might seem like a daunting task, but it’s really simple. Protopie has a function to do that for you called parseJson(). To read a value from the JSON, you go through each curly bracket, separated by a dot. If you encounter a rectangular bracket (array), its time to decide for one of the entries inside it. Arrays start counting at zero instead of one. To select the first entry of the main details, you need to put in a function:

// reads first entry of items and first entry of mainDetails
parseJson(currentJSON, "data.search.items.0.mainDetails.0.value")

Reminder parsing JSON is case sensitive – mainDetails has a capital letter inside, which might happen in any API.

interface element with parsing formula
Reading our JSON main detail into our variable

Clustering events

Let me quickly swerve into the topic of how to structure Protopie. I found it to be good practice to cluster similar functionality into Receive events, creating a single source of truth for everything you do. This makes testing easier, avoids repetition in case you need to access the same function again, and reduces mistakes. And best of all: you can collapse everything when you are done. In short: as soon as you want to write the same event a second time — combine it with a new Receive event.

Let's cluster all our data readings into a Receive event called setData. Don’t forget to name your event layers as well. Now parse the JSON for each text field into a variable.

uncollapsed and collapsed receive event
Being able to collapse your events by type is a winner

Reading data

Now read all the data stored in variables into the text fields. You only need to add a Text Event -> Function -> data_variablename each time. As the price is stored as a 5-digit number we need to apply additional styling:

data_price/100 + " €"

Read only the first item for now (index 0), we will add the others later.

image filled by JSON
Yes, images can be filled from JSON too!

Applying ‘manual’ Auto Layout

The fun part: Each cell needs to grow in height depending on the content. As there is no native function for stacking/auto layout in Protopie yet, we need to calculate this ‘by hand’. Fear not, it's pretty easy, albeit a bit repetitive. At the end of our setData cluster, we add another send event, let's call it handleMainDetails and a corresponding receive event.

No data — collapse

Group each main detail text and select clip sublayers on the bottom right. Inside your handleMainDetails add a condition for each main detail to check if it's empty and if it is — scale it to zero. This not only collapses the view but also hides any potential leftover content.

protopie null check: empty value field in condition
Protopie null check — just leave the value empty

Use duplicate to save you some work for the rest of the main details, but don’t forget to adjust the references.

collapse
Collapsing the container view to zero height. Make sure to clip sublayers on the group.

Stacking elements

Whenever the main details are missing, we need to move all elements below accordingly. To do this, we add another Send/Receive event called autoLayout. Inside, align each item below the title with the item above it. E.g., we read the values for the title Y position and add the title height to get the bottom edge of the title.

As we want to accommodate for the gap above grp_maindetail1 as well, we need to add a variable holding the distance between main details and price, in this case, it's 15. Let’s call it gap_abovemaindetails. This variable is added to the title bottom edge in the function to position grp_maindetail1. Make sure you select the container group instead of the text field for this.

`grp_maindetail1`.y+`grp_maindetail1`.height+gap_abovemaindetails

Do this for all main details accordingly, same for price.

Calculating main details position
Main details moved below title (on Y value, hidden behind popover)

Calculating cell height

Now add another Send/Receive to determine the resulting cell height. I assume you have prepared all the gaps as variables now. In short, the function of this could be

gap_topEdge+
`category`.height+
`title`.height+
gap_abovemaindetails+
`grp_maindetail1`.height+
gap_maindetails_between+
`grp_maindetail2`.height+
gap_maindetails_between+
`grp_maindetail3`.height+
gap_aboveprice+
`price`.height+
gap_bottomEdge

This nicely snuggles the cell around its content.

height calculation function
The final piece inside the component: height calculation of tile crammed into the function field.

Building and stacking the list

Up until now, we have only built one cell component. How does the component know which cell is inside the layout? We need a new variable in the component for this. Let’s call it tile_index. It’s important to check the Make Overridable here, as this is how we will feed the tile position in the list from a scene into a component.

Let’s revisit all data for variable assignments in the component and add our new tile_index to the reading like this:

parseJson(currentJSON, "data.search.items."+tile_index+".price")

Do this for all variables.

Adding the tile index to all variables

Scene preparation

We are almost done! Switch to the scene now and stack 6 of your tile components into a scroll view (by grouping and activating vertical scroll).

Then click on each component and assign the correct tile_index override number to each. As arrays count from zero, the top most can stay at zero. The second gets a 1, and so forth. This should look pretty good already, all cells should fill their respective content and adjust their heights. However, you might notice the gaps between the cells are still wonky.

Assigning the tile index to each component override in the scene manually (here is hoping for some kind of auto index in the Protopie future)

Stacking the cells

Define the target gap between the tiles and put it in a variable named gap_tile. Then create a start event, add a Send/Receive just for the sake of it, and name it stackCells. Inside the Receive create one Move event for each cell, starting from the second (assuming the first already sits in the correct place).

`tile 1`.y + `tile 1`.height+gap_tile

Do this for each cell, and you will notice that — it won’t work.

This is due to the recalculation of the layout, which needs a minimum amount of time after each move. We can solve this by adding a 0.01 start delay for each cell moved.

Notice the small delay needed for the calculation. You can mask this by a loading animation but 0.05s is barely noticeable anyway.

Outcome

You should now have a neatly aligned list of cells that change height dynamically depending on the content fed by JSON. I hope the techniques used made sense to you and helped you in achieving even more complex prototypes. As soon as auto layout arrives in Protopie this whole process will hopefully get a whole lot easier. I cannot (and could not) wait for it. Thank you for reading.

the finished prototype with neatly aligned cells in different heights
Our result: neatly height-adapted stacked cells

Do you love agile product development? Have a look at our vacancies.

--

--