<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sven Hennessen</title>
    <description>The latest articles on DEV Community by Sven Hennessen (@svenhennessen).</description>
    <link>https://dev.to/svenhennessen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F350066%2Fef1ab67b-ccc8-4ab2-9ca2-ca9511820658.jpg</url>
      <title>DEV Community: Sven Hennessen</title>
      <link>https://dev.to/svenhennessen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/svenhennessen"/>
    <language>en</language>
    <item>
      <title>A first look at Flutter</title>
      <dc:creator>Sven Hennessen</dc:creator>
      <pubDate>Fri, 17 Apr 2020 06:35:28 +0000</pubDate>
      <link>https://dev.to/svenhennessen/a-first-look-at-flutter-4ec9</link>
      <guid>https://dev.to/svenhennessen/a-first-look-at-flutter-4ec9</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://hennessen.net/blog/posts/A-first-look-at-flutter/"&gt;hennessen.net&lt;/a&gt; on December 22nd, 2019&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-platform app development (read "iOS and Android from the same code-base") is the wet dream of many teams and companies for a long time. I've spent quite some time with projects based on HTML5 + Cordova or Xamarin.iOS / .Android (not Forms), so I have a bit of experience in the area and try to keep up with the trends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flutter.dev"&gt;Flutter&lt;/a&gt; is in everybody's mouth these days when talking cross-platform app development. Basically, Flutter is Google's approach to the cross-platform topic, based on their own programming language, &lt;a href="https://dart.dev"&gt;Dart&lt;/a&gt; with a declarative UI SDK. I won't repeat the details of the language or SDK here or its features here. Head over to &lt;a href="https://flutter.dev"&gt;flutter.dev&lt;/a&gt; if you're interested in more.&lt;/p&gt;

&lt;p&gt;Apple introduced their own, declarative UI development framework, called &lt;a href="https://developer.apple.com/xcode/swiftui/"&gt;SwiftUI&lt;/a&gt;, when releasing iOS 13. Thinking declarative UI code often is superior to other approaches (see &lt;a href="https://docs.microsoft.com/en-us/dotnet/desktop-wpf/fundamentals/xaml"&gt;XAML for WPF&lt;/a&gt;, &lt;a href="https://docs.microsoft.com/en-us/dotnet/desktop-wpf/fundamentals/xaml"&gt;XAML for Xamarin.Forms&lt;/a&gt; or Google's argument for the approach in the &lt;a href="https://flutter.dev/docs/get-started/flutter-for/declarative"&gt;Flutter docs&lt;/a&gt;, ), I gave it quick shot, but became frustrated quickly for all the bugs in the first releases. Hence, it was time to give Flutter a shot for my next iOS side project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This post shows the key aspects of that app and how I implemented them.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The App - Spiramentum 2
&lt;/h2&gt;

&lt;p&gt;The app is a very simple meditation app. The main features I wanted to have are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ability to configure a timeframe for how much time I want to spend&lt;/li&gt;
&lt;li&gt;A timer display, for checking the time that has past&lt;/li&gt;
&lt;li&gt;A push notification at the end of the time&lt;/li&gt;
&lt;li&gt;Store the time spend in Apple's HealthKit as mindful time spent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Besides playing around with another SDK, this app addresses a particular problem for me: All existing mindfulness or meditations apps in the app store, which I've checked, require a registration/login or event subscription.&lt;/p&gt;

&lt;p&gt;One question you might have is: Why is the app called "Spiramentum 2"? Have a look at the app project &lt;a href="https://github.com/sventropy/spiramentum2#where-can-i-find-spiramentum-1"&gt;README&lt;/a&gt; for the answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Widgets
&lt;/h3&gt;

&lt;p&gt;To follow the description in this blog, it is important to understand an important thing in Flutter: Everything is a "widget". With that I mean your screens/pages, controls, layouts, spaces between controls, paddings, etc. Everything inherits from the &lt;code&gt;Widget&lt;/code&gt; base class and shares the same life cycle. Flutter provides a &lt;a href="https://flutter.dev/docs/development/ui/widgets/animation"&gt;variety of widgets&lt;/a&gt; as part of the SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  The main screen
&lt;/h3&gt;

&lt;p&gt;The single screen of the app is implemented as a stateful flutter widget called &lt;code&gt;MyHomePage&lt;/code&gt; and the corresponding &lt;code&gt;MyHomePageState&lt;/code&gt;. We need state to maintain stuff, like the selected time to run the timer for or its start timestamp. In addition, other technically required components like a controller for animations are maintained here as well.&lt;/p&gt;

&lt;p&gt;Everything is setup in the &lt;code&gt;void initState()&lt;/code&gt; method and cleaned up, if necessary in the &lt;code&gt;void dispose()&lt;/code&gt; method, which are both inherited from &lt;code&gt;State&amp;lt;MyHomePage&amp;gt;&lt;/code&gt;. Another inherited method is &lt;code&gt;Widget build(BuildContext context)&lt;/code&gt;, which controls what is rendered to the UI. Here is the structure for our UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;titleText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Padding&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="c1"&gt;// A title label to tell the user what to do&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;durationPicker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CupertinoPicker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// A picker to the allow selection for different timeframees&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;pickerTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SizeTransition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// A transition wrapper for the picker&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;timerTextTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SizeTransition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="c1"&gt;// A label for the timer, wrapped in a transition&lt;/span&gt;
        &lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;startStopButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CupertinoButton&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// Button to start/stop the timer&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Finally the page scaffold putting everything together&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CupertinoPageScaffold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;[&lt;/span&gt;
            &lt;span class="n"&gt;Spacer&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;titleText&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Spacer&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;pickerTransition&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timerTextTransition&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Spacer&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;startStopButton&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Spacer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
          &lt;span class="o"&gt;],&lt;/span&gt;
        &lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since the app is targeted for iOS only, only Flutter's &lt;code&gt;Cuptertino*&lt;/code&gt; controls (see the &lt;a href="https://flutter.dev/docs/development/ui/widgets/cupertino"&gt;flutter docs&lt;/a&gt;) are being used. &lt;/p&gt;

