Jan 30, 2022

Layouting across platforms

Building user interfaces involves a lot of layout work: Placing controls, aligning list items, adding spacing, sticky elements, and other details that create an easy-to-use, low-friction interface.

Depending on the platform, performing these tasks might require a couple of minutes, but there are cases when you spend much more time, resorting to hacks to make your solution work somehow. I’d like to argue that even though most engineers have their own story of figuring out how to center a div, the web as a platform has matured into one of the most powerful environments where complex layouts are relatively easy to implement.

In the following, I’ll examine some implementations of layouting of different systems, be it the (modern) web, or desktop and mobile applications.

Layouting across platforms

The Web

Over more than two decades, the web as a platform has seen a multitude of features to enable complex layouts. In its current form, if you need more than the normal element flow across a page, you can choose from powerful layouts including the two most popular: Flexbox (which arranges items in rows or columns, while staying responsive for different viewport sizes) and CSS Grid (offering a two-dimensional layout for complex layouts).

Due to the long history of the web, and the fact that powerful layouting systems like CSS Grid are relatively recent additions, there have been numerous legacy approaches to achieving similar results over time.

Today, the high compatibility with most browsers has made it easy to create complex user interfaces, as you can rely on a number of supported methods, and use polyfills in the worst case.

Centering A Box

.container {
  display: grid;
  place-items: center;
}

Items (e.g. Buttons and Text) on the same level

.container {
  display: flex;
  align-items: center;
}

Java

In the early days of building graphical user interfaces (GUIs), most platforms implemented their own layouting systems. Thinking back to Java AWT or Swing, building a layout involved choosing the right layout manager for the problem, then trying as hard as possible to

public static void addComponentsToPane(Container pane) {
	pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));

	addAButton("Button 1", pane);
	addAButton("Button 2", pane);
	addAButton("Button 3", pane);
	addAButton("Long-Named Button 4", pane);
	addAButton("5", pane);
}

private static void addAButton(String text, Container container) {
	JButton button = new JButton(text);
	button.setAlignmentX(Component.CENTER_ALIGNMENT);
	container.add(button);
}

Newer approaches tend to do one of the following things: The first change is to add (partial) support for more well-known styling and layouting systems, like CSS. JavaFX, for example, offers to use CSS to build user interfaces. While it doesn’t support all features, using a system that’s popular in other parts of software engineering helps in many ways, making the software slightly easier to maintain over time, compared to legacy layout managers.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class HelloFontFace extends Application {
    @Override public void start(Stage primaryStage) {
        Label label = new Label("Hello @FontFace");
        label.setStyle("-fx-font-family: sample; -fx-font-size: 80;");
        Scene scene = new Scene(label);
        scene.getStylesheets().add("http://font.samples/web?family=samples");
        primaryStage.setTitle("Hello @FontFace");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) { launch(args); }
}

SwiftUI

Then, there’s another possible direction if you don’t want to add the powers of CSS: Add primitives to your layouting and styling system, like SwiftUI introduced in 2019.

struct ContentView: View {
	var body: some View {
    HStack {
        Spacer()
        Button(action: {
            self.onContinue()
        }, label: {
            Text("Get Started")
                .font(.system(.body, design: .rounded))
                .fontWeight(.semibold)
                .padding()
                .background(Color.accentColor)
                .foregroundColor(.white)
                .cornerRadius(18)
        })
        Spacer()
    }
    .padding([.top], 40)
	}
}

Here, you have a lot of similarities with the web, without requiring users to go and write HTML or CSS directly. For framework designers and engineers, this option allows for more flexibility and control, as they do not have to worry about weird edge cases that come up when you must support rules you haven’t built or checked yourself.

Flutter

Google have built a similar combination of layout and styling with Flutter

Widget _buildList() {
  return ListView(
    children: [
      _tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),
      _tile('The Castro Theater', '429 Castro St', Icons.theaters),
      _tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),
      _tile('Roxie Theater', '3117 16th St', Icons.theaters),
      _tile('United Artists Stonestown Twin', '501 Buckingham Way',
          Icons.theaters),
      _tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),
      const Divider(),
      _tile('K\'s Kitchen', '757 Monterey Blvd', Icons.restaurant),
      _tile('Emmy\'s Restaurant', '1923 Ocean Ave', Icons.restaurant),
      _tile(
          'Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),
      _tile('La Ciccia', '291 30th St', Icons.restaurant),
    ],
  );
}

ListTile _tile(String title, String subtitle, IconData icon) {
  return ListTile(
    title: Text(title,
        style: const TextStyle(
          fontWeight: FontWeight.w500,
          fontSize: 20,
        )),
    subtitle: Text(subtitle),
    leading: Icon(
      icon,
      color: Colors.blue[500],
    ),
  );
}

Over time, almost every platform has created primitives for layouts like lists and grids, taking some inspiration from what existed previously, including the web. While I don’t think that every application should render a browser view to allow developers to use what they’re familiar with, I do believe that the web allows moving really fast, and much faster than most native frameworks for desktop and mobile applications, at least for myself.

I’m really curious what the next decade of building user interfaces brings us: Will we still try to break down pages into atomic, reusable components, add styles via CSS-in-JS or Tailwind on the web, or build applications for the Apple ecosystem using SwiftUI? Only time can tell.