{"id":2473,"date":"2022-05-19T12:18:20","date_gmt":"2022-05-19T19:18:20","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=2473"},"modified":"2022-05-19T12:18:20","modified_gmt":"2022-05-19T19:18:20","slug":"flutter-3-foldable","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/flutter-3-foldable\/","title":{"rendered":"Flutter foldable support is now stable"},"content":{"rendered":"<p>\n  Hello! <a href=\"https:\/\/medium.com\/flutter\/whats-new-in-flutter-3-8c74a5bc32d0\">Flutter 3 was announced<\/a> last week, and we have good news to share with you. The foldable support that we contributed to Flutter is now released in the latest <i>stable<\/i> branch. Let us dive into what this means for you. We will first go over the new API that you can use and then review what else Flutter now does for you out of the box.\n<\/p>\n<h2>What are Display Features?<\/h2>\n<p>\n  Display features are parts of the display that create a visual distortion and can create a logical separation in the screen space. Our focus is on the hinge of the Surface Duo, but that is not the only type of display feature that Flutter knows about. The folding area on the Surface Galaxy Fold and the notch or cutout on many smartphones are also <em>display features<\/em>.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1100\" height=\"456\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-3-types-of-display-fe.png\" class=\"wp-image-2474\" alt=\"Visual representation of the 3 types of display features that Flutter knows about. They are the hinge, fold, and cut out. In the image, one can see the hinge as a clear separation between two solid screens, the fold as the part that bends on a flexible continuous screen, and the cutout as a normal smartphone with the top part of the screen having a hole for the camera\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-3-types-of-display-fe.png 1100w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-3-types-of-display-fe-300x124.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-3-types-of-display-fe-1024x424.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-3-types-of-display-fe-768x318.png 768w\" sizes=\"(max-width: 1100px) 100vw, 1100px\" \/>\n<\/p>\n<p>\n  For developers that work with both Android and Flutter, it is worth noting that Flutter display features are a bit different from Android display features. In Android, <a href=\"https:\/\/developer.android.com\/reference\/android\/view\/DisplayCutout\">cutouts<\/a> and <a href=\"https:\/\/developer.android.com\/reference\/androidx\/window\/layout\/DisplayFeature\">display features<\/a> are different concepts. Since Flutter works at a higher level, unifying the two concepts under one API was important.\n<\/p>\n<p>\n  Until now, screens rarely made a shape other than a 2D plane. When a screen has a <code>hinge<\/code> or <code>fold<\/code> display feature, it can change shape. These shapes are called postures. When it looks like a book or laptop, the posture is <code>half-opened<\/code>. When the screen is continuous, the posture is <code>flat<\/code>.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1101\" height=\"644\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-flat-and-half-open.png\" class=\"wp-image-2475\" alt=\"Visual representation of the &quot;flat&quot; and &quot;half-opened&quot; postures as two images of a foldable device, first as a flat surface and then bent in the middle, like a book\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-flat-and-half-open.png 1101w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-flat-and-half-open-300x175.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-flat-and-half-open-1024x599.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/visual-representation-of-the-flat-and-half-open-768x449.png 768w\" sizes=\"(max-width: 1101px) 100vw, 1101px\" \/>\n<\/p>\n<p>\n  Now that we went through the types of display features and what properties they might have, there is enough context for me to show you the structure that the DisplayFeature class has:\n<\/p>\n<pre>class DisplayFeature {\r\n  final Rect bounds;\r\n  final DisplayFeatureType type;\r\n  final DisplayFeatureState state; \r\n}<\/pre>\n<p>\n  The <code>bounds<\/code> represent the location and size of the display feature on the screen. The <code>type<\/code> and <code>state<\/code> are direct representations of the images above.\n<\/p>\n<h2>MediaQuery has a new property<\/h2>\n<p>\n  The way you access information about display features is through <code>MediaQuery.of(context).displayFeatures<\/code>. This is a new property, and it is a list. It is empty for devices that do not have any display features. For the Surface Duo, you can expect this list to have one item when your app is spanned across both screens and be empty when your app is in single-screen mode.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1100\" height=\"731\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our.png\" class=\"wp-image-2476\" alt=\"Screenshot of the Surface Duo emulator running our Flutter samples app, spanning across both screens. The app shows debug information, like the display feature being detected as a Hinge with a Flat posture\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our.png 1100w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-300x199.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-1024x680.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-768x510.png 768w\" sizes=\"(max-width: 1100px) 100vw, 1100px\" \/>\n<\/p>\n<p>\n  The app behavior should be the same in single-screen mode as on a candy bar smartphone. Therefore, no display feature is reported. Another way of thinking about it is that only display features that overlap your rendering area are reported.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1100\" height=\"731\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-a-surface-duo-running-our-flutter-sa.png\" class=\"wp-image-2477\" alt=\"Screenshot of a Surface Duo running our Flutter samples app on a single screen. The app shows debug information, like the display feature not being detected.\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-a-surface-duo-running-our-flutter-sa.png 1100w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-a-surface-duo-running-our-flutter-sa-300x199.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-a-surface-duo-running-our-flutter-sa-1024x680.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-a-surface-duo-running-our-flutter-sa-768x510.png 768w\" sizes=\"(max-width: 1100px) 100vw, 1100px\" \/>\n<\/p>\n<p>\n  On foldable devices that also have a cutout, you can expect the list to have two display features, one for the folding area and one for the cutout. If you rely on display features in many parts of your codebase, we recommend creating higher-level functions or widgets that help you with your specific enhancements. For example, checking the list to see if there is a hinge on the device could look like this and can be accessed through <code>MediaQuery.of(context).hinge<\/code>:\n<\/p>\n<pre>\/\/\/ Extension method that helps with working with the hinge specifically.\r\nextension MediaQueryHinge on MediaQueryData {\r\n  DisplayFeature? get hinge {\r\n    for (final DisplayFeature e in displayFeatures) {\r\n      if (e.type == DisplayFeatureType.hinge)\r\n        return e;\r\n    }\r\n    return null;\r\n  }\r\n}<\/pre>\n<h2>Batteries included<\/h2>\n<p>\n  Some things simply work without you needing to do anything. For example, dialogs and popups were made aware of existing display features. They avoid overlapping them. \n<\/p>\n<p>\n  Dialogs, for instance, typically show in the middle of the screen. This would not be good when your app is spanned, since the dialog would overlap the hinge. To fix this, the default behavior for dialogs now is to show on the <em>first<\/em> screen. The default behavior depends on <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Directionality-class.html\">Directionality<\/a> and puts the dialog on the left for left-to-right layouts or on the right on right-to-left layouts.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1100\" height=\"731\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-1.png\" class=\"wp-image-2478\" alt=\"Screenshot of the Surface Duo emulator running our Flutter samples app, showing a dialog on the left screen in an English language left-to-right setup\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-1.png 1100w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-1-300x199.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-1-1024x680.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/screenshot-of-the-surface-duo-emulator-running-our-1-768x510.png 768w\" sizes=\"(max-width: 1100px) 100vw, 1100px\" \/>\n<\/p>\n<p>\n  You have control over this behavior if you want to do something different. If you need finer control over this, you can use the <code>anchorPoint<\/code> parameter. This is like a target on your display. The screen containing the target is the screen that renders the dialog. The <code>anchorPoint<\/code> parameter is newly added to <a href=\"https:\/\/api.flutter.dev\/flutter\/material\/showDialog.html\"><code>showDialog<\/code><\/a> and similar methods and is optional. What we think is particularly cool about this parameter is that you can easily show the dialog on the same screen that the user last interacted with. For example, you can create a helper widget or method that gives you the location on the screen of the button that led to showing the dialog.\n<\/p>\n<pre>showDialog(\r\n  context: context,\r\n  anchorPoint: Offset(1000, 1000), \/\/ New and optional parameter.\r\n  builder: \u2026,\r\n);<\/pre>\n<p>\n  If instead of using <code>showDialog<\/code>, you use custom popup routes and the navigator directly for rendering dialogs, you should wrap your routes with <code>DisplayFeatureSubScreen<\/code>. Without doing this, your dialogs will overlap the hinge.\n<\/p>\n<pre>class _MyRoute&lt;T&gt; extends PopupRoute&lt;T&gt; {\r\n  @override\r\n  Widget buildPage(...) {\r\n    return DisplayFeatureSubScreen(\r\n      child: ..., \/\/ previous content of buildPage\r\n      anchorPoint: ..., \/\/ optional\r\n    );\r\n  }\r\n}<\/pre>\n<h2>Popup menus just work<\/h2>\n<p>\n  Popup menus, such as those in the following image, are also now aware of display features and avoid overlapping them. There is no change that you need to make since these contributions were made to <a href=\"https:\/\/api.flutter.dev\/flutter\/material\/showMenu.html\"><code>showMenu<\/code><\/a>, which is then used by <a href=\"https:\/\/api.flutter.dev\/flutter\/material\/PopupMenuButton-class.html\"><code>PopupMenuButton<\/code><\/a>.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1100\" height=\"731\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/image-of-a-spanned-app-showing-a-list-on-the-left.png\" class=\"wp-image-2479\" alt=\"Image of a spanned app, showing a list on the left screen. Each item in the list has a 3-dots icon on the right side that can open a menu. One of the items has the menu showing. There is a red outline that shows where the menu would have normally shown.\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/image-of-a-spanned-app-showing-a-list-on-the-left.png 1100w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/image-of-a-spanned-app-showing-a-list-on-the-left-300x199.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/image-of-a-spanned-app-showing-a-list-on-the-left-1024x680.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/05\/image-of-a-spanned-app-showing-a-list-on-the-left-768x510.png 768w\" sizes=\"(max-width: 1100px) 100vw, 1100px\" \/>\n<\/p>\n<h2>What happened to TwoPane<\/h2>\n<p>\n  Since other parts of the framework did not depend on TwoPane we were able to make it part of the <a href=\"https:\/\/pub.dev\/packages\/dual_screen\">dual_screen package<\/a>. This way, we can iterate on it quicker and be a bit more opinionated on how it behaves. We have another blog article planned for diving into details about TwoPane and what else you can find in the <code>dual_screen<\/code> package. Stay tuned for more (a good reason to <a href=\"https:\/\/twitter.com\/surfaceduodev\">follow us<\/a>) and in the meantime, you can read the <a href=\"https:\/\/pub.dev\/packages\/dual_screen\">package description<\/a>. \n<\/p>\n<h2>Samples used in this article<\/h2>\n<p>\n  To give the new features a try you can quickly clone our <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk-samples-flutter\">Flutter foldable samples<\/a> and simply run the app. All the screenshots in this article are of our sample apps running in the Surface Duo Emulator. We recommend our <a href=\"https:\/\/docs.microsoft.com\/en-us\/dual-screen\/android\/emulator\/\">Surface Duo emulator<\/a> as a target device since it simulates the <code>hinge<\/code> display feature. Use the foldable devices available in Android Studio to simulate a <code>fold<\/code> display feature.\n<\/p>\n<h2>Call to action<\/h2>\n<ol>\n<li>\n  If you are on the <code>stable<\/code> flutter channel, run <code>flutter upgrade<\/code>.\n<\/li>\n<li>\n  Download the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dual-screen\/android\/emulator\/\">Surface Duo emulator<\/a>.\n<\/li>\n<li>\n  Clone the <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk-samples-flutter\">Flutter foldable samples<\/a> and run it.\n<\/li>\n<li>\n  If you have any questions or would like to tell us about your apps, use the <a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a> or message us on Twitter <a href=\"https:\/\/twitter.com\/surfaceduodev\">@surfaceduodev<\/a>.\n<\/li>\n<li>\n  Finally, please join us for the <a href=\"https:\/\/twitch.tv\/surfaceduodev\">dual screen developer livestream<\/a> this Friday at 13h00 Central European Time and replay at 11am (Pacific time) \u2013 mark it in your calendar! You can also check out the <a href=\"https:\/\/www.youtube.com\/channel\/UClGu9QLtPNz8OdddBfhZXPA\">archives on YouTube<\/a>.\n<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Hello! Flutter 3 was announced last week, and we have good news to share with you. The foldable support that we contributed to Flutter is now released in the latest stable branch. Let us dive into what this means for you. We will first go over the new API that you can use and then [&hellip;]<\/p>\n","protected":false},"author":54297,"featured_media":2480,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[31,693,717,706],"class_list":["post-2473","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-dual-screen-development","tag-flutter","tag-foldable","tag-jetpack-window-manager"],"acf":[],"blog_post_summary":"<p>Hello! Flutter 3 was announced last week, and we have good news to share with you. The foldable support that we contributed to Flutter is now released in the latest stable branch. Let us dive into what this means for you. We will first go over the new API that you can use and then [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2473","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/54297"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=2473"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2473\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/2480"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=2473"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=2473"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=2473"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}