&lt;p&gt;The two &lt;code&gt;SizeTransition&lt;/code&gt;s are used to switch between the timer and the picker, depending on the state of the timer. I will come back to that later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interaction and State
&lt;/h3&gt;

&lt;p&gt;Interaction with the UI, background processing, what is executed on the UI thread and what is not have been problematic areas in past SDKs and led to errors in projects. Hence I was curious to see how Flutter solves this and luckily the answer is "very simplistic".&lt;/p&gt;

&lt;p&gt;Explicit actions like our button to start and stop the timer, simply have a &lt;a href="https://flutter.dev/docs/development/ui/widgets-intro#handling-gestures"&gt;callback action&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;onPressed&lt;/span&gt;&lt;span class="o"&gt;():&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// do stuff&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Updates to the UI are automatically triggered, each time state on a stateful &lt;code&gt;Widget&lt;/code&gt;. State updates are triggered through the &lt;code&gt;void setState(VoidCallback fn)&lt;/code&gt; method on the widget, which might sound familiar if you've worked with &lt;a href="https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly"&gt;React&lt;/a&gt; before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;_myAction&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Update UI&lt;/span&gt;
    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;myStateProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By the way, the underscore before the method declaration marks the method as &lt;em&gt;private&lt;/em&gt; in Dart as you would know it from other languages like C# or Java.&lt;/p&gt;

&lt;p&gt;So far so good, but what about long running tasks which should not block my UI? Well, Dart supports &lt;a href="https://dart.dev/codelabs/async-await"&gt;async/await&lt;/a&gt; like other languages/frameworks do today. Async methods return their value wrapped into the &lt;code&gt;Future&lt;/code&gt; type, which corresponds to the &lt;code&gt;Task&lt;/code&gt; in &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=netframework-4.8"&gt;.NET&lt;/a&gt; or a &lt;code&gt;Promise&lt;/code&gt; in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think, this is basically everything you need without having to worry too much about threading issues or similar. Oh and for our app's timer, there is the &lt;code&gt;Timer&lt;/code&gt; class from the &lt;code&gt;dart:async&lt;/code&gt; library, which does the heavy lifting (scheduling and threading) for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Animation
&lt;/h3&gt;

