{"id":361,"date":"2020-05-14T12:25:48","date_gmt":"2020-05-14T19:25:48","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=361"},"modified":"2020-09-11T18:12:15","modified_gmt":"2020-09-12T01:12:15","slug":"get-started-with-flutter-on-surface-duo","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/get-started-with-flutter-on-surface-duo\/","title":{"rendered":"Get Started with Flutter on Surface Duo"},"content":{"rendered":"<p>Hello Flutter developers!<\/p>\n<p>Today we are going to get you started with our Surface Duo SDK for Android so that you can adapt your existing applications for dual-screens and the hinge. The sample code demonstrates how Android developers can add the Surface Duo SDK to a project and then access the screen and hinge APIs.<\/p>\n<p>Our ultimate goal is to develop a single codebase that runs on iOS, Android (targeting single-screen devices), Android (targeting dual-screen devices like the Surface Duo), Windows devices, Web, and anything else Flutter supports \ud83d\ude42 You can see the prototype we\u2019ve built in this video:<\/p>\n<p><div style=\"width: 640px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-361-1\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/Surface-Duo-Flutter-Sample-App.mp4?_=1\" \/><a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/Surface-Duo-Flutter-Sample-App.mp4\">https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/Surface-Duo-Flutter-Sample-App.mp4<\/a><\/video><\/div><\/p>\n<p><em><span style=\"font-size: 10pt;\">Video Flutter application running on multiple devices<\/span><\/em><\/p>\n<p>To get started, first follow the instructions to <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/get-duo-sdk\">download and install<\/a> the Surface Duo dual-screen emulator on a machine that you have also setup for Flutter development. You can follow the <a href=\"https:\/\/flutter.dev\/docs\/get-started\/install\">Flutter Getting Started Guide<\/a> if you are starting from scratch.<\/p>\n<p>Once you have the Surface Duo emulator running, there are three steps to get started with the dual-screen APIs in Flutter:<\/p>\n<ol>\n<li>Add the Android dual-screen SDK to your Flutter project.<\/li>\n<li>Add the Platform Channel and native code required to the Android MainActivity.<\/li>\n<li>Add the Platform Channel code to your Flutter application code.<\/li>\n<\/ol>\n<h2>Step 1. Add the Android dual-screen SDK to your Flutter project<\/h2>\n<p>Begin by creating a Flutter application and make sure to leave kotlin support enabled (default). The sample app that I\u2019m building is the Contoso Movies application. You can see what the \u201cfinished\u201d application will look like in the video above. I say \u201cfinished\u201d because there are some features in the video that are not yet optimized for the dual-screen experience. This leaves us something to work on down the road \ud83d\ude42<\/p>\n<p>Once you have the typical counter application created, let\u2019s add the SDK integration. First, we need to add a file to our Flutter app inside the Android section. Start by creating a folder below <strong>\/android\/app<\/strong>; I named mine <strong>libs<\/strong>.<\/p>\n<h2><img decoding=\"async\" width=\"417\" height=\"342\" class=\"wp-image-363\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati.png 417w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-300x246.png 300w\" sizes=\"(max-width: 417px) 100vw, 417px\" \/><\/h2>\n<p><em><span style=\"font-size: 10pt;\">Figure 1: Create a folder for the SDK library<\/span><\/em><\/p>\n<p>Now that we have a folder created to hold the SDK library, we need to copy the SDK library into this new folder. On Windows this will typically be found at <strong>C:\\Users\\&lt;username&gt;\\SurfaceDuoEmulator\\sdk<\/strong><\/p>\n<p>In the SurfaceDuoEmulator directory you will find a JAR file; copy this file into the folder you created in your application. The last step is to add a dependency on this file to your app level build.gradle. Open the build.gradle found in the android\/app folder. Scroll down to the \u201cdependencies\u201d section and add the following line inside it:<\/p>\n<pre class=\"prettyprint\">compileOnly files(\u2018libs\/com.microsoft.device.display.displaymask.jar\u2019)<\/pre>\n<p>Your dependencies section should now look like this if you started with a new Flutter application:<\/p>\n<p><img decoding=\"async\" width=\"1042\" height=\"251\" class=\"wp-image-364\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1.png 1042w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1-300x72.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1-1024x247.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1-768x185.png 768w\" sizes=\"(max-width: 1042px) 100vw, 1042px\" \/><\/p>\n<p><em><span style=\"font-size: 10pt;\">Figure 2: Modified build.gradle<\/span><\/em><\/p>\n<h2>Step 2. Add the Platform Channel and native code required to the Android MainActivity<\/h2>\n<p>Now that we have the SDK in the app, we need to add the relative native code that we can then call from Flutter to check things like, \u201cis this a dual-screen device\u201d and \u201cis my app spanned across both screens\u201d among other things. We\u2019ll start with these two for now. Open the <strong>MainActivity.kt<\/strong> file found in <strong>\/android\/app\/src\/main\/kotlin<\/strong>.<\/p>\n<p>We\u2019ll first need a couple of imports, one to be able to create a Channel for Flutter to communicate over and a second to import the library. Add these to the imports section of your app:<\/p>\n<pre class=\"prettyprint\">import io.flutter.plugin.common.MethodChannel\r\nimport com.microsoft.device.display.DisplayMask\r\nimport android.hardware.Sensor\r\nimport android.hardware.SensorManager\r\nimport android.hardware.SensorEvent\r\nimport android.hardware.SensorEventListener<\/pre>\n<p>Next, we need to create the channel we\u2019ll use to communicate and ensure that the Native side and Flutter side are in sync. Add the following declaration inside the <strong>MainActivity<\/strong>:<\/p>\n<pre class=\"prettyprint\">private val CHANNEL = \u201cduosdk.microsoft.dev\u201d<\/pre>\n<p>We\u2019ll also need some local variables to hold some information, so let\u2019s add these to the <strong>MainActivity<\/strong> right below our <strong>CHANNEL<\/strong> variable:<\/p>\n<pre class=\"prettyprint\">private val HINGE_ANGLE_SENSOR_NAME = \"Hinge Angle\"\r\nprivate var mSensorsSetup : Boolean = false\r\nprivate var mSensorManager: SensorManager? = null\r\nprivate var mHingeAngleSensor: Sensor? = null\r\nprivate var mSensorListener: SensorEventListener? = null\r\nprivate var mCurrentHingeAngle: Float = 0.0f<\/pre>\n<p>To make things readable, we\u2019ll break our APIs into different functions that we can call from within the Channel\u2019s <strong>methodCallHandler<\/strong>. Add the following functions to <strong>MainActivity<\/strong>:<\/p>\n<pre class=\"prettyprint\">fun isDualScreenDevice(): Boolean {\r\n  val feature = \"com.microsoft.device.display.displaymask\"\r\n  val pm = this.getPackageManager()  if (pm.hasSystemFeature(feature)) {\r\n    return true\r\n  } else {\r\n    return false\r\n  }\r\n}\r\n\r\nfun isAppSpanned(): Boolean {\r\n  var displayMask = DisplayMask.fromResourcesRectApproximation(this)\r\n  var boundings = displayMask.getBoundingRects()\r\n  var first = boundings.get(0)\r\n  var rootView = this.getWindow().getDecorView().getRootView()\r\n  var drawingRect = android.graphics.Rect()\r\n  rootView.getDrawingRect(drawingRect)  if (first.intersect(drawingRect)) {\r\n    return true\r\n  } else {\r\n    return false\r\n  }\r\n}\r\n\r\nprivate fun setupSensors() {\r\n  mSensorManager = getSystemService(SENSOR_SERVICE) as SensorManager?\r\n  val sensorList: List&lt;Sensor&gt; =\r\n    mSensorManager!!.getSensorList(Sensor.TYPE_ALL)\r\n\r\n  for (sensor in sensorList) {\r\n    if (sensor.getName().contains(HINGE_ANGLE_SENSOR_NAME)) {\r\n      mHingeAngleSensor = sensor\r\n      break\r\n    }\r\n  }\r\n\r\n  mSensorListener = object : SensorEventListener {\r\n    override fun onSensorChanged(event: SensorEvent) {\r\n      if (event.sensor === mHingeAngleSensor) {\r\n        mCurrentHingeAngle = event.values.get(0) as Float\r\n      }\r\n    }\r\n\r\n    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {\r\n      \/\/TODO \u2013 Add support later\r\n    }\r\n  }\r\n\r\n  mSensorManager?.registerListener(\r\n    mSensorListener, \r\n    mHingeAngleSensor, \r\n    SensorManager.SENSOR_DELAY_NORMAL)\r\n\r\n  mSensorsSetup = true\r\n}\r\n<\/pre>\n<p>The last piece we need to add to the <strong>MainActivity<\/strong> is the code to handle the calls to this channel. This code will return a failure if we aren\u2019t on a dual-screen device and will handle API calls. I\u2019ve called these <strong>isDualScreenDevice<\/strong> and <strong>isAppSpanned<\/strong> and <strong>getHingeAngle<\/strong>. This code should go right below the GeneratedPluginRegistrant.registerWith(flutterEngine); line:<\/p>\n<pre class=\"prettyprint\">MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {\r\n        call, result -&gt;\r\n            if(!isDualScreenDevice()) {\r\n                result.success(false)\r\n            } else {\r\n                try {\r\n                    if (call.method == \"isDualScreenDevice\") {\r\n                        if (isDualScreenDevice()) {\r\n                            result.success(true)\r\n                        } else {\r\n                            result.success(false)\r\n                        }\r\n                    } else if (call.method == \"isAppSpanned\") {\r\n                        if (isAppSpanned()) {\r\n                            result.success(true)\r\n                        } else {\r\n                            result.success(false)\r\n                        }\r\n                    } else if (call.method == \"getHingeAngle\") {\r\n                        if (!mSensorsSetup) {\r\n                            setupSensors()\r\n                        }\r\n                        result.success(mCurrentHingeAngle)\r\n                    } else {\r\n                        result.notImplemented()\r\n                    }\r\n                } catch(e: Exception){\r\n                    result.success(false)\r\n                }\r\n            }\r\n        }\r\n<\/pre>\n<h2>Step 3. Add the Platform Channel code to your Flutter application code<\/h2>\n<p>Now that we have the native side setup, we can make use of that functionality within our Flutter application code. Let\u2019s modify our\u00a0<strong>main.dart\u00a0<\/strong>file now. Add the following import so we can make the platform specific calls:<\/p>\n<pre class=\"prettyprint\">import 'package:flutter\/services.dart';<\/pre>\n<p>Next, we need to create the platform channel like what we did on the native side in the <strong>MainActivity<\/strong>. First we\u2019ll define a global constant. It\u2019s not ideal, but for our sample this is fine.<\/p>\n<pre class=\"prettyprint\">const platform = const MethodChannel('duosdk.microsoft.dev');<\/pre>\n<p><strong><em>Note<\/em><\/strong><em>: Make sure the string you use here matches what you used in the <\/em><strong><em>MainActivity<\/em><\/strong><em> code.<\/em><\/p>\n<p>In the <strong>MyHomePage<\/strong> stateful widget, lets now add a function that will simply print out if we are on a dual-screen device, if the app is spanned and the current hinge angle.<\/p>\n<pre class=\"prettyprint\">void _updateDualScreenInfo() async {\r\n    final bool isDual = await platform.invokeMethod('isDualScreenDevice');\r\n    final bool isSpanned = await platform.invokeMethod('isAppSpanned');\r\n    final double hingeAngle = await platform.invokeMethod('getHingeAngle');\r\n    \r\n    print('isDualScreenDevice : $isDual');\r\n    print('isAppSpanned : $isSpanned');\r\n    print('hingeAngle : $hingeAngle');\r\n}<\/pre>\n<p>And finally, let\u2019s call that new function from our build method.<\/p>\n<pre class=\"prettyprint\">@override\r\nWidget build(BuildContext context) {\r\n    _updateDualScreenInfo();\r\n\r\n    ... \/\/ The rest of the build method\r\n}<\/pre>\n<p>That\u2019s it! Now we have an application that will work on both a dual-screen and single-screen device. Every time you click the \u2018+\u2019 action button we query the APIs and write the info to the debug console. If you run the application and watch the debug console in your debugger, you will see our output each time you click the button. When you span the app or if you run on a single screen device vs. a dual-screen device and then click the \u2018+\u2019 button in the application, you will see new output, including the hinge angle.<\/p>\n<p><strong><em>Note: <\/em><\/strong><em>The hinge angle is simulated in the emulator by changing the Pressure sensor in the settings<\/em><\/p>\n<p><img decoding=\"async\" width=\"820\" height=\"576\" class=\"wp-image-365\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-computer-description-automatica.png\" alt=\"A screenshot of a computer Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-computer-description-automatica.png 820w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-computer-description-automatica-300x211.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-computer-description-automatica-768x539.png 768w\" sizes=\"(max-width: 820px) 100vw, 820px\" \/><\/p>\n<p><em><span style=\"font-size: 10pt;\">Figure 3: Extended controls UI showing Pressure sensor slider<\/span><\/em><\/p>\n<p>This gif below shows the final project; notice the output showing the dual-screen and spanned state as the \u2018+\u2019 button is clicked.<\/p>\n<p><img decoding=\"async\" width=\"1280\" height=\"916\" class=\"wp-image-366\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut.gif\" alt=\"A screenshot of a computer screen Description automatically generated\" \/><\/p>\n<p><em><span style=\"font-size: 10pt;\">Figure 4: A gif of the final project showing the debug output<\/span><\/em><\/p>\n<p>The steps above have laid the groundwork we need to move into the next stage of building apps for the Surface Duo. Next time we\u2019ll look at how we can use this info to decide how to layout our content and take advantage of both single and dual-screen modes.<\/p>\n<h2>Invitation to Microsoft Build and DroidCon online<\/h2>\n<p>We invite you to join us at <a href=\"https:\/\/www.microsoft.com\/en-us\/build\">Microsoft Build<\/a> on May 19-21, a free 48-hour online developer event. This unique event for developers brings together developers to help the world solve new challenges\u2014sharing knowledge and staying connected is more important than ever. Join your community to learn, connect, and code\u2014to expand your skillset today, and innovate for tomorrow.<\/p>\n<p>Among the many topics, we will feature a dedicated dual-screen session for C# developers by Craig, SK123, where we\u2019ll discuss our Surface Duo SDK, and a session on React Native, SK119.<\/p>\n<p>You can also attend the <a href=\"https:\/\/www.meetup.com\/GoogleGoa\/events\/270606396\/\">GDG India online meetup<\/a>, where <a href=\"https:\/\/twitter.com\/CesarValiente\">Cesar<\/a> from the Surface Duo team will be talking about developing dual-screen experiences. Mark your calendar for Saturday May 16<sup>th<\/sup> from 8:00 PM to 9:30 PM GMT+5:30.<\/p>\n<h2>Resources and feedback<\/h2>\n<p>The completed sample project can be found in <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk-samples-flutter\">our GitHub<\/a>.<\/p>\n<p>We would love to hear from you about your experiences using the Surface Duo SDK, emulator, and your thoughts on how you can utilize these in your Flutter applications.<\/p>\n<p>Please reach out using our <a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a> or message me on <a href=\"https:\/\/twitter.com\/johnwiese\">Twitter<\/a> or <a href=\"https:\/\/github.com\/jwiese-ms\">GitHub<\/a>.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello Flutter developers! Today we are going to get you started with our Surface Duo SDK for Android so that you can adapt your existing applications for dual-screens and the hinge. The sample code demonstrates how Android developers can add the Surface Duo SDK to a project and then access the screen and hinge APIs. [&hellip;]<\/p>\n","protected":false},"author":28023,"featured_media":362,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[693],"class_list":["post-361","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-flutter"],"acf":[],"blog_post_summary":"<p>Hello Flutter developers! Today we are going to get you started with our Surface Duo SDK for Android so that you can adapt your existing applications for dual-screens and the hinge. The sample code demonstrates how Android developers can add the Surface Duo SDK to a project and then access the screen and hinge APIs. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/361","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\/28023"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=361"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/361\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/362"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=361"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=361"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=361"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}