In the previous step, we created an ImageRow to display a row of images as part of building an image grid for our app.
Recall that our image grid will be structured as follows:
- Each image is stored in an `ImageItem`.
- Multiple items are laid out horizontally within a `ImageRow`.
- Multiple rows are laid out vertically within an `ImageGrid`.
In this step, we'll group multiple `ImageRow`s in an `ImageGrid` to display a grid of images. The code for `ImageGrid` is highly similar to what we wrote for `ImageRow`, so we won't have to go over it in much detail.
> **Note:**
> If you don't feel like typing along, you can find all the code for this step here:
> https://github.com/makepad/image_viewer/tree/main/step_5
## What you will learn
In this step, you will learn:
- How to group items vertically.
## Defining an `ImageGrid`
Let's start by adding a definition for an `ImageGrid`. An `ImageGrid` is responsible for laying out multiple `ImageRow`s vertically.
In **app.rs**, add the following code to the live design block, after the definition of `ImageRow`:
```rust
ImageGrid = {{ImageGrid}} {
<PortalList> {
flow: Down,
ImageRow = <ImageRow> {}
}
}
```
As with `ImageRow`, within an `ImageGrid`, we use a `PortalList` to list its rows. The only difference is that we use `flow: Down`, which ensures its children are laid out from top to bottom.
## Updating `App`
Now that we have an `ImageGrid`, let's update our definition of `App` to display an `ImageGrid` instead of an `ImageRow`.
In **app.rs**, replace the definition of `App` in the live design block with the one here below:
```rust
App = {{App}} {
ui: <Root> {
<Window> {
body = <View> {
<ImageGrid> {}
}
}
}
}
```
## Adding the Rust Code
Add the following code to **app.rs**:
```rust
#[derive(Live, LiveHook, Widget)]
pub struct ImageGrid {
#[deref]
view: View,
}
impl Widget for ImageGrid {
fn draw_walk(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
walk: Walk,
) -> DrawStep {
while let Some(item) = self.view.draw_walk(cx, scope, walk).step() {
if let Some(mut list) = item.as_portal_list().borrow_mut() {
list.set_item_range(cx, 0, 3);
while let Some(row_idx) = list.next_visible_item(cx) {
let row = list.item(cx, row_idx, live_id!(ImageRow));
row.draw_all(cx, &mut Scope::empty());
}
}
}
DrawStep::done()
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope)
}
}
```
This defines the `ImageGrid` struct and implements the `Widget` trait for it. This code is highly similar to what we wrote for the `ImageRow` struct, so we won't go over it in detail. The only difference is that instead of drawing 4 `ImageItems`, we are drawing 3 `ImageRows`.
## Checking our Progress so far
Let's check our progress so far.
Make sure you're in your package directory, and run:
```
cargo run --release
```
If everything is working correctly, a window containing a grid of placeholder images should appear on your screen:
![[ImageGrid Placeholders.png]]
## What's next
In this step, we created an `ImageGrid` to display a grid of images.
We now have an initial implementation of an image grid. It's still very limited — it always displays both the same number of rows and number of items per row, and it only displays placeholder images, but we're off to a good start!
In the next few steps, we'll remove these limitations and make the image grid dynamic.