&lt;p&gt;One thing Flutter surprised me with, was the simple animation API and &lt;a href="https://flutter.dev/docs/development/ui/widgets/animation"&gt;diverse standard widgets which already allow all sorts of animations&lt;/a&gt;. Our simple app uses the &lt;code&gt;SizeTransformation&lt;/code&gt; widget, which can be easily animated using a &lt;code&gt;AnimationController&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight dart"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// do this in initState&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_animationController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AnimationController&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;duration:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nl"&gt;vsync:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_pickerAnimation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="n"&gt;Tween&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nl"&gt;begin:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;end:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_animationController&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_counterLabelAnimation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;Tween&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nl"&gt;begin:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;end:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_animationController&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// and this in build&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;pickerTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SizeTransition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;sizeFactor:&lt;/span&gt; &lt;span class="n"&gt;_pickerAnimation&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;timerTextTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SizeTransition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;sizeFactor:&lt;/span&gt; &lt;span class="n"&gt;_counterLabelAnimation&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Having the two transitions rendered right above each other, it is easy to hide one and show the other with &lt;code&gt;_animationController.forward()&lt;/code&gt; and &lt;code&gt;_animationController.reverse()&lt;/code&gt;. This is how the picker is replaced by the label while the timer is running and vice versa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Native Plugin
&lt;/h3&gt;

&lt;p&gt;To achieve the functionality I described in the beginning, we need direct access to the HealthKit and &lt;code&gt;UNUserNotification&lt;/code&gt; APIs of iOS. Well, there is no native API in Dart/Flutter, hence the answer was to provide a &lt;a href="https://flutter.dev/docs/development/platform-integration/platform-channels?tab=ios-channel-swift-tab#step-4-add-an-ios-platform-specific-implementation"&gt;native iOS plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The API is straight-forward and string-based similar to native plugin APIs, e.g. in Cordova.&lt;/p&gt;

&lt;p&gt;These are the two classes provided on the Flutter side to wrap the API calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/services.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MethodChannel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'de.sventropy/notification-service'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;showNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'showNotification'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Triggered notification with text &lt;/span&gt;&lt;span class="si"&gt;$message&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MindfulStore&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MethodChannel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'de.sventropy/mindfulness-minutes'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;storeMindfulMinutes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'storeMindfulMinutes'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$minutes&lt;/span&gt;&lt;span class="s"&gt; stored"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the iOS Runner project, this is the code provided to receive the plugin call and call the native API&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;didFinishLaunchingWithOptions&lt;/span&gt; &lt;span class="nv"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LaunchOptionsKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;flutterViewController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rootViewController&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;FlutterViewController&lt;/span&gt; &lt;span class="c1"&gt;// provided via FlutterAppDelegate base class&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;storeMindfulMinutesChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FlutterMethodChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"de.sventropy/mindfulness-minutes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;binaryMessengerflutterViewController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binaryMessenger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;showNotificationChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FlutterMethodChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"de.sventropy/notification-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;binaryMessengerflutterViewController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binaryMessenger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// ensure permissions for notifications and HealthKit&lt;/span&gt;

    &lt;span class="n"&gt;storeMindfulMinutesChannel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setMethodCallHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;FlutterMethodCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="kt"&gt;FlutterResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"storeMindfulMinutes"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FlutterMethodNotImplemented&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;Int32&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;storeMindfulMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;showNotificationChannel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setMethodCallHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;FlutterMethodCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="kt"&gt;FlutterResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"showNotification"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FlutterMethodNotImplemented&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// register plugins with platform&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;storeMindfulMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="kt"&gt;FlutterResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;endDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;startDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;byAdding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;endDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;mindfulSessionTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;HKCategorySample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HKObjectType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;categoryType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mindfulSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HKCategoryValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notApplicable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;startDate&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;endDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;healthStore&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mindfulSessionTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;withCompletion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FlutterError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"UNAVAILABLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Error storing mindfulness time"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="nv"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;describing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localizedDescription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="kt"&gt;FlutterResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UNMutableNotificationContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;badge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UNNotificationSound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UNTimeIntervalNotificationTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;timeInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                        &lt;span class="nv"&gt;repeats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;requestIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"de.sventropy.spiramentum2.notification"&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UNNotificationRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requestIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;UNUserNotificationCenter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;withCompletionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FlutterError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One thing to point out: Both plugins are "write-only" since only data is sent to the native platform but none is returned. However, it is important to call the &lt;code&gt;result(...)&lt;/code&gt; method anyway, otherwise the asynchronous plugin call will not return.&lt;/p&gt;

&lt;p&gt;So basically, this concept is nothing new, works as intended, but will never have the flexibility of a native app with all these APIs at hand without a plugin wrapper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community
&lt;/h2&gt;

