Hi Stefan,
Thank you for your thoughtful response!
1) Let me show you what the "high-level construct of a layout system" looks like in my app. My app uses cards as a level of content between the bottom nav bar and a "list of stuff". They're still pretty basic for now, content-wise. Here are my cards in portrait mode:
In landscape mode, I can show two rows:
On my iPhone XS Max, in "wide landscape mode", I can see three rows:
If I were to do this in Flutter myself, I would switch between one column, two rows, and one row, depending on the screen width. A couple of flags, the card widgets as variables because they are used in multiple places, nothing too difficult. But it wouldn't be "Flutter-like" - you know, the widgets as one stream of builder-pattern code. Maybe you can do this more elegantly with flex properties/widgets in Flutter? Please reply and let me know!
How do I do this now? As I already mentioned, I use the
flutter_bootstrap plugin. That transfers the concept of the
Bootstrap grid to Flutter. What's the Bootstrap grid? Two things:
- The screen has rows. Each row has twelve columns.
- The rows come in six different sizes, from "extra small" (less than 576 pixels wide) to "extra extra large" (wider than 1400 pixels). The latter one is new in Bootstrap 5, it used to end at "extra large" (wider than 1200 pixels).
The key is that you can specify how many columns your content takes up for multiple sizes. So for my cards, I say:
- 12 columns in extra small: Each card fills the whole width (col-12). That's portrait mode.
- 6 columns in small: I show two cards per row (col-sm-6). That's landscape mode. Since the total number of columns exceeds 12, the third card (columns 13-18) automatically wraps to a new row.
- 4 columns in medium: Now I show three cards in a row (col-md-4). That's wide landscape mode on my iPhone XS Max.
Here's my simplified code, with one asterisk:
return BootstrapContainer(
children: [
BootstrapRow(
children: <BootstrapCol>[
BootstrapCol(
sizes: 'col-12 col-sm-6 col-md-4',
child: // visit card,
),
BootstrapCol(
sizes: 'col-12 col-sm-6 col-md-4',
child: // customer card,
),
BootstrapCol(
sizes: 'col-12 col-sm-6 col-md-4',
child: // veterinary card,
)
],
),
],
);
To me, this has four advantages over the "plain Flutter with Rows & Columns approach":
- It's more Flutter-like: No flags, no widget variables, no branches, just the builder pattern in action.
- It's more obvious: I can parse & understand the compact sizes: 'col-12 col-sm-6 col-md-4' much quicker than the control flow of flags, widget variables, and branches.
- It's a higher level of abstraction: Software development is the history of raising the level of abstraction in our code. To me, this is such a raised level.
- It's familiar: This only applies to people like me that know the Bootstrap grid.
So, what's the asterisk? I mentioned in my original post that the Bootstrap grid is "too coarse-grained for phones (everything below 576 pixels is one category)". And a 4 inch iPhone 5 is 568 pixels wide in landscape mode, just a hair below the 576-pixel breakpoint for "extra small". So a card is six columns wide in extra small if the screen width > 560 pixels wide (and less than 576):
final width = SDevice.instance.retrieveScreenWidth(context);
final smallWidth = width >= 560 ? 'col-6' : 'col-12';
return BootstrapContainer(
children: [
BootstrapRow(
children: <BootstrapCol>[
BootstrapCol(
sizes: '$smallWidth col-sm-6 col-md-4',
child: // visits card,
),
BootstrapCol(
sizes: '$smallWidth col-sm-6 col-md-4',
child: // customer card,
),
BootstrapCol(
sizes: '$smallWidth col-sm-6 col-md-4',
child: // veterinary card,
)
],
),
],
);
Now I do have one variable. But that's still better than the alternatives to me.
I found the Bootstrap grid to work well here. I think it does miss that one "extra extra small size". But then again, it was built for websites in general, not for phones specifically.
Please show me if you can achieve this elegantly with "flex code" in plain Flutter. Thank you!
2) Thank you for letting me know that Android fragments don't apply automatically! Switching a bottom nav bar for a side bar seems such an obvious approach, beyond a certain screen size. I certainly hope that this becomes a feature in frameworks. Yes, of course, we can all do this ourselves in our code. But why should we if if "soo obvious"?
3) It's great that Ubuntu gets its native widget set. But for my app, that's irrelevant. Windows and macOS count for me.
I seriously doubt that you can repaint a whole OS widget set in one man-month, as you suggested: It's dark mode, it's light mode, it's animations, it's a lot of stuff. And you can sink weeks into possible showstopper issues like "
janky animations on iOS".
I was hoping for some sort of announcement at Flutter Engage, but none came from what I know. I submitted this as a
question with #FlutterAsk, but they didn't pick it up. I'm afraid Google isn't going to do this. I mean more than two years after 1.0, we still don't have a
proper iOS table widget!
I'm also afraid it's too big for a community effort. And as a third-party or commercial vendor, I'd be hesitant to step in here. What if Google does release such a widget set after all? And would Microsoft and Apple support a cross-platform framework by repainting all their OS widgets for Flutter? Microsoft maybe, once they figure out what "fluent design" really looks like in Windows. But Apple? Probably not.