Innovative Strategies for Flutter Adaptive Design and Layout
Written on
The adaptive design strategy for Flutter, as overlooked by GSKinner and Very Good Ventures, is crucial for achieving remarkable control over app aesthetics. This method is invaluable for developers aiming to create exceptional Flutter applications.
The code referenced in this article can be found here:
It is located within the custom widgets directory of the to-do app subfolder.
Understanding the Importance of Flutter Adaptive Design
While managing navigation at a shared scaffold level is beneficial, challenges arise with foldable devices and larger screens.
Foldables present a unique issue; there is no clear indication of the device's fold status when it is closed. Alternatively, when the hinge is open, data can be accessed through the DisplayFeature API.
Microsoft addressed this issue with their foldable Surface devices by implementing a dual-pane layout that adapts based on hinge detection and screen size breakpoints, allowing for the correct display of either a single or dual-pane interface. Their two-pane layout class also accommodates orientation shifts.
However, the Flutter Adaptive Scaffold has its own challenges. When Google revamped the adaptive scaffold, they assumed that handling foldables at the scaffold level with the secondBody slots would suffice. This is how the adaptive scaffold operates within the package, relying on the adaptive layout class.
In practice, crafting a custom adaptive scaffold is often necessary to dictate navigation preferences effectively, leading to additional coding requirements.
I will first discuss general helper classes and methods, followed by instructions on creating a custom scaffold that supports dual-pane layouts in accordance with the dual-screen package, which enables canonical layouts.
Automatic Adaptations in Flutter SDK
Here are the automatic adaptive constructors already integrated into the Flutter SDK:
- Switch.adaptive()
- Slider.adaptive()
- CircularProgressIndicator.adaptive()
- Checkbox.adaptive()
- Radio.adaptive()
Additional adaptations are achieved through modifications to specific third-party packages and useful utility classes and methods. The first step is accurately detecting the operating system platform to ensure the correct setting of the simulated OS via the ThemeData.platform property during widget testing.
Accessing Platform OS Detection for Testing
Let’s delve into the importance of the ThemeData.platform property documentation:
When executing widget tests, the Flutter SDK framework defaults the platform object to TargetPlatform.android. Since this was initially the assumption in testing, we should utilize Theme.of(context).platform for accurate platform detection.
Moreover, in the RunningPlatform class, it’s important to recognize that if using a web browser, an OS platform will still be detected. Thus, the class implementation must check the isWeb property first.
Following this, whenever Platform.iOS appears, we should replace it with RunningPlatform.isIOS(context), which can then be utilized in new adaptive methods, such as creating adaptive text fields and displaying adaptive dialogs.
Additional Helper Classes and Methods
Before proceeding to redesign the adaptive scaffold for supporting canonical layouts, we need some supplementary helper classes.
Helpers for Foldable Devices
- FoldObserver: This extension is designed to retrieve hinge status, ensuring the app's orientation aligns with the hinge configuration and adapting to the screen size. It is crucial to include the FoldObserver within the NavigatorObservers slot of the MaterialApp.
- Dialog Route Fold: This ensures correct display on foldable devices.
Next, I’ll demonstrate how to modify the adaptive scaffold to support canonical layouts.
Implementing a Canonical Scaffold for Canonical Layouts
The following is the AdaptiveLayout class that will be utilized to create the canonical scaffold:
We will not utilize the secondBody slots, and the bodyRatio parameter will be set to 1.0.
First, we need to establish breakpoint values for the window:
Insert window breakpoint values here
Next, we’ll create custom app breakpoints:
Insert custom app breakpoints here
To effectively utilize AdaptiveLayout, it must be wrapped in a Scaffold, so we will set up a stateful class:
Insert stateful class setup here
The key alteration from the adaptive scaffold in the Flutter Adaptive Scaffold package is that the bodyRatio is set to 1.0 and the secondBody parameters are excluded.
The methods will mirror those in the adaptive scaffold, with the drawer being switched to a specific NavigationDrawer, which is a new addition to the Flutter SDK.
In the paired state class of the stateful widget, the AdaptiveLayout will, of course, be wrapped with a Scaffold Widget.
The rationale for removing the SecondBody slots, aside from supporting canonical layouts, is that while we can transfer arguments to screens via router navigation 2.0 packages, there is no method for passing arguments to a shared scaffold through the router.
The complete source code for the canonical scaffold is:
Insert source code here
Now, we need accessory classes for the canonical layouts.
Canonical Layouts
Canonical layouts as described in the Material Design 3 specification can be found here:
What remains uncommunicated is that Microsoft has already implemented the canonical layout API via the two-pane layout in the dual-screen package:
To effectively leverage this, we need some accessory classes. First, we will publicize the private methods from the adaptive scaffold:
- CanonicalBrickLayout Delegate
- CanonicalBrickLayout
- toMaterialGrid method
Additionally, an accessory class will be added to ensure a navigation route is only visible and functional when in a single-screen compact use case, presuming that at the expanded state, a two-pane layout is in effect.
We must consider that the hinge will be null if the fold is not open or if the device is not foldable. Consequently, we implement logic to assess if we are at the single-screen breakpoint to integrate the navigator into the set of panes, as illustrated by the ListDetail example from the Microsoft Surface Duo SDK:
Insert ListDetail example here
Single-screen scenarios occur only when the hinge is open on a foldable or at the compact screen point:
Insert logic for single-screen detection here
According to the Material Design 3 guidelines, we would adjust this to 840 instead of 1000.
The Microsoft Duo SDK samples can be found here:
Final Thoughts
In conclusion, implementing canonical layouts is inevitable, though you will find that Microsoft's contributions significantly reduce the workload for supporting Flutter on Surface Duo devices. The primary adaptive pattern, aside from custom adaptive constructors, involves using a Canonical Adaptive Scaffold and executing Canonical Layouts at the screen level.
Resources
My resources for Flutter App Architecture can be accessed here:
https://github.com/fredgrott/flutter_bytes_app_arch
For a more affordable educational experience in design, check out Chris Do’s YouTube channels:
Additional resources include:
- AIGA: https://www.aiga.org/resources
- Nielsen Norman Group Articles: https://www.nngroup.com/articles/
- Flutter Internals Book: https://flutter.megathink.com/
- Dart VM Internals: https://mrale.ph/dartvm/
- Awesome Psychology Resource List on GitHub: https://github.com/weeeBox/awesome-psychology
About the Author
Fred Grott is currently developing course materials focused on front-end development and user interface design.
You can find me on:
- YouTube: https://www.youtube.com/c/FredGrott
- LinkedIn: https://www.linkedin.com/in/fredgrottstartupfluttermobileappdesigner/
- Instagram: https://www.instagram.com/fredgrott/
- Twitter: https://twitter.com/fredgrott
As I embark on building my brand storytelling studio, I will be releasing various UI Kits and books on GumRoad:
I will also share free UI Kits on:
- GitHub: https://github.com/fredgrott
- Dribbble: https://dribbble.com/FredGrott
- Behance: https://www.behance.net/gwsfredgrott