&lt;p&gt;What I liked looking around for resources on Flutter was the community work around the SDK. I'd like to point out three examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The "Widget of the Week" videos on the &lt;a href="https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw"&gt;Flutter YouTube channel&lt;/a&gt;, where Google presents an existing or new Widget per week, by example&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/Solido/awesome-flutter"&gt;Awesome Flutter&lt;/a&gt; community project on GitHub with a collection of other projects, concepts and patterns&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/esskar/vscode-flutter-i18n-json"&gt;Flutter i18n plugin&lt;/a&gt; for VSCode built by a friend of mine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My gut feeling is, that there is simply more going on around after a short live of Flutter of ~2.5 years compared to other SDKs/Frameworks. I've witnessed the rise and life of Xamarin from the first line, which at least felt a little less noisy than the Flutter hype.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I like Flutter. Most of all for its accessible declarative API, simple concepts and its easy to learn language, Dart. If you've worked with Swift, you'll know that that not all languages are like that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2019-12-23&lt;/strong&gt;: This conclusion would not be complete without stating that Flutter, similar to other cross-platform approaches with their own runtime and API can never be as performing and up-to-date (in terms of new features) as the native SDK is. It still is fun and easy to use ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯.&lt;/p&gt;

&lt;p&gt;That's it. The entire &lt;a href="https://github.com/sventropy/spiramentum2"&gt;project is available on GitHub&lt;/a&gt;, so feel happy to browse the code.&lt;/p&gt;

&lt;p&gt;Feedback? I am happy to hear it &lt;a href="https://twitter.com/svenhennessen"&gt;@svenhennessen&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>flutter</category>
      <category>crossplattform</category>
      <category>lessonslearned</category>
    </item>
    <item>
      <title>On working remotely</title>
      <dc:creator>Sven Hennessen</dc:creator>
      <pubDate>Fri, 17 Apr 2020 06:31:54 +0000</pubDate>
      <link>https://dev.to/svenhennessen/on-working-remotely-2h6o</link>
      <guid>https://dev.to/svenhennessen/on-working-remotely-2h6o</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://hennessen.net/blog/posts/on-working-remotely/"&gt;hennessen.net&lt;/a&gt; on November 15th, 2019&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A topic I keep coming back to, is remote collaboration in software development. Why?&lt;br&gt;
Well, I had the opportunity to work in multiple project setups with different setups with regards to the team members locations. Basically it spanned from single location office, over multiple locations in one time zone up to offices and remote colleagues across three time zones. The main reason is, that I myself like working from home quite a lot and will be doing so more in the future. Hence, I started looking around for material on the topic and best-practices for remote teams quite a while back.&lt;/p&gt;

&lt;p&gt;This blog sums up, what I've experienced and found in several sources.&lt;/p&gt;

&lt;p&gt;ℹ️ For those of you understanding German and being more interested in listening than reading, I've discussed this topic with my friend Michael in the &lt;a href="http://entropisches-duett.de/?podcast=markdownsucht"&gt;first episode our Podcast &lt;em&gt;Entropisches Duett&lt;/em&gt;&lt;/a&gt;. However, there might be more in here, since &lt;a href="https://twitter.com/mi_schm"&gt;@mi_schm&lt;/a&gt; recommended some more material on the topic after the show. Thanks for that!&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Re-Mode?
&lt;/h2&gt;

&lt;p&gt;Collaboration in software project can have many flavors, spanning from &lt;em&gt;all in one office&lt;/em&gt; (not remote) to &lt;em&gt;all somewhere, no shared location&lt;/em&gt; (full remote). The usual model I've seen so far in the corporate area, was a larger part of the team being on the same location (&lt;a href="https://en.wikipedia.org/wiki/Conway%27s_law"&gt;obviously&lt;/a&gt;). This gets more complex from a team setup perspective when one or more locations are added, which I will keep referring to as a &lt;em&gt;mixed&lt;/em&gt; setup.&lt;/p&gt;

&lt;p&gt;Looking around for software companies actively, publicly talking about their remote collaboration models, I found multiple examples, like &lt;a href="https://zapier.com/learn/remote-work"&gt;Zapier&lt;/a&gt;, &lt;a href="https://www.innoq.com/en/podcast/061-remote-mob-programming/"&gt;InnoQ&lt;/a&gt;, &lt;a href="https://doist.com/blog/asynchronous-communication/"&gt;Doist&lt;/a&gt;, and more which all add their personal experience to the topic. One finding up-front: &lt;a href="https://www.atlassian.com/agile/teams/remote-teams"&gt;Atlassian&lt;/a&gt; was the only example I found, actively talking about their &lt;em&gt;mixed&lt;/em&gt; setup with feature teams on one location.&lt;/p&gt;

