[GH-ISSUE #285] Understanding Flex & Layouts #220

Closed
opened 2026-03-04 01:03:06 +03:00 by kerem · 3 comments
Owner

Originally created by @joshuamcginnis on GitHub (May 29, 2019).
Original GitHub issue: https://github.com/rivo/tview/issues/285

I'm struggling with understanding the various layout options. For example, I'm trying to implement a basic "Main Menu" for a little app I'm experimenting with.

image

From what I can gather, I should create a "Page" that the user can switch to during the app lifecycle. And this Page will need to contain the necessary layout primitives that implement the menu itself.

However; for what I want to do above, a regular Modal won't work. Nor will a text box since a TextBox cannot have a title and a primitive (List) cannot be added inside of the box. I'll need to do some degree of nesting of layouts and elements.

The wiki shows this example for how to create a more robust modal-like box that is centered:

// Returns a new primitive which puts the provided primitive in the center and
// sets its size to the given width and height.
modal := func(p tview.Primitive, width, height int) tview.Primitive {
	return tview.NewFlex().
		AddItem(nil, 0, 1, false).
		AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
			AddItem(nil, 0, 1, false).
			AddItem(p, 0, 2, false).
			AddItem(nil, 0, 1, false), 0, 2, false).
		AddItem(nil, 0, 1, false)
}

I understand why the top-level AddItems are doing; creating three rows which the middle row being 2x relative sizing to the top and bottom nil primitive rows. But why does the inner Flex row also have nil top and bottom rows?

In general, do you have any tips for approaching layout and developing a more thorough understanding of how to layout the various components of an app?

When taking in account the various layout types, the need to often nest them, keeping track of explicit vs. proportional sizing, ordering of rows / cols - I'm finding the the cognitive load builds rather quickly.

I'd appreciate any thoughts on this.

Originally created by @joshuamcginnis on GitHub (May 29, 2019). Original GitHub issue: https://github.com/rivo/tview/issues/285 I'm struggling with understanding the various layout options. For example, I'm trying to implement a basic "Main Menu" for a little app I'm experimenting with. ![image](https://user-images.githubusercontent.com/391074/58598596-d7615780-8231-11e9-8739-4457dcbb2d65.png) From what I can gather, I should create a "Page" that the user can switch to during the app lifecycle. And this Page will need to contain the necessary layout primitives that implement the menu itself. However; for what I want to do above, a regular Modal won't work. Nor will a text box since a TextBox cannot have a title and a primitive (List) cannot be added inside of the box. I'll need to do some degree of nesting of layouts and elements. The wiki shows this example for how to create a more robust modal-like box that is centered: ```go // Returns a new primitive which puts the provided primitive in the center and // sets its size to the given width and height. modal := func(p tview.Primitive, width, height int) tview.Primitive { return tview.NewFlex(). AddItem(nil, 0, 1, false). AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(nil, 0, 1, false). AddItem(p, 0, 2, false). AddItem(nil, 0, 1, false), 0, 2, false). AddItem(nil, 0, 1, false) } ``` I understand why the top-level `AddItems` are doing; creating three rows which the middle row being 2x relative sizing to the top and bottom `nil` primitive rows. But why does the inner `Flex` row also have `nil` top and bottom rows? In general, do you have any tips for approaching layout and developing a more thorough understanding of how to layout the various components of an app? When taking in account the various layout types, the need to often nest them, keeping track of explicit vs. proportional sizing, ordering of rows / cols - I'm finding the the cognitive load builds rather quickly. I'd appreciate any thoughts on this.
kerem closed this issue 2026-03-04 01:03:06 +03:00
Author
Owner

@rivo commented on GitHub (Jun 2, 2019):

The Wiki example you mentioned starts with centering a primitive using Flex but further down, there is a simpler example that uses Grid which, in my opinion, is the better choice as you don't need to nest two Flex items to achieve the same effect.

The Modal class is not very flexible. It is meant to be used to display simple messages or confirmation prompts.

For your menu example, you could wrap a List into a Frame (see also the Wiki for an example). The List could be used to handle the menu options. The Frame could be used to display a title etc.

If all of your pages have this layout, it makes sense to wrap this into a function or class that automatically generates the Page object given title, menu options, and so on. I'm taking this approach my applications as well as in the presentation demo:

github.com/rivo/tview@29b0d3cfbf/demos/presentation/main.go (L23-L26)

Each slide in the presentation then implements this function type, which makes it very easy to switch between slides.

Let me know if this helps you.

<!-- gh-comment-id:498058229 --> @rivo commented on GitHub (Jun 2, 2019): The Wiki example you mentioned starts with centering a primitive using `Flex` but further down, there is a simpler example that uses `Grid` which, in my opinion, is the better choice as you don't need to nest two `Flex` items to achieve the same effect. The `Modal` class is not very flexible. It is meant to be used to display simple messages or confirmation prompts. For your menu example, you could wrap a `List` into a [`Frame`](https://godoc.org/github.com/rivo/tview#Frame) (see also the [Wiki](https://github.com/rivo/tview/wiki/Frame) for an example). The `List` could be used to handle the menu options. The `Frame` could be used to display a title etc. If all of your pages have this layout, it makes sense to wrap this into a function or class that automatically generates the `Page` object given title, menu options, and so on. I'm taking this approach my applications as well as in the presentation demo: https://github.com/rivo/tview/blob/29b0d3cfbfad0229345588beba99a1732599444c/demos/presentation/main.go#L23-L26 Each slide in the presentation then implements this function type, which makes it very easy to switch between slides. Let me know if this helps you.
Author
Owner

@joshuamcginnis commented on GitHub (Jun 3, 2019):

@rivo I really appreciate you taking the time to read through my issue and provide a thoughtful response with many pointers. 🙏 Thanks for that.

I'm taking this approach my applications as well as in the presentation demo.

It would be awesome if you could share the source for your demo (presuming we're talking about the one in the main project readme). I think seeing many examples of more complex workflows would really help me to best understand how to lay out my project.

Let me know if this helps you.
Yessir. 👍

<!-- gh-comment-id:498463356 --> @joshuamcginnis commented on GitHub (Jun 3, 2019): @rivo I really appreciate you taking the time to read through my issue and provide a thoughtful response with many pointers. 🙏 Thanks for that. > I'm taking this approach my applications as well as in the presentation demo. It would be awesome if you could share the source for your demo (presuming we're talking about the one in the main project readme). I think seeing many examples of more complex workflows would really help me to best understand how to lay out my project. > Let me know if this helps you. Yessir. 👍
Author
Owner

@rivo commented on GitHub (Jun 9, 2019):

I can't release my private applications but the presentation demo is part of the repo. You'll find it in the demos/presentation/ folder.

<!-- gh-comment-id:500212311 --> @rivo commented on GitHub (Jun 9, 2019): I can't release my private applications but the presentation demo is part of the repo. You'll find it in the `demos/presentation/` folder.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#220
No description provided.