This article came from this in-depth video:
published on the official Flutter channel on YouTube which explains the difference between the concept of adaptive and responsive interface.
Both concepts are very important in the development of interfaces and their meaning is often confused or misinterpreted, in this article a definition will be given first, and then concrete examples will be presented to distinguish the two cases.
Adaptive
Adaptive means that the application must adapt to the style and expectations of a specific platform's users, it must follow the guidelines for creating layouts, inputs, and elements. This means that if we want to write an application that works right both on mobile and on desktop, our UI will have to adapt, for example, to the different types of interactions typical of that platform, therefore touch on mobile, mouse, and keyboard on desktop; on mobile, navigation and transitions from one page to another must be right for the platform as well as the physics of scrolling and the back button.
In addition to this, the elements of our interface will have to adapt to the platform on which they are displayed, an example of all is the switches for enabling or disabling a feature or preferences; each operating system that Flutter supports has its way of representing them and it is up to us to visualize the correct one for each platform we want to support.
Responsive
Responsive means that the layout of the elements within an application changes according to the size of the screen on which it is displayed or the orientation of the device. This means that on a device with a small screen like that of a smartphone, the contents, navigation, and menus will have a different position and size from that the same when viewed on a monitor or TV. If our application is optimized for small screens, when viewed on large screens there could be a lot of wasted space because of the new space available, unlike if our application was designed for a large screen when will be viewed on a small screen may be difficult to navigate or the content may be too condensed or unreadable.
The concept of responsive was born in the web as the browsers that display web pages and applications are present on almost all devices of any size that we use every day such as smartphones, tablets, PCs, or TVs. Therefore in the realization of the sites, it must be taken into account that these could be seen on screens of any size and orientation. Flutter, which allows us to develop multi-platform applications for Android, iOS, Web, and Desktop, inherits the same implementation challenges.
Guidelines and packages
As we have seen in the previous definitions, to develop an application that makes our users happy whatever the device they use, there must be a good mix of adaptive and responsive. But how can we go about implementing all this?
A first help is given to us by the classes related to the LayoutBuilder and the MediaQuery, with these two classes we can have the size of the screen or window of our application and build or modify the layout according to some breakpoints that we have defined. As a starting point these could be breakpoints to use to structure our layout:
- Small (<640px): portrait smartphone
- Medium (<1000px): horizontal tablet
- Large (> 1200px): desktop
Thinking about navigation when we have a small screen we can take advantage of the NavigationBar, the TabBar and use a pop-up menu with the Drawer. When we have a slightly larger screen we can eliminate the tabs and organize the Drawer differently, leaving it always visible as if it were a sidebar, perhaps in a compact version, which then becomes expanded when we switch to the desktop.
Organize contents
For the organization of contents in our application we often refer to the concept of Reposition with which we modify the position of the elements within the interface to adapt to the change in the size of the window, the smaller it is the more we arrange the elements vertically by organizing them in lists, the more the window widens the more we exploit the width by arranging the contents horizontally, side by side and arranged on a line.
Another technique used is to replace or collapse portions of the UI with others that are better suited to a particular size of the window, a classic case is the one mentioned in the previous paragraph with the use of the NavigationBar when the window is small (or is on mobile) and then go to the drawer when the window is large.
Finally, it is also possible to take an extra step and move to architect our app in a different way depending on the size of the screen, this technique is certainly more advanced, it requires more work but in some situations it is to be preferred for example when we have an interface with list and detail in which on small screens two separate pages are used and you switch from one to the other, while on large screens the two are side by side and always visible and when you tap on the list the content of the detail view change.
Another very useful package for dynamically arranging content in our application is flutter_staggered_grid_view which provides a series of widgets to organize content such as staggered, masonry, quilted style grids, woven. Its use cases are for example visual contents such as photos, videos, and news; used in tandem with MediaQuery it is also possible to dynamize the size and number of columns as described in this article: Create a Simple Responsive GridView with Flutter.
Window Size
When developing a desktop application, you may want to control the size of the window so as not to make it go below a certain size, because the contents would look bad or because a layout that fits well to those dimensions has not yet been thought of. To prevent the window from shrinking too much, the package window_manager comes to the rescue, which puts at the disposal of comfortable bees to define the size of our window on Windows, macOS, and Linux. For example, to open our application at a size of 1200x800 and not make it squeeze more than 800x600 we can write this:
...
// Use it only after calling `hiddenWindowAtLaunch`
windowManager.waitUntilReadyToShow().then((_) async{
...
//Set window size to 1200x800
await windowManager.setSize(Size(1200, 800));
//Set window minimun size to 800x600
await windowManager.setMinimumSize(Size(800, 600));
...
});
...
The package also allows us to prevent the window from being resized with the setResizable (false)
API but I believe that from a usability point of view this is too strong an imposition and should be avoided.
Adapt to the operating system
As we reported earlier, an important part of the user experience is our app's ability to adapt to the operating system it runs on. The Flutter developer community has produced several packages that can be very useful for us to develop adaptive applications on supported desktop operating systems:
- macos_ui: set of widgets and themes that implement Apple's guidelines for macOS;
- fluent_ui: Unofficial implementation of Fluent UI based on official documentation;
- yaru: implementation of the Yaru style of the Ubuntu desktop, customizations for Ubuntu flavors are also available such as Kubuntu or Xubuntu;
- libadwaita: Libadwaita-based widgets for the implementation of GNOME guidelines;
- stockholm: a fairly recent package with a collection of desktop environment widgets designed to be integrated into both Windows and macOS;
- arna: a set of widgets for building applications with Flutter.
Here is also a very interesting article that describes how to create a perfectly adaptive application on macOS and Windows, useful for saving application shortcuts: Using Flutter to build a native-looking desktop app for macOS and Windows.
Always linked to the theme of the controls of our applications that adapt according to the operating system, it is very important to also mention the package flutter_platform_widgets which wants to provide widgets that are platform aware and that can be easily called up with a single API, depending on whether you are on Android or iOS the package will provide the Material or Cupertino version for the requested widget:
//A switch widget that will use a Switch for material
// or a CupertinoSwitch for cupertino.
return PlatformSwitch(
onChanged: (bool value) {},
value: value,
);
Conclusions
As we have seen, the correct implementation of the concepts of responsive and adaptive allow us to develop applications that can visually satisfy our users on any OS they use our application and Flutter provides us with many packages to help us in this work. For further information I leave you some useful links:
- Creating responsive and adaptive apps
- Platform-specific behaviors and adaptations
- Building adaptive apps
- Responsive design techniques
- Screen sizes and breakpoints
- Material guidelines on responsive UI layout
- Build Responsive UIs in Flutter
- Typical Breakpoints
See you in the next article and in the meantime Happy Fluttering ๐
The icon on the cover is from by: Tablet icons created by smalllikeart on Flaticon.