&lt;p&gt;For me, the topic breaks down in these areas:&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Mode
&lt;/h2&gt;

&lt;p&gt;Besides the setup of teams from a geographical perspective, the working mode in which the team operates plays a role to the success of a remote team as well. Basically, in this area many of the articles emphasized the importance of the agile best practices many know for years, but probably not so many teams execute. &lt;br&gt;&lt;br&gt;
Examples are &lt;em&gt;Ship things once they are done, not when a deadline tells you&lt;/em&gt; or &lt;em&gt;Everybody does support on a rotation basis&lt;/em&gt; to keep workload and knowledge distributed across the team.&lt;/p&gt;

&lt;p&gt;While doing so, the organizational structure in the teams can have different formats peculiarities: Developers working on features by them selves with reviews by others, compared to pair programming are mentioned.&lt;br&gt;&lt;br&gt;
A while back, I came across an approach entirely new to me: &lt;a href="https://www.remotemobprogramming.org"&gt;&lt;em&gt;Remote Mob Programming&lt;/em&gt;&lt;/a&gt;, which is based on a team of four members and non-stop rotation of one member typing and the other three discussing what is to type. The company behind it InnoQ also as a &lt;a href="https://www.innoq.com/en/podcast/061-remote-mob-programming/"&gt;podcast with an episode dedicated to this topic&lt;/a&gt; which I can highly recommend, while unfortunately having no experience in this area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Team Member Profile
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://zapier.com/learn/remote-work"&gt;Zapier&lt;/a&gt; talks extensively about the profile people should fulfill to work in a remote environment. They should be of &lt;em&gt;time management&lt;/em&gt; and self-organizing of course. But they also have to be what they call &lt;em&gt;tech-savvy&lt;/em&gt; and &lt;em&gt;good writers&lt;/em&gt;, as their only direct interaction partner throughout their workday will be a computer, where they communicate in written form. Potential team members need to be ok with working in a non-social workplace, like their home office most of the time.&lt;br&gt;&lt;br&gt;
Manager's should judge employee's on the outcome of their work and not the hours they are online; they should hire trustworthy people and then (and I like that) &lt;em&gt;also trust them&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication
&lt;/h2&gt;

&lt;p&gt;Communication is the area, where in my experience things can get complicated easily and hence I deem it to be the most important area.&lt;/p&gt;

&lt;p&gt;In remote teams, most commination will happen in text form, usually in a collaboration tool. This requires &lt;strong&gt;all&lt;/strong&gt; participants to change their behavior compared to an office environment:&lt;/p&gt;

&lt;p&gt;Such communication is happening &lt;a href="https://en.wikipedia.org/wiki/Asynchronous_communication"&gt;asynchronously&lt;/a&gt; (and it should). This can be difficult, as &lt;a href="https://www.groovehq.com/blog/being-a-remote-team"&gt;GrooveHQ&lt;/a&gt;, &lt;a href="https://x-team.com/remote-team-guide/communication"&gt;X-Team&lt;/a&gt; and &lt;a href="https://doist.com/blog/asynchronous-communication/"&gt;Doist&lt;/a&gt; point out, but it is the basis for team members being able to plan their day in advance and be focused when working on a task without interrupts. The planning is getting more important and hence a necessity. Immediate answers to questions just raised, can no longer be the expectation. Synchronous communication can (and as well should) happen in certain rare occasions, for instance for emergencies or team-alignment (like your daily standup). However, async should be the default.&lt;/p&gt;

&lt;p&gt;For asynchronous communication, multiple tools are already available and frequently used out there, the most famous example probably being Slack. And yes, email can be used asynchronously, however the usual expectation in projects is synchronous feedback and email has a major other flaw: No transparency to whom is not on the recipients list. Hence whatever the tool, it has to ensure, that all communication is visible to all others and can be found again later. The last point is something that I haven't read consciously in any of the posts I am linking. Thinking of new team members boarding on once a project is one year down the road, tells me that this is highly important.&lt;br&gt;&lt;br&gt;
Synchronous communication can be easily achieved face-to-face, using Zoom, Google Hangout or other video conferencing solutions.&lt;/p&gt;

