<?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: Isuru</title>
    <description>The latest articles on DEV Community by Isuru (@isurujn).</description>
    <link>https://dev.to/isurujn</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%2F845033%2F3914c30d-2d52-4a4e-a83e-9d2a9ee0897e.jpg</url>
      <title>DEV Community: Isuru</title>
      <link>https://dev.to/isurujn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/isurujn"/>
    <language>en</language>
    <item>
      <title>Unit Testing JSON Functions in Android</title>
      <dc:creator>Isuru</dc:creator>
      <pubDate>Sun, 08 Jan 2023 19:45:14 +0000</pubDate>
      <link>https://dev.to/isurujn/unit-testing-json-functions-in-android-4jh2</link>
      <guid>https://dev.to/isurujn/unit-testing-json-functions-in-android-4jh2</guid>
      <description>&lt;p&gt;Working with JSON is very common in Android apps. Parsing API responses, reading JSON files, creating JSON objects to send to a server are some examples of it. And like the good programmer you are, you likely want to write unit tests to test functions that handle JSON. But in doing so, you could run into a surprising and confusing problem. Let me elaborate.&lt;/p&gt;

&lt;p&gt;Imagine we have a simple function that validates whether the given string is valid JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;isStringValidJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;JSONObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objEx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JSONException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;JSONArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arrEx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JSONException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&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;&lt;a href="https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter" rel="noopener noreferrer"&gt;JUnit 5&lt;/a&gt; is used in these examples. To run unit tests written using JUnit 5, the following needs to be added to the &lt;strong&gt;build.gradle&lt;/strong&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tasks.withType(Test){
    useJUnitPlatform()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we have a unit test that verifies the logic of the above function. When an invalid JSON string is passed, it should return false. Ideally this unit test should pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidatorTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;jsonStringIsInvalid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;string&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lorem ipsum"&lt;/span&gt;
        &lt;span class="nc"&gt;Assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isStringValidJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&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;But when the test is run, it fails!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0bkxnjqfzfbzz0niwlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0bkxnjqfzfbzz0niwlv.png" alt="Unit test failing in Android Studio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What gives?!&lt;/p&gt;

&lt;p&gt;The answer is in the Android Studio’s &lt;a href="http://tools.android.com/tech-docs/unit-testing-support" rel="noopener noreferrer"&gt;unit testing support&lt;/a&gt; docs under the “Method … not mocked.” section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The android.jar file that is used to run unit tests does not contain any actual code — that is provided by the Android system image on real devices. Instead, all methods throw exceptions (by default). This is to make sure your unit tests only test your code and do not depend on any particular behaviour of the Android platform (that you have not explicitly mocked).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This basically means, the &lt;a href="https://mvnrepository.com/artifact/org.json/json" rel="noopener noreferrer"&gt;JSON in Java&lt;/a&gt; library (which contains the classes such as &lt;code&gt;JSONObject&lt;/code&gt; and &lt;code&gt;JSONArray&lt;/code&gt;) is a part of the Android SDK and it’s not available for unit testing by default. To resolve this, we have to add that library explicitly in the &lt;strong&gt;build.gradle&lt;/strong&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    ...
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
    testImplementation 'org.json:json:20220924'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when the test is run again, you should see it passing!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclaridoten9b4w4ucn34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclaridoten9b4w4ucn34.png" alt="Unit test passing in Android Studio"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This is a tricky implementation detail that’s not very obvious and easy to find an answer to. Keep this in mind next time you write unit tests for JSON related functionalities.&lt;/p&gt;




&lt;p&gt;The following video goes over this topic step by step and a bit more details. This written post is more of a summarized reference. Please watch the video it if you don’t fully understand how I arrive at certain points.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/lIfNzWf2m58"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>android</category>
      <category>unittest</category>
      <category>junit</category>
      <category>json</category>
    </item>
    <item>
      <title>Beware of FlutterSecureStorage on iOS</title>
      <dc:creator>Isuru</dc:creator>
      <pubDate>Sun, 20 Nov 2022 09:05:46 +0000</pubDate>
      <link>https://dev.to/isurujn/beware-of-fluttersecurestorage-on-ios-m6e</link>
      <guid>https://dev.to/isurujn/beware-of-fluttersecurestorage-on-ios-m6e</guid>
      <description>&lt;p&gt;This is not a guide on how to use the &lt;a href="https://pub.dev/packages/flutter_secure_storage"&gt;&lt;code&gt;flutter_secure_storage&lt;/code&gt;&lt;/a&gt; package. If you need to learn about that, please check the &lt;a href="https://pub.dev/packages/flutter_secure_storage/example"&gt;documentation&lt;/a&gt;. The purpose of this post is to warn about a certain pitfall with &lt;code&gt;FlutterSecureStorage&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;code&gt;SharedPreferences&lt;/code&gt; (&lt;a href="https://pub.dev/packages/shared_preferences"&gt;shared_preferences&lt;/a&gt;) and &lt;code&gt;FlutterSecureStorage&lt;/code&gt; (&lt;a href="https://pub.dev/packages/flutter_secure_storage"&gt;flutter_secure_storage&lt;/a&gt;) are both lightweight local storage solutions available for Flutter with some notable differences. &lt;code&gt;SharedPreferences&lt;/code&gt; support storing many data types such as string, integer, double, boolean and even string arrays. But in &lt;code&gt;FlutterSecureStorage&lt;/code&gt;, you can only store string values.&lt;/p&gt;

&lt;p&gt;In addition to that, the biggest difference between &lt;code&gt;SharedPreferences&lt;/code&gt; and &lt;code&gt;FlutterSecureStorage&lt;/code&gt; is security. &lt;code&gt;SharedPreferences&lt;/code&gt; is backed by similarly named &lt;a href="https://developer.android.com/reference/android/content/SharedPreferences"&gt;&lt;code&gt;SharedPreferences&lt;/code&gt;&lt;/a&gt; on Android and &lt;a href="https://developer.apple.com/documentation/foundation/nsuserdefaults"&gt;&lt;code&gt;NSUserDefaults&lt;/code&gt;&lt;/a&gt; on iOS. While neither of these local data stores can be easily accessed by a regular app user, a curious person given a little bit of time can dig them up by decompiling the app executable using commonly available tools/methods. &lt;code&gt;SharedPreferences&lt;/code&gt; and &lt;code&gt;FlutterSecureStorage&lt;/code&gt; both store data in a human-readable format.&lt;/p&gt;

&lt;p&gt;Whereas, &lt;code&gt;FlutterSecureStorage&lt;/code&gt; saves data in a much more secure way by using the &lt;a href="https://developer.android.com/training/articles/keystore.html"&gt;Keystore&lt;/a&gt; on Android (&lt;a href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences"&gt;&lt;code&gt;EncryptedSharedPreferences&lt;/code&gt;&lt;/a&gt; in flutter_secure_storage v5.0.0 and above) and &lt;a href="https://developer.apple.com/documentation/security/keychain_services#//apple_ref/doc/uid/TP30000897-CH203-TP1"&gt;Keychain&lt;/a&gt; on iOS. Data saved in these locations are encrypted so they are not human-readable.&lt;/p&gt;

&lt;p&gt;Due to this reason, many developers use &lt;code&gt;FlutterSecureStorage&lt;/code&gt; to store sensitive data. However, there is a caveat when working with &lt;code&gt;FlutterSecureStorage&lt;/code&gt; on iOS.&lt;/p&gt;

&lt;p&gt;As mentioned above, on iOS, FlutterSecureStorage stores data in the Keychain. The Keychain is a system level service. Therefore even if an app is deleted from the phone, the values that app saved in the Keychain do not get removed. To demonstrate why this can be problematic, let us see a possible real-world example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This problem does not exist on Android. When the app is deleted on Android, it removes the values saved in the Keystore/&lt;code&gt;EncryptedSharedPreferences&lt;/code&gt; as well.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s imagine you have an app that has a user account feature. You use &lt;code&gt;FlutterSecureStorage&lt;/code&gt; to save a token (tokens are commonly used to authenticate API calls). At app launch, you check whether a token exists in storage, if it doesn’t, you prompt the user to login. Upon successful login, you save the received token in &lt;code&gt;FlutterSecureStorage&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;authorizeUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Navigate to login screen&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;FlutterSecureStorage&lt;/span&gt; &lt;span class="n"&gt;storage&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;FlutterSecureStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="s"&gt;'token'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Parse login API response and retrieve token&lt;/span&gt;

  &lt;span class="n"&gt;FlutterSecureStorage&lt;/span&gt; &lt;span class="n"&gt;storage&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;FlutterSecureStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="s"&gt;'token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;value:&lt;/span&gt; &lt;span class="s"&gt;'AYR87uYy7jD6jN6RzJeg*G'&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;Now if the user deletes the app from their phone and re-install it, ideally the user should be taken to the login screen to log back in. But because on iOS, Keychain values do not get removed, the &lt;code&gt;getToken()&lt;/code&gt; method will still return a token saved in a previous login session! If there has been a long time gap between the removal of the app and the re-installation, that token could be no longer valid.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, unlike &lt;code&gt;FlutterSecureStorage&lt;/code&gt;, &lt;code&gt;SharedPreferences&lt;/code&gt; do get removed when the app is deleted. This behavior can help with working around the Keychain issue.&lt;/p&gt;




&lt;p&gt;Create a boolean flag called &lt;code&gt;is_first_app_launch&lt;/code&gt; to be saved in &lt;code&gt;SharedPreferences&lt;/code&gt;. This flag determines if the app is launched for the first time (whether it be the very first install or any re-installs). Initially &lt;code&gt;is_first_app_launch&lt;/code&gt; will be &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At the app launch, check if &lt;code&gt;is_first_app_launch&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;, and if it is, delete the &lt;code&gt;FlutterSecureStorage&lt;/code&gt; completely. If the app previously existed and had any values saved in the Keychain, they are removed and the app now has a clean slate.&lt;/p&gt;

&lt;p&gt;Make sure to set the &lt;code&gt;is_first_app_launch&lt;/code&gt; value to &lt;code&gt;false&lt;/code&gt;, so that subsequent launches skip this step. If the app is deleted, it will delete the &lt;code&gt;SharedPreferences&lt;/code&gt; values automatically, thus making &lt;code&gt;is_first_app_launch&lt;/code&gt; value &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_HomeScreenState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeScreen&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;clearKeychainValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;authorizeUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;clearKeychainValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;SharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'is_first_app_launch'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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="n"&gt;FlutterSecureStorage&lt;/span&gt; &lt;span class="n"&gt;storage&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;FlutterSecureStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'is_first_app_launch'&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="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;That is it! It is a little cumbersome solution to accomplish a very simple task. But in Flutter, you have no choice but to work with or around platform limitations/rules.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
    </item>
  </channel>
</rss>