&lt;p&gt;Besides the planning part for your day or the project becoming more important, asynchronous communication adds other aspects of pro-activeness required by all team members. Without transparency across the team, misunderstandings can happen and hence work even not done, or done twice. Transparency means, that all team members communicate transparently on the status of their work, problems, future plans, etc. Some examples I'd like to point out, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Meeting Notes, per default, visible to any-one (including management meetings)&lt;/li&gt;
&lt;li&gt;Clear, joint priorities for the entire team&lt;/li&gt;
&lt;li&gt;Developers stating their plan for the day each morning in Slack&lt;/li&gt;
&lt;li&gt;Developers sharing their most important lesson for the day at the end of it ("Today I learned")&lt;/li&gt;
&lt;li&gt;All team members following up on the information (i.e. "reading") relevant for their work&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Social Interaction
&lt;/h2&gt;

&lt;p&gt;Apart from communication on work itself, the social aspect cannot be disregarded, even in full-remote teams. This starts with everyone's attitude towards communicating with others in text form. Sometimes, chat messages, questions or even decisions might catch us on the wrong foot. There is a known phenomenon, where people would assume other’s unexpected behavior as an display of bad or at least confrontational attitude instead of simply not being up to date or not knowing: &lt;a href="https://en.wikipedia.org/wiki/Hanlon's_razor"&gt;Hanlon’s Razor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Synchronous communication, as mentioned before, still has its place in even entirely remote working teams. There are work-driven reasons, like the daily standup, which simply works better as a direct discussion. Bonding between team-members, building team spirit and getting to know each other beyond work are important goals, which direct conversation and face-to-face meetings help to achieve. Formats like regular 1-on-1's between manager and employee, monthly team-meetings (in person or via video conference) or yearly gatherings in-person are only some of the formats used. Zapier for instance &lt;a href="https://zapier.com/learn/remote-work/how-build-culture-remote-team/"&gt;creates pairs of employees randomly&lt;/a&gt; every couple of weeks and encourages them to have a discussion and get to know each other.&lt;/p&gt;

&lt;p&gt;I've briefly touched the social impact, a completely remote working environment can have on people. At &lt;a href="https://doist.com/blog/mental-health-and-remote-work/"&gt;Doist&lt;/a&gt;, they are aware of the potential issues, which can arise in an isolated space like your own four walls and hence promote actively awareness and measures against it, to counter these effects on their employees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side effects
&lt;/h2&gt;

&lt;p&gt;Random work has its challenges (as seen above), however there are certain advantages mentioned by almost all articles I've read. Less commuting means more time throughout the day and being home earlier. Seeing remote work as the default compared to onsite workshops spares everybody airplane flights. For company owners, remote teams mean no cost for office space and furniture.&lt;/p&gt;

&lt;p&gt;In 2019, I cannot finish on the topic without mentioning, that the time and resources reduced, directly result in a reduction of the the carbon footprint and hence helps our planet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I like working remotely and I like it more than working in an office. I am writing this, while never working longer remotely than two or three weeks in a row. However, reading on the matter makes me think that I will enjoy it much longer, while knowing that frustration can arise when stuff like proactive communication is not lived throughout the team or company.&lt;/p&gt;

&lt;p&gt;In case you have any feedback, I would be happy to hear it &lt;a href="https://twitter.com/svenhennessen"&gt;@svenhennessen&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;p&gt;Have fun working remotely, cheers!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/agile/teams/remote-teams"&gt;Atlassian: Remote Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zapier.com/learn/remote-work"&gt;Zapier: Remote Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x-team.com/remote-team-guide/communication"&gt;X-Team: Communication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.groovehq.com/blog/being-a-remote-team"&gt;GrooveHQ: Being a remote team&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.remotemobprogramming.org"&gt;InnoQ: Remote Mob Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.innoq.com/en/podcast/061-remote-mob-programming/"&gt;InnoQ Podcast: Remote Mob Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doist.com/blog/asynchronous-communication/"&gt;Doist: Asynchronous Communication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://about.gitlab.com/blog/2015/04/08/the-remote-manifesto/"&gt;GitLab: The Remote Manifesto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doist.com/blog/mental-health-and-remote-work/"&gt;Doist: Mental Health and Remote Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Conway%27s_law"&gt;Wikipedia: Conway's law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Asynchronous_communication"&gt;Wikipedia: Asynchronous Communication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Hanlon's_razor"&gt;Wikipedia: Hanlon's Razor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zapier.com/learn/remote-work/how-build-culture-remote-team/"&gt;Zapier: Buddies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>collaboration</category>
      <category>remote</category>
      <category>devlive</category>
    </item>
  </channel>
</rss>
