<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ application - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ application - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 20:25:10 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/application/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ In App Update - How to Notify Users of App Updates in Flutter ]]>
                </title>
                <description>
                    <![CDATA[ When you roll out a new version of your application, you want your users to know about it. Whether you fixed a critical bug, added a new feature, or the application just runs smoother or faster – they need to know.  As application developers, we want... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/in-app-update-the-flutter-way/</link>
                <guid isPermaLink="false">66ba5026f8a814ef73b78bc4</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tomer ]]>
                </dc:creator>
                <pubDate>Fri, 18 Nov 2022 21:50:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/clarisse-meyer-xXiKQ2AavlY-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you roll out a new version of your application, you want your users to know about it. Whether you fixed a critical bug, added a new feature, or the application just runs smoother or faster – they need to know. </p>
<p>As application developers, we want all of our users to use the most recent version of our application.</p>
<p>But how can we make sure that users are aware of a new version of our application?</p>
<p>The answer to that question is quite simple: Why not inform them when a new version of the application is out? </p>
<p>You can do this in various ways:</p>
<ul>
<li>Use a push notification</li>
<li>Let them know when the application is launched</li>
</ul>
<p>We won’t be dealing with push notifications in this article. Instead we'll focus on showing how you can (using a package or two) show a dialog to your users informing them that a new version of the application is out and how to deal with the update.</p>
<h2 id="heading-wait-isnt-this-already-included">Wait, Isn’t This Already Included?</h2>
<p>You would think that this kind of functionality should already be included in the modern mobile OS systems. And you would be right – but only for Android. </p>
<p>iOS does not (currently) give developers the ability to see if there is a new version of the application and notify users about it. In Android, you have the <a target="_blank" href="https://developer.android.com/guide/playcore/in-app-updates/kotlin-java">In-App Update library</a> that is part of the Google Play libraries.</p>
<p>Because of this, and because Flutter supports both platforms, I am going to go over two prominent packages that help you handle version updates to your application:</p>
<ol>
<li><a target="_blank" href="https://pub.dev/packages/upgrader">Upgrader</a></li>
<li><a target="_blank" href="https://pub.dev/packages/in_app_update">In App Update</a></li>
</ol>
<p>Both can get you the desired result, but they vary widely in how they do it.</p>
<p>Before we start, <strong>it is crucial to understand that you must have a version of your application that was installed directly from the Google Play store</strong>. This is required since both packages rely on Google Play services and its ability to verify the owner of the application. </p>
<p>If you fail to do so, you will see the following error when trying to use one of the packages:</p>
<blockquote>
<p>_Install Error(-10): The app is not owned by any user on this device. An app is “owned” if it has been acquired from Play. (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_APP_NOT<em>OWNED)</em></p>
</blockquote>
<h2 id="heading-how-to-use-the-in-app-update-package">How to Use The In App Update Package</h2>
<p>Right off the bat you should know that this package will only work on Android. This is because it relies on the in app update library for its inner workings. </p>
<p>This package is basically a wrapper for the Android library. Below are its exposed API methods:</p>
<ul>
<li><code>Future&lt;AppUpdateInfo&gt; checkForUpdate()</code>: Checks if there's an update available</li>
<li><code>Future&lt;AppUpdateResult&gt; performImmediateUpdate()</code>: Performs an immediate update (full-screen)</li>
<li><code>Future&lt;AppUpdateResult&gt; startFlexibleUpdate()</code>: Starts a flexible update (background download)</li>
<li><code>Future&lt;void&gt; completeFlexibleUpdate()</code>: Actually installs an available flexible update</li>
</ul>
<p>✋ If you want to read more about the differences between an immediate update or a flexible update, head over <a target="_blank" href="https://developer.android.com/guide/playcore/in-app-updates">here</a>.</p>
<h3 id="heading-how-to-set-up-the-package">How to Set Up the Package</h3>
<p>First, add the package to your pubspec.yaml file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">in_app_update:</span> <span class="hljs-string">^3.0.0</span>
</code></pre>
<p>Then perform pub get.</p>
<p>Inside your application, where you intend to perform the logic to handle in app updates, add the following import:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:in_app_update/in_app_update.dart'</span>;
</code></pre>
<p>We will first need to add logic that checks if our application has an update. To do that, we will use the <strong>checkForUpdate</strong> method. Its return value is a Future which contains information about the availability and progress of an app update. </p>
<p>We can check if an update is available by using the <a target="_blank" href="https://developer.android.com/reference/com/google/android/play/core/install/model/UpdateAvailability">updateAvailability</a> property. If an update is available, it will have the value of <strong>UPDATE_AVAILABLE</strong>. So, your method may look like this:</p>
<pre><code class="lang-dart">InAppUpdate.checkForUpdate().then((updateInfo) {
  <span class="hljs-keyword">if</span> (updateInfo.updateAvailability == UpdateAvailability.updateAvailable) {
      <span class="hljs-comment">//Logic to perform an update </span>
  }
});
</code></pre>
<p>Next, we need to decide which kind of update we want to trigger – either a flexible or an immediate update. </p>
<p>Going for an immediate update should be reserved for an application update that is critical for your users. That may mean a version that fixes a critical bug or offers a new feature. </p>
<p>To start an immediate update, we can use the <strong>performImmediateUpdate</strong> method. This method returns a <a target="_blank" href="https://developer.android.com/reference/com/google/android/play/core/ktx/AppUpdateResult">AppUpdateResult</a> enum that lets you know if the update was successful or not. </p>
<p>Before calling this method we need to check if we are allowed to run an immediate update. We do that by accessing the <strong>immediateUpdateAllowed</strong> flag on the AppUpdateInfo object.</p>
<p>If we want to trigger a flexible update, we use the <strong>startFleixbleUpdate</strong> method. This runs in the background and similar to the immediate update method. It also returns an AppUpdateResult enum. </p>
<p>If in this scenario the update was successful, we need to call the <strong>completeFlexibleUpdate</strong> method to install the update to our application.</p>
<p>So, if we look at the code snippet above and add the logic for the different types of updates, it will look like this:</p>
<pre><code class="lang-dart">InAppUpdate.checkForUpdate().then((updateInfo) {
  <span class="hljs-keyword">if</span> (updateInfo.updateAvailability == UpdateAvailability.updateAvailable) {
      <span class="hljs-keyword">if</span> (updateInfo.immediateUpdateAllowed) {
          <span class="hljs-comment">// Perform immediate update</span>
          InAppUpdate.performImmediateUpdate().then((appUpdateResult) {
              <span class="hljs-keyword">if</span> (appUpdateResult == AppUpdateResult.success) {
                <span class="hljs-comment">//App Update successful</span>
              }
          });
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (updateInfo.flexibleUpdateAllowed) {
        <span class="hljs-comment">//Perform flexible update</span>
        InAppUpdate.startFlexibleUpdate().then((appUpdateResult) {
              <span class="hljs-keyword">if</span> (appUpdateResult == AppUpdateResult.success) {
                <span class="hljs-comment">//App Update successful</span>
                InAppUpdate.completeFlexibleUpdate();
              }
          });
      }
  }
});
</code></pre>
<h2 id="heading-how-to-use-the-upgrader-package">How to Use The Upgrader Package</h2>
<p>As opposed to the first option, this one offers a solution for both iOS and Android. It relies on gathering data from the store and checking it against the current data from the application itself. </p>
<p>Instead of having an API to query the data, this package has widgets that perform the logic under the hood.</p>
<h3 id="heading-how-to-set-up-the-package-1">How to Set Up the Package</h3>
<p>First, add the package to your pubspec.yaml file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">upgrader:</span> <span class="hljs-string">^5.0.0</span>
</code></pre>
<p>Then perform pub get.</p>
<p>Inside your application, where you intend to perform the logic to handle in app updates, add the following import:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:upgrader/upgrader.dart'</span>;
</code></pre>
<p>The main difference between these two options is just a UI one, so pick the one that fits the most for you. </p>
<p>To integrate this package, you will need to wrap your body widget with either <strong>UpgradeAlert</strong> or <strong>UpgradeCard</strong>. Below is an example:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
      <span class="hljs-keyword">return</span> MaterialApp(
        title: applicationName,
        home: UpgradeAlert(                  <span class="hljs-comment">/// <span class="markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">------------------</span></span></span></span></span>
          child: MainPage(
              key: Key(<span class="hljs-string">"YOUR_KEY"</span>),
              title: applicationName
          ),
        )
      );
    }
}
</code></pre>
<p>If a new version of your application is available in the store, you will see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To test things out, make sure you add this:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> Upgrader.clearSavedSettings()
</code></pre>
<p>inside your main method in your main.dart file.</p>
<p>Just so you are aware, there are a ton of configurations that you can set for the Upgrader package. I highly recommend you go and check these out.</p>
<h2 id="heading-how-to-test-the-packages">How to Test the Packages</h2>
<p>Regardless of which package you choose to work with, you need to know that your logic functions properly. </p>
<p>But how can you do that without releasing an official version of your application? You can use the internal testing option in Google Play Console. By releasing a new version of your application to internal testers, it will not be a public one and will allow you to test out the upgrading functionality.</p>
<p>Here is what you need to do:</p>
<ol>
<li>Log in to your Google Play Console account and head into the application you are working on to have the updating logic</li>
<li>Under Setup → Internal App Sharing, go to Manage Testers and make sure to allow testers to download and install the shared application. You can either choose to do so via link or by email.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Then, go to Testing → Internal Testing and click on the Create new release button (top right).</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Once you have performed a release, you can head back to the main Internal Testing page and click on the Testers tab. There you will see a list containing tester emails (empty right now). Click on the blue arrow icon.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-3.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="5">
<li>In this screen you can add yourself to be an internal tester (in the Add email addresses).</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-4.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="6">
<li>When you are done, you can head back to the Internal Testing window. Scroll down to the bottom and you will see the <strong>How testers join your test</strong> and you will see a Copy link button.</li>
</ol>
<p>You can now click the button and send yourself the link so you will be able to download the new version of your application.</p>
<p>If you fail to do one of the above steps, the link generated will lead to a not found (Error 404) page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-5.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you did everything successfully, you will see the following when you click on the generated link:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-6.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you see this error:</p>
<blockquote>
<p>_Install Error(-6): The download/install is not allowed, due to the current device state (e.g. low battery, low disk space, …). (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_INSTALL_NOT<em>ALLOWED)</em></p>
</blockquote>
<p>It might mean you are running your application on an emulated device and you need to have Google Play Store installed on it and be logged in.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>I wrote this article because I had to go through the same process when integrating the in app update package with my own application. </p>
<p>You are welcome to check it out at the <a target="_blank" href="https://play.google.com/store/apps/details?id=com.tomerpacific.birthday_calendar">Google Play Store</a>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-7.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And see the entire source code here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/TomerPacific/BirthdayCalendar">https://github.com/TomerPacific/BirthdayCalendar</a></div>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Real-time Chat Application with Nuxt ]]>
                </title>
                <description>
                    <![CDATA[ By Idorenyin Udoh In a real-time chat application, the recipient can view the sender’s message almost immediately. This can either be in a one-on-one conversation between two parties or a group conversation.  And that's what we're going to build in t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-real-time-chat-application-with-nuxt/</link>
                <guid isPermaLink="false">66d45f31706b9fb1c166b94f</guid>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 10 Aug 2022 19:39:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/chat-app-with-nuxt-image.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Idorenyin Udoh</p>
<p>In a real-time chat application, the recipient can view the sender’s message almost immediately. This can either be in a one-on-one conversation between two parties or a group conversation. </p>
<p>And that's what we're going to build in this tutorial. For this application, we will be using <a target="_blank" href="https://nuxtjs.org/">Nuxt</a>, the intuitive Vue framework.</p>
<p>Now that we know what we’re going to be building and the technology we’ll be using, let’s go over the chat API we’ll be using.</p>
<p>For this article, we’ll be going with <a target="_blank" href="https://robinapp.io/">Robin</a> because of its minimal UI and how easy it is to integrate into our app. With that out of the way, let’s get started.</p>
<h1 id="heading-step-1-create-the-nuxt-app">Step 1 – Create the Nuxt App</h1>
<p>First, we need to create the Nuxt app with any of the following commands:</p>
<pre><code>yarn create nuxt-app nuxt-chat-app
<span class="hljs-comment">// OR</span>
npx create-nuxt-app nuxt-chat-app
<span class="hljs-comment">// OR</span>
npm init nuxt-app nuxt-chat-app
</code></pre><h1 id="heading-step-2-create-a-robin-account">Step 2 – Create a Robin Account</h1>
<p>Now that our app is ready, we need to have a Robin account before we can use it. Head over to <a target="_blank" href="https://dashboard.robinapp.co/signup">Robin’s signup page</a> to create a 30-day free trial account. </p>
<p>Robin notifies you 7 days after you've created the account and you can remove your card before the billing date. </p>
<p>After filling out the signup form, you’ll be redirected to a billing page to fill in your card information. On the next page where it redirects you, Robin requests the name of the app you want to create and its authentication type. Feel free to use any name of your choice and either of the auth options.</p>
<p><img src="https://paper-attachments.dropbox.com/s_8728EF96CF25BE6F7A46E3619EB658CA92CDD4D1E377FEC5C8707FC59B5068A6_1658071816533_Screenshot+2022-07-17+at+16.29.23.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-step-3-get-your-robin-credentials">Step 3 – Get Your Robin Credentials</h1>
<p>Now that we have created an app on our Robin dashboard, there is something you should take note of. There are a couple of credentials you need when using Robin in your Nuxt app:</p>
<ul>
<li>API key,</li>
<li>User token,</li>
<li>User name,</li>
<li>Users, and</li>
<li>Keys</li>
</ul>
<p>Let’s go over each of them individually.</p>
<ul>
<li>API key: Robin automatically creates the API key when you create an app. You can retrieve it from either the getting started or the API config page on your dashboard. It is unique for every application.</li>
<li>User token: The user token is a unique identifier for every user of your Robin app. The token that should be passed to this property is yours since you’re the one using the app on your site. However, it is to be created by you, the user, typically on the server, and then used on the client side.</li>
<li>User name: The User name is the name of the current user of your Robin app. In this case, it will be your name. If you wanted someone else to include your Robin chat on their site or web app (i.e another user of your Robin app), it should be their name.</li>
<li>Users: Users is a list of the users on your Robin app. It usually contains their user tokens, profile images, and user names.</li>
<li>Keys: This fundamentally exists to help us be flexible in describing the user tokens, profile images, and user names in our users list. Here’s an example. If our keys object looks like this:</li>
</ul>
<pre><code class="lang-javascript">keys: {
  <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
  <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
  <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
}
</code></pre>
<p>Then our <code>users</code> array should describe the users’ tokens, profile images, and names with the values in the <code>keys</code> object.</p>
<p>Regardless of the users that would be using your Robin App, Robin requires a <code>userToken</code>, <code>profileImage</code> and a <code>userName</code> from them. Robin requires this for the display name and to identify each message sender and receiver uniquely on the platform.</p>
<pre><code class="lang-js">users: [
  {
    <span class="hljs-string">'user_token'</span>: <span class="hljs-string">'ABCDEF098765GH'</span>,
    <span class="hljs-string">'profile_image'</span>: <span class="hljs-string">'https://url-to-image'</span>,
    <span class="hljs-string">'user_name'</span>: <span class="hljs-string">'Article Reader'</span>
  }
]
</code></pre>
<h1 id="heading-step-4-install-robin-in-your-nuxt-app">Step 4 – Install Robin in Your Nuxt App</h1>
<p>Since we have everything we’ll need, we can go ahead and install Robin.</p>
<pre><code>npm i robin-vue
<span class="hljs-comment">// OR</span>
yarn add robin-vue
</code></pre><h1 id="heading-step-5-setup-the-robin-plugin">Step 5 – Setup the Robin Plugin</h1>
<p>In your <code>plugins</code> directory, create a <code>robin.js</code> file with the plugin setup:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> RobinChat <span class="hljs-keyword">from</span> <span class="hljs-string">'robin-vue'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'robin-vue/dist/style.css'</span>

Vue.use(RobinChat)
</code></pre>
<p>Note that we import the CSS because the <code>RobinChat</code> component does not include any CSS itself.</p>
<h1 id="heading-step-6-register-the-plugin">Step 6 – Register the Plugin</h1>
<p>The <code>plugins</code> property in the <code>nuxt.config.js</code> file is to let our Nuxt app know about the plugins that it should use. So if we don’t include our Robin plugin there, it won’t be available in our app.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-attr">plugins</span>: [
    { <span class="hljs-attr">src</span>: <span class="hljs-string">'~/plugins/robin.js'</span>, <span class="hljs-attr">mode</span>: <span class="hljs-string">'client'</span> }
  ]
}
</code></pre>
<h1 id="heading-step-7-use-the-plugin">Step 7 – Use the Plugin</h1>
<p>Now what’s left is for us to include the <code>RobinChat</code> component anywhere in our app and pass those credentials we discussed earlier as props. </p>
<p>Once again, the credentials are:</p>
<ul>
<li>API key,</li>
<li>User token,</li>
<li>User name,</li>
<li>Users, and</li>
<li>Keys</li>
</ul>
<p>In this list, what we currently don’t have is our user token and the tokens of the users on our app. </p>
<p>Recall that these tokens are usually created on the server. But we don’t have the luxury of that. So we can go ahead and create them with the help of <a target="_blank" href="https://www.npmjs.com/package/robin.io-js">Robin’s JavaScript SDK</a>. The Vue SDK we previously installed depends on this JavaScript SDK. So we don’t need to install it since it already exists in our app.</p>
<h2 id="heading-how-to-create-the-user-tokens">How to Create the User Tokens</h2>
<p>We can go ahead and create the tokens in the page we're going to include the chat UI. Because it’s for learning purposes, we can go ahead and create tokens for 5 users, ourselves included. We need to come up with usernames for each of them.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  &lt;!-- ... --&gt;
&lt;/template&gt;


<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">users</span>: [
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'idorenyin'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'ayo'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'elvis'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'favour'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'enoch'</span>
        }
      ],
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Note that the keys in every user object in the <code>users</code> array have to be defined in the <code>keys</code> object that we’ll be passing as a prop to the Robin component.</p>
<pre><code class="lang-javascript">keys: {
  <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
  <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
  <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
},
</code></pre>
<p>Next, we use the SDK’s <code>createUserToken()</code> function to create the tokens after creating a Robin instance, as it says in <a target="_blank" href="https://docs.robinapp.co/frontend-sdks/javascript/getting-started">Robin’s docs</a>.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  &lt;!-- ... --&gt;
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { Robin } <span class="hljs-keyword">from</span> <span class="hljs-string">'robin.io-js'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">keys</span>: {
        <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
        <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
        <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
      },
      <span class="hljs-attr">users</span>: [
        <span class="hljs-comment">// ...</span>
      ]
    }
  },
  created () {
    <span class="hljs-built_in">this</span>.createTokens()
  },
  <span class="hljs-attr">methods</span>: {
    <span class="hljs-keyword">async</span> createTokens () {
      <span class="hljs-keyword">const</span> robin = <span class="hljs-keyword">new</span> Robin(<span class="hljs-string">'API_KEY'</span>, <span class="hljs-literal">true</span>)
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">this</span>.users.length; i++) {
        <span class="hljs-keyword">await</span> robin.createUserToken({
          <span class="hljs-attr">meta_data</span>: {
            <span class="hljs-attr">username</span>: <span class="hljs-built_in">this</span>.users[i].user_name
          }
        }).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
          <span class="hljs-built_in">this</span>.users[i].user_token = res.data.user_token
        })
      }
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<h2 id="heading-how-to-use-credentials-on-the-robinchat-component">How to Use Credentials on the RobinChat Component</h2>
<p>We now have everything we need to display the Robin chat UI on our app. Whew!<br>We can now go ahead and use the tokens and the other credentials.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  &lt;!-- ... --&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RobinChat</span>
    <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tokensAreAvailable"</span>
    <span class="hljs-attr">:api-key</span>=<span class="hljs-string">"apiKey"</span>
    <span class="hljs-attr">:user-token</span>=<span class="hljs-string">"users[0].user_token"</span>
    <span class="hljs-attr">user-name</span>=<span class="hljs-string">"Idorenyin Udoh"</span>
    <span class="hljs-attr">:keys</span>=<span class="hljs-string">"keys"</span>
    <span class="hljs-attr">:users</span>=<span class="hljs-string">"users"</span>
  /&gt;</span></span>
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { Robin } <span class="hljs-keyword">from</span> <span class="hljs-string">'robin.io-js'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">tokensAreAvailable</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">apiKey</span>: <span class="hljs-string">'API_KEY'</span>,
      <span class="hljs-attr">keys</span>: {
        <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
        <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
        <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
      },
      <span class="hljs-attr">users</span>: [
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'idorenyin'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'ayo'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'elvis'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'favour'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'enoch'</span>
        }
      ]
    }
  },
  created () {
    <span class="hljs-built_in">this</span>.createTokens()
  },
  <span class="hljs-attr">methods</span>: {
    <span class="hljs-keyword">async</span> createTokens () {
      <span class="hljs-keyword">const</span> robin = <span class="hljs-keyword">new</span> Robin(<span class="hljs-built_in">this</span>.apiKey, <span class="hljs-literal">true</span>)
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">this</span>.users.length; i++) {
        <span class="hljs-keyword">await</span> robin.createUserToken({
          <span class="hljs-attr">meta_data</span>: {
            <span class="hljs-attr">username</span>: <span class="hljs-built_in">this</span>.users[i].user_name
          }
        }).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
          <span class="hljs-built_in">this</span>.users[i].user_token = res.data.user_token
        })
      }
      <span class="hljs-built_in">this</span>.tokensAreAvailable = <span class="hljs-literal">true</span>
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Note that we only display the <code>RobinChat</code> component when all the users’ tokens are available to avoid errors.</p>
<p>This is what the result looks like:</p>
<p><img src="https://paper-attachments.dropbox.com/s_8728EF96CF25BE6F7A46E3619EB658CA92CDD4D1E377FEC5C8707FC59B5068A6_1658311851926_Screenshot+2022-07-20+at+11.10.45.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The app is available <a target="_blank" href="https://nuxt-chat-lmqlbq79p-idorenyinudoh.vercel.app/">here</a>.</p>
<p>Note that I used previously-created user tokens for this app because you wouldn’t be able to view messages if tokens are created every time the app loads. Permanent tokens are what make the messages on Robin persist.</p>
<p>Also, I created <a target="_blank" href="https://nuxt-chat-app-git-ayo-idorenyinudoh.vercel.app/">another app</a> for the user Ayo. You can check it out too. This way, you can test the real-time communication between Idorenyin and Ayo.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>You just learned how to implement real-time communication on a Nuxt application with Robin. </p>
<p>The ease of integration makes it super fast to implement a chat system in your app and focus on building/maintaining it. </p>
<p>If you make sure to create your users’ tokens on the server, then implementing the integration on the frontend wouldn't be too hard.</p>
<p>Happy building!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Full-Stack Real-Time Voting App With Hasura and React ]]>
                </title>
                <description>
                    <![CDATA[ By Catalin Pit This article will teach you how to build a voting application that displays the poll results in real-time. Each time someone votes, the application updates automatically and shows the new results. Even though you will build a full-stac... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-full-stack-real-time-voting-app-with-hasura-and-react/</link>
                <guid isPermaLink="false">66d84e5f29e30bc0ad477559</guid>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 29 Mar 2022 19:07:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/03/pexels-rodnae-productions-7581108.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Catalin Pit</p>
<p>This article will teach you how to build a voting application that displays the poll results in real-time. Each time someone votes, the application updates automatically and shows the new results.</p>
<p>Even though you will build a full-stack application, you will not write any backend code thanks to Hasura!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646303998900/GysSg52mZ.png" alt="Screenshot of the real-time poll application built with Hasura and React" width="2866" height="1906" loading="lazy"></p>
<p>The application will use:</p>
<ul>
<li>Hasura GraphQL Engine for the backend</li>
<li>React and Apollo for the frontend</li>
</ul>
<p><a target="_blank" href="https://realtime-poll.demo.hasura.io/">Live Demo</a> | <a target="_blank" href="https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Frealtime-poll.hasura.app/v1/graphql">Backend Explorer</a> | <a target="_blank" href="https://github.com/catalinpit/graphql-engine/tree/master/community/sample-apps/realtime-poll">GitHub Repository</a></p>
<h2 id="heading-why-these-technologies">Why These Technologies?</h2>
<p>Before going further, let's talk about the technologies we'll use for the application stack.</p>
<h3 id="heading-hasura-graphql-engine">Hasura GraphQL Engine</h3>
<p><a target="_blank" href="https://hasura.io">Hasura</a> is an open source <a target="_blank" href="https://github.com/hasura/graphql-engine">GraphQL Engine</a> that enables you to create an instant, real-time GraphQL API without writing backend code at all.</p>
<p>You might wonder how that works. Hasura connects to your database and it automatically generates the API based on your database tables and views. You get things like the GraphQL Schema and Resolvers out of the box.</p>
<p>Therefore, the reason for choosing Hasura is to speed up the process of building the real-time GraphQL API. Hasura does all the heavy lifting, so we can focus on other things.</p>
<h3 id="heading-react-and-apollo-client">React and Apollo Client</h3>
<p>React is one of the most popular JavaScript frameworks with a great community. It's also versatile, allowing you to build both web and mobile applications.</p>
<p>Apollo Client is a fully-featured GraphQL client that allows you to build user interface components and fetch data via GraphQL seamlessly. The Apollo Client is also one of the most popular GraphQL clients.</p>
<p>Together, React and Apollo Client form a powerful combination that fits the requirements for the real-time voting application.</p>
<h2 id="heading-data-modeling">Data Modeling</h2>
<p>The first step is to determine the structure of the database. The database will contain the following tables:</p>
<ul>
<li>user – a <em>user</em> is a person who votes in the poll</li>
<li>poll – the <em>poll</em> represents the question (<em>e.g. what is your favorite framework?</em>)</li>
<li>option – an <em>option</em> is a poll option that people can pick</li>
<li>vote – a <em>vote</em> is the link between a user and a poll option. It represents the user’s vote.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646384055424/h8aci5wK0.png" alt="drawSQL-export-2022-03-04_10_53.png" width="1894" height="1026" loading="lazy"></p>
<p>The figure above illustrates the database tables and relationships.</p>
<p>There is a <code>one-to-many</code> relationship between the <code>user</code> and <code>vote</code>. A user can vote multiple times, but one vote can belong to only one user.</p>
<p>The <code>poll</code> and <code>option</code> have a <code>one-to-many</code> relationship meaning that a poll can have multiple options, but an option belongs to only one poll.</p>
<p>Lastly, there is a <code>one-to-many</code> relationship between the <code>option</code> and <code>vote</code> tables. What that means is that you can only pick one option. One vote represents one option.</p>
<p>The database also has two views – <code>online_users</code> and <code>poll_results</code>. They show the number of online users and the results from the poll.</p>
<h2 id="heading-backend-implementation-with-hasura">Backend Implementation with Hasura</h2>
<p>You can use Hasura in two ways:</p>
<ul>
<li>locally with the help of Docker (<a target="_blank" href="https://hasura.io/docs/latest/graphql/core/getting-started/docker-simple.html#docker-simple">see guide</a>)</li>
<li>in the cloud with the help of Hasura Cloud (<a target="_blank" href="https://hasura.io/docs/latest/graphql/cloud/getting-started/index.html#cloud-getting-started">see guide</a>)</li>
</ul>
<p>It's worth mentioning that Hasura Cloud also offers advanced performance, security, and monitoring features. Some of the things it offers are as follows:</p>
<ul>
<li>automatically scaling your application up or down</li>
<li>monitoring and tracing</li>
<li>rate limiting</li>
</ul>
<p>These are just three benefits, but there are more. If you want to check them out, you can do it <a target="_blank" href="https://hasura.io/cloud/">here</a>.</p>
<p>This tutorial uses the cloud version, but you can follow the tutorial even if you use Hasura locally. With that out of the way, let's start building the backend.</p>
<h3 id="heading-database-setup">Database Setup</h3>
<p>After setting up the account, go to the project’s dashboard and click on the "DATA" tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646391279001/BCP-EtIN2.png" alt="Screenshot 2022-03-03 at 15.04.47.png" width="2866" height="1906" loading="lazy"></p>
<p>In the "DATA" tab, you can connect to an existing database or create a new one on Heroku. We will create a new database so click on the "Create Heroku Database" option.</p>
<p>After that, click on "Create Database" and you should have a PostgreSQL database up and running within seconds.</p>
<h3 id="heading-database-tables">Database Tables</h3>
<p>The next step involves creating the database tables. Go to your newly created database and click on the "Create Table" button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646396538475/13G3LsTT9.png" alt="Screenshot 2022-03-04 at 14.21.48.png" width="2840" height="1636" loading="lazy"></p>
<p>Clicking the button opens a new page where you can create a new table.</p>
<h4 id="heading-user-table">User Table</h4>
<p>The "user" table has the following columns:</p>
<ul>
<li>id (primary key) – UUID, <code>gen_random_uuid()</code>, Unique</li>
<li>created_at – Timestamp, <code>now()</code></li>
<li>online_ping – Boolean, Nullable</li>
<li>last_seen_at -–Timestamp, Nullable</li>
</ul>
<p>The figure illustrates the table columns, types, and other configurations.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646398900732/dI-bt0NnQ.png" alt="Screenshot 2022-03-04 at 14.27.36.png" width="2850" height="1986" loading="lazy"></p>
<p>Before saving the table, set the <code>id</code> column as the Primary Key, as shown in the above figure.</p>
<p>After that, scroll down and press the "Add Table" button to save it. You can follow the same process to create the other tables.</p>
<h4 id="heading-poll-table">Poll Table</h4>
<p>The "poll" table has the following columns:</p>
<ul>
<li>id (primary key) – UUID, <code>gen_random_uuid()</code>, Unique</li>
<li>created_at – Timestamp, <code>now()</code></li>
<li>created_by – UUID, nullable</li>
<li>question – text</li>
</ul>
<h4 id="heading-option-table">Option Table</h4>
<p>The "option" table has the following columns:</p>
<ul>
<li>id (primary key) – UUID, <code>gen_random_uuid()</code>, Unique</li>
<li>poll_id – UUID</li>
<li>text – text</li>
</ul>
<h4 id="heading-vote-table">Vote Table</h4>
<p>The "vote" table has the following columns:</p>
<ul>
<li>id (primary key) – UUID, <code>gen_random_uuid()</code>, Unique</li>
<li>created_by_user_id – UUID</li>
<li>option_id – UUID</li>
<li>created_at – Timestamp, <code>now()</code></li>
</ul>
<h3 id="heading-database-views">Database Views</h3>
<p>We will use views for the poll results and online users because they allow us to re-use complex queries. A view is the result of querying one or more tables.</p>
<p>You can think of a view as saving a complex query and giving it a name so you can re-use it. A view is called a "virtual table" and you can query it as you would query a regular table.</p>
<h4 id="heading-poll-results">Poll Results</h4>
<p>Displaying the poll results requires us to perform database joins on the <code>poll</code>, <code>option</code> and <code>vote</code> tables.</p>
<p>The first database join returns all the records from the <code>vote</code> table and the matching records from the <code>option</code> table. That is, it returns each vote and its associated poll option.</p>
<p>The second join returns all the records from the <code>option</code> table and the matching records from the <code>poll</code> table. That is, it returns all options and the poll they belong to.</p>
<p>After that, the view counts all the records returned and returns them as "votes".</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span>
<span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">VIEW</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"poll_results"</span> <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span>
  poll.id <span class="hljs-keyword">AS</span> poll_id,
  o.option_id,
  <span class="hljs-keyword">count</span>(*) <span class="hljs-keyword">AS</span> votes
<span class="hljs-keyword">FROM</span>
  (
    (
      <span class="hljs-keyword">SELECT</span>
        vote.option_id,
        option.poll_id,
        option.text
      <span class="hljs-keyword">FROM</span>
        (
          vote
          <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">option</span> <span class="hljs-keyword">ON</span> ((option.id = vote.option_id))
        )
    ) o
    <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> poll <span class="hljs-keyword">ON</span> ((poll.id = o.poll_id))
  )
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span>
  poll.question,
  o.option_id,
  poll.id;
</code></pre>
<p>Where do you add these database views?</p>
<p>To add the database views, go to the "DATA" tab and click on the "SQL" option. The "SQL" page allows you to run SQL statements directly on the database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646914117338/RcIcFniVe.png" alt="Screenshot 2022-03-10 at 14.08.23.png" width="2926" height="1994" loading="lazy"></p>
<p>After that, add the SQL code and click on the "Run!" button. If there are no errors, you should be able to access and use the newly created view.</p>
<h4 id="heading-online-users">Online Users</h4>
<p>For the number of online users, we can also use a view.</p>
<p>The <code>users</code> table has a property <code>last_seen_at</code> that keeps track of the users’ last login. We can use this property to determine the number of logged-in users (online).</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span>
<span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">VIEW</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"online_users"</span> <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span>
  <span class="hljs-keyword">count</span>(*) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">count</span>
<span class="hljs-keyword">FROM</span>
  <span class="hljs-string">"user"</span>
<span class="hljs-keyword">WHERE</span>
  (
    <span class="hljs-string">"user"</span>.last_seen_at &gt; (<span class="hljs-keyword">now</span>() - <span class="hljs-string">'00:00:15'</span> :: <span class="hljs-built_in">interval</span>)
  );
</code></pre>
<p>The above view counts the number of users seen in the last 15 seconds. If they logged in during the last 15 seconds, we count them as online users.</p>
<h3 id="heading-relationships">Relationships</h3>
<p>The last step in implementing the backend is configuring the relationships between tables. With Hasura, you can create relationships between tables in two ways:</p>
<ol>
<li>using foreign key constrains</li>
<li>manually (when it's not possible to use foreign key constraints)</li>
</ol>
<p>Going further, we will create relationships by adding foreign key constraints. If you want to read more about relationships, the documentation has a comprehensive section on <a target="_blank" href="https://hasura.io/docs/latest/graphql/core/databases/postgres/schema/table-relationships/index.html">table relationships</a>.</p>
<h4 id="heading-user-vote">User – Vote</h4>
<p>Navigate to "Modify" in the <code>vote</code> table and click on the button saying "Add a foreign key".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646921160263/stfCBtYDb.png" alt="Screenshot 2022-03-10 at 16.05.35.png" width="2838" height="1906" loading="lazy"></p>
<p>Let's configure the <code>created_by_user_id</code> column as a foreign key for the <code>id</code> column in the <code>users</code> table.</p>
<p>The image illustrates the process of adding foreign keys.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646921268139/YzXpsQC1h.png" alt="Screenshot 2022-03-10 at 16.07.09.png" width="2838" height="1906" loading="lazy"></p>
<p>Following the same process, add the <code>option_id</code> column as a foreign key for the <code>id</code> column in the <code>option</code> table.</p>
<p>The "Reference Table" field value should be <code>option</code>. The value for "From" should be <code>option_id</code>, whereas the value for "To" should be <code>id</code>.</p>
<p>Since you added the foreign keys, Hasura suggests potential relationships automatically. If you go to the "Relationships" tab, you should see the suggested relationships.</p>
<p>When you click on the "Add" button, you get the chance to name your relationship. You can either leave the default name or use a custom one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646921792095/fXUhw7ppq.png" alt="Screenshot 2022-03-10 at 16.15.49.png" width="2926" height="1994" loading="lazy"></p>
<p>Save the relationships by pressing the "Add" button and you are done!</p>
<h4 id="heading-poll-option">Poll – Option</h4>
<p>Go to the <code>option</code> table and add the <code>poll_id</code> as the foreign key for the <code>id</code> column in the <code>poll</code> table.</p>
<ul>
<li><strong>Reference Table</strong> – poll</li>
<li><strong>From</strong> – poll_id</li>
<li><strong>To</strong> – id</li>
</ul>
<p>After saving it, go to the "Relationships" tab and accept only the suggested "Object Relationship".</p>
<h4 id="heading-option-vote">Option – Vote</h4>
<p>When you configured the foreign keys for the "User – Vote" relationship earlier, you added the <code>option_id</code> column as a foreign key for the <code>id</code> column in the <code>option</code> table.</p>
<p>That means the only thing left to do is to go to the "Relationships" tab in the <code>poll</code> table and accept the suggested "Array Relationship".</p>
<h4 id="heading-poll-results-view">Poll Results View</h4>
<p>For the <code>poll_results</code> View, we need to manually set the relationships with the <code>option</code> and <code>poll</code> tables. Looking at the view, you can see we have the foreign keys <code>poll_id</code> and <code>option_id</code>.</p>
<p>Go to the "Relationships" tab in <code>poll_results</code> to add the relationships manually. Once you are there, click on the button saying "Configure".</p>
<p>The relationships between <code>poll_results</code> and the <code>option</code> and <code>poll</code> tables are object relationships.</p>
<p>Configure the relationship between <code>poll_results</code> and <code>option</code> as shown in the figure below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647432404572/wVAaTy93w.png" alt="Screenshot 2022-03-16 at 14.05.57.png" width="2926" height="1994" loading="lazy"></p>
<p>The relationship between <code>poll_results</code> and <code>poll</code> is configured in a similar way. Add the following information:</p>
<ul>
<li><strong>Relationship Type</strong> – Object Relationship</li>
<li><strong>Relationship Name</strong> – poll</li>
<li><strong>Reference Schema</strong> – public</li>
<li><strong>Reference Table</strong> – poll</li>
<li><strong>From</strong> – poll_id</li>
<li><strong>To</strong> – id</li>
</ul>
<p>Save them and you are done with the relationships!</p>
<h3 id="heading-graphql-api-is-ready">GraphQL API is Ready</h3>
<p>You now have a fully functioning GraphQL API without writing any line of code. If you go to the API Explorer in Hasura Console, you can insert, modify and delete data.</p>
<p>Imagine building the same application manually – it would be quite tedious and time-consuming.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646924258746/MRS3Us3SX.png" alt="Screenshot 2022-03-10 at 16.57.13.png" width="2926" height="1994" loading="lazy"></p>
<p>The next step is to implement the frontend part.</p>
<h2 id="heading-frontend-implementation">Frontend Implementation</h2>
<p>The first step of the frontend implementation is to create and initialize the project. Go to your preferred folder and run:</p>
<pre><code>npx create-react-app realtime-poll
</code></pre><p>Once the installation finishes, go to the <code>realtime-poll</code> folder and install the required dependencies:</p>
<pre><code>npm i react-bootstrap react-google-charts @apollo/client graphql graphql-ws
</code></pre><p>The <code>react-bootstrap</code> package will allow us to build the application interface with Bootstrap, whereas <code>react-google-charts</code> will help us display the poll results as a chart. The other packages will enable us to use the GraphQL API we built previously.</p>
<p>Before going further, delete <code>setupTests.js</code>, <code>reportWebVitals.js</code> and <code>logo.svg</code> from the <code>src</code> folder. After that, remove all the references to those files from <code>index.js</code> and <code>App.js</code>.</p>
<h3 id="heading-setup-graphql-client-with-apollo">Setup GraphQL Client with Apollo</h3>
<p>The voting application uses GraphQL Subscriptions to show the poll results in real-time. When people vote, the results of the poll update automatically, so we need to display them without forcing people to refresh the page.</p>
<p>A GraphQL Subscription is an operation that allows us to do that by subscribing to server events. When the data (<em>poll results</em>) gets updated (<em>someone votes</em>),  we receive the updates in real-time.</p>
<p>Since the data is pushed to the client each time there is an update, we need a special connection. GraphQL Subscriptions use WebSockets, which allows us to keep an open connection between the server and client.</p>
<p><em>Note: This article briefly touches on Subscriptions. For more information, check the documentation to <a target="_blank" href="https://hasura.io/learn/graphql/intro-graphql/graphql-subscriptions/">learn about GraphQL Subscription</a>.</em></p>
<p>Let's start implementing the GraphQL Client with Apollo. The first step is to create a new file in the <code>src</code> folder:</p>
<pre><code>📂 realtime-poll
 └ 📁 node_modules
   📁 package-lock.json
   📁 package.json
   📁 public
   📁 README.md
   📂 src
    └ apollo.js
</code></pre><p>Open the newly created file, <code>apollo.js</code>, and import the following packages:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { ApolloClient, HttpLink, InMemoryCache, split } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> { GraphQLWsLink } <span class="hljs-keyword">from</span> <span class="hljs-string">'@apollo/client/link/subscriptions'</span>;
<span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"graphql-ws"</span>;
<span class="hljs-keyword">import</span> { getMainDefinition } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client/utilities"</span>;
</code></pre>
<p>After that, store your application endpoint in a separate variable. Replace the value "realtime-poll-example.hasura.app" with the URL of your application.</p>
<p>As we mentioned earlier, GraphQL Subscriptions use the WebSocket protocol, so we need two links. We will use one link, <code>httpURL</code>, for queries and mutations and the other, <code>wsURI</code>, for subscriptions.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> GRAPHQL_ENDPOINT = <span class="hljs-string">"realtime-poll-example.hasura.app"</span>;

<span class="hljs-keyword">const</span> scheme = <span class="hljs-function">(<span class="hljs-params">proto</span>) =&gt;</span>
  <span class="hljs-built_in">window</span>.location.protocol === <span class="hljs-string">"https:"</span> ? <span class="hljs-string">`<span class="hljs-subst">${proto}</span>s`</span> : proto;

<span class="hljs-keyword">const</span> wsURI = <span class="hljs-string">`<span class="hljs-subst">${scheme(<span class="hljs-string">"ws"</span>)}</span>://<span class="hljs-subst">${GRAPHQL_ENDPOINT}</span>/v1/graphql`</span>;
<span class="hljs-keyword">const</span> httpURL = <span class="hljs-string">`<span class="hljs-subst">${scheme(<span class="hljs-string">"https"</span>)}</span>://<span class="hljs-subst">${GRAPHQL_ENDPOINT}</span>/v1/graphql`</span>;
</code></pre>
<p>We also have a function (splitter) that determines which link to use. If the operation is a query or mutation, it uses the HTTP link. Otherwise, it uses the WebSocket link.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> splitter = <span class="hljs-function">(<span class="hljs-params">{ query }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { kind, operation } = getMainDefinition(query) || {};
  <span class="hljs-keyword">const</span> isSubscription =
    kind === <span class="hljs-string">"OperationDefinition"</span> &amp;&amp; operation === <span class="hljs-string">"subscription"</span>;
  <span class="hljs-keyword">return</span> isSubscription;
};
</code></pre>
<p><code>GraphQLWsLink</code> enables us to execute the subscriptions. The <code>createClient</code> constructor receives the WebSocket link and additional connection options as parameters. Then, we pass the value returned by <code>createClient</code> to the <code>GraphQLWsLink</code> constructor.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">new</span> InMemoryCache();
<span class="hljs-keyword">const</span> options = { <span class="hljs-attr">reconnect</span>: <span class="hljs-literal">true</span> };

<span class="hljs-keyword">const</span> wsLink = <span class="hljs-keyword">new</span> GraphQLWsLink(createClient({ <span class="hljs-attr">url</span>: wsURI, <span class="hljs-attr">connectionParams</span>: { options } }));
</code></pre>
<p>We are left with:</p>
<ul>
<li>setting up the HTTP link for queries and mutation</li>
<li>using the splitter function</li>
<li>creating the ApolloClient</li>
</ul>
<p>The <code>split</code> function takes the splitter function we wrote earlier and the two links as arguments. We pass the link returned by the "split" function as an argument to the <code>ApolloClient</code> constructor.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> httpLink = <span class="hljs-keyword">new</span> HttpLink({ <span class="hljs-attr">uri</span>: httpURL });
<span class="hljs-keyword">const</span> link = split(splitter, wsLink, httpLink);
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> ApolloClient({ link, cache });
</code></pre>
<p>Lastly, we export the client so we can use it for queries, mutations and subscriptions.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> client;
</code></pre>
<p>You can see the complete code in <a target="_blank" href="https://gist.github.com/catalinpit/839c22b0a430c5b690a3d2d409115674">this gist</a>.</p>
<h3 id="heading-graphql-operations">GraphQL Operations</h3>
<p>We will use a couple of <a target="_blank" href="https://hasura.io/learn/graphql/intro-graphql/graphql-queries/">GraphQL queries</a>, <a target="_blank" href="https://hasura.io/learn/graphql/intro-graphql/graphql-mutations/">mutations</a> and <a target="_blank" href="https://hasura.io/learn/graphql/intro-graphql/graphql-subscriptions/">subscriptions</a> in the application. They will also be displayed on the homepage of the application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647347965793/tZyz3XUhX.png" alt="Screenshot 2022-03-15 at 14.39.13.png" width="2436" height="1208" loading="lazy"></p>
<p>As a result, we will store them in a separate file and import them where necessary.</p>
<p>Go to the <code>src</code> folder and create a new file named <code>GraphQL.jsx</code>.</p>
<pre><code>📂 realtime-poll
 └ 📁 node_modules
   📁 package-lock.json
   📁 package.json
   📁 public
   📁 README.md
   📂 src
    └ GraphQL.jsx
</code></pre><p>Open <code>GraphQL.jsx</code> and add the following imports:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> gql <span class="hljs-keyword">from</span> <span class="hljs-string">"graphql-tag"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Card } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-bootstrap"</span>;
</code></pre>
<p>We need <code>gql</code> so the GraphQL queries, mutations, and subscriptions can be parsed into the standard GraphQL AST. We also need React and the Card component because we will render the GraphQL query, mutation, and subscription strings on the page.</p>
<h4 id="heading-get-polls">Get Polls</h4>
<p>The application needs a query to fetch all the polls from the database.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> QUERY_GET_POLL = gql<span class="hljs-string">`
  query {
    poll(limit: 10) {
      id
      question
      options(order_by: { id: desc }) {
        id
        text
      }
    }
  }
`</span>;
</code></pre>
<p>The above query returns 10 polls along with their id, question and options (answers). The options are ordered in descending order by the id.</p>
<h4 id="heading-vote">Vote</h4>
<p>Since it's a poll, there should be a way to pick an answer and vote.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> MUTATION_VOTE = gql<span class="hljs-string">`
  mutation vote($optionId: uuid!, $userId: uuid!) {
    insert_vote(
      objects: [{ option_id: $optionId, created_by_user_id: $userId }]
    ) {
      returning {
        id
      }
    }
  }
`</span>;
</code></pre>
<p>The above mutation inserts a new vote into the database.</p>
<h4 id="heading-real-time-poll-results">Real-time Poll Results</h4>
<p>The voting application displays the results in real-time with the help of this subscription:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> SUBSCRIPTION_RESULT = gql<span class="hljs-string">`
  subscription getResult($pollId: uuid!) {
    poll_results(
      order_by: { option_id: desc }
      where: { poll_id: { _eq: $pollId } }
    ) {
      option_id
      option {
        id
        text
      }
      votes
    }
  }
`</span>;
</code></pre>
<p>These are the GraphQL operations used by the application to display the polls, allow the users to vote and show the results in real-time.</p>
<p>The file also contains:</p>
<ul>
<li>two mutations to create a new user and mark the user online</li>
<li>one subscription to display the number of online users in real-time</li>
</ul>
<p>You can find the complete code for <code>GraphQL.jsx</code> in this <a target="_blank" href="https://gist.github.com/catalinpit/11d9e23b12878749b7eb44a22b047169">gist</a>.</p>
<h3 id="heading-poll-implementation">Poll Implementation</h3>
<p>The next step is to implement the Poll. Create a new file <code>Poll.jsx</code> in the <code>src</code> folder.</p>
<pre><code>📂 realtime-poll
 └ 📁 node_modules
   📁 package-lock.json
   📁 package.json
   📁 public
   📁 README.md
   📂 src
    └ Poll.jsx
</code></pre><p>The <code>Poll.jsx</code> file will contain two components:</p>
<ul>
<li><code>PollQuestion</code> which represents the poll itself and it handles the voting</li>
<li><code>Poll</code> which renders the poll question and answers</li>
</ul>
<p>Open the newly created file and add the following imports:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useMutation, useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> React, { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Button, Form } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-bootstrap"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-built_in">Error</span>, Loading } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Components"</span>;
<span class="hljs-keyword">import</span> { MUTATION_VOTE, QUERY_GET_POLL } <span class="hljs-keyword">from</span> <span class="hljs-string">"./GraphQL"</span>;
<span class="hljs-keyword">import</span> { Result } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Result"</span>;
</code></pre>
<p>The first line imports the two hooks from the Apollo Client that allow you to execute queries and mutations. We import React and its two default hooks on the second line, whereas on the third line, we import two Bootstrap components.</p>
<p>The last three lines import custom React components, GraphQL Queries, and Mutations. They do not exist yet, but we will implement them later.</p>
<h4 id="heading-poll-question-component">Poll Question Component</h4>
<p>The next step is to implement the component for the poll question. The "PollQuestion" component handles the voting process. After your imports, write the following code:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> PollQuestion = <span class="hljs-function">(<span class="hljs-params">{ poll, userId }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> defaultState = {
      <span class="hljs-attr">optionId</span>: <span class="hljs-string">""</span>,
      <span class="hljs-attr">pollId</span>: poll.id,
      <span class="hljs-attr">voteBtnText</span>: <span class="hljs-string">"🗳 Vote"</span>,
      <span class="hljs-attr">voteBtnStyle</span>: <span class="hljs-string">"primary"</span>,
    };
    <span class="hljs-keyword">const</span> [state, setState] = useState(defaultState);
    <span class="hljs-keyword">const</span> [vote, { data, loading, error }] = useMutation(MUTATION_VOTE);
};
</code></pre>
<p>In the above code, we set the default state for the poll. When someone visits the poll for the first time, there should not be any answer selected. Also, the button should display "🗳 Vote".</p>
<p>The image illustrates what the poll looks like with the default state.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647330497362/WhUxYq-qx.png" alt="Screenshot 2022-03-15 at 09.48.04.png" width="2174" height="682" loading="lazy"></p>
<p>We also use the <code>useMutation</code> hook so people can vote. The hook returns an array that contains two elements. The first element (<code>vote</code>) is a function that we can call to run the mutation. The second one is an object which we can destructure further.</p>
<p>We need to update the <code>optionId</code> each time a user selects an answer. For example, if the user selects "Vue" in this poll, we set the <code>optionId</code> to the id of that option.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> handleOptionChange = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> optionId = e.currentTarget.value;
    setState(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> ({ ...prev, optionId }));
};
</code></pre>
<p>Let's move on to writing the logic for handling a vote submission. First, we need to ensure the users cannot submit an empty form. If the user did not select an answer, the button becomes yellow and prompts the user to choose an answer and try again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647333432909/GPH4Vn4Hj.png" alt="Screenshot 2022-03-15 at 10.34.43.png" width="2142" height="630" loading="lazy"></p>
<p>If an answer is selected, we call the <code>vote</code> function returned by the <code>useMutation</code> hook. We pass the id of the selected answer and the id of the user that voted.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> handlesubmitVote = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault();

    <span class="hljs-keyword">if</span> (!state.optionId) {
      setState({
        <span class="hljs-attr">voteBtnText</span>: <span class="hljs-string">"✋ Select an option and try again"</span>,
        <span class="hljs-attr">voteBtnStyle</span>: <span class="hljs-string">"warning"</span>,
      });
      <span class="hljs-keyword">return</span>;
    }

    setState({
      <span class="hljs-attr">voteBtnText</span>: <span class="hljs-string">"🗳️ Submitting"</span>,
      <span class="hljs-attr">voteBtnStyle</span>: <span class="hljs-string">"info"</span>,
    });

    vote({
      <span class="hljs-attr">variables</span>: {
        <span class="hljs-attr">optionId</span>: state.optionId,
        userId,
      },
    });
};
</code></pre>
<p>If the vote is successful, we update the button state accordingly. After 5 seconds, we reset the state of the button so the users can vote again. If there is an error, the button will highlight that.</p>
<p>The <code>useEffect</code> hook runs each time the value of <code>data</code> or <code>error</code> changes.</p>
<pre><code class="lang-jsx">useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (data) {
      setState({
        <span class="hljs-attr">voteBtnText</span>: <span class="hljs-string">"👍 Done"</span>,
        <span class="hljs-attr">voteBtnStyle</span>: <span class="hljs-string">"success"</span>,
      });

      <span class="hljs-comment">//  Re-authorize to vote after 5 seconds</span>
      <span class="hljs-keyword">let</span> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setState({
          <span class="hljs-attr">voteBtnText</span>: <span class="hljs-string">"🗳️ Vote"</span>,
          <span class="hljs-attr">voteBtnStyle</span>: <span class="hljs-string">"primary"</span>,
        });
      }, <span class="hljs-number">5000</span>);

      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearTimeout</span>(timer);
    }

    <span class="hljs-keyword">if</span> (error) {
      setState({
        <span class="hljs-attr">voteBtnText</span>: <span class="hljs-string">"Error 😞 Try again"</span>,
        <span class="hljs-attr">voteBtnStyle</span>: <span class="hljs-string">"danger"</span>,
      });
    }
}, [data, error]);
</code></pre>
<p>Lastly, we render the form, poll options, and the voting button.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"textLeft"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{poll.question}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Form</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"pollForm textLeft"</span>
        <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
          handlesubmitVote(e);
        }}
      &gt;
        {poll.options.map(({ id, text }) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Form.Check</span>
            <span class="hljs-attr">custom</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"voteCandidate"</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">{id}</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{id}</span>
            <span class="hljs-attr">label</span>=<span class="hljs-string">{text}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleOptionChange}</span>
          /&gt;</span>
        ))}
        <span class="hljs-tag">&lt;<span class="hljs-name">Button</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"voteBtn info"</span>
          <span class="hljs-attr">variant</span>=<span class="hljs-string">{state.voteBtnStyle}</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
        &gt;</span>
          {state.voteBtnText}
        <span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);
</code></pre>
<p>We are done with the <code>PollQuestion</code> component. You can find the complete code for <code>PollQuestion</code> in this <a target="_blank" href="https://gist.github.com/catalinpit/74a64bcb9a6af13364ea1ebf8aa61729">gist</a>.</p>
<h4 id="heading-poll-component">Poll Component</h4>
<p>The <code>Poll</code> component renders the poll question and the results. Let's start by calling the <code>useQuery</code> hook with the GraphQL Query string that returns all the polls.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Poll = <span class="hljs-function">(<span class="hljs-params">{ userId }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { data, loading, error } = useQuery(QUERY_GET_POLL);

    <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loading</span> /&gt;</span></span>;
    <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Error</span> <span class="hljs-attr">message</span>=<span class="hljs-string">{error.message}</span> /&gt;</span></span>;
};
</code></pre>
<p>The <code>data</code> property will contain an array with polls if the query is successful. Once we have the array, we map through it and render the polls and their corresponding answers.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
      {data?.poll.map((poll) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{poll.id}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"pollWrapper wd100"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"displayFlex"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-md-4 pollSlider"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">PollQuestion</span> <span class="hljs-attr">poll</span>=<span class="hljs-string">{poll}</span> <span class="hljs-attr">userId</span>=<span class="hljs-string">{userId}</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-md-8 pollresult"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Result</span> <span class="hljs-attr">pollId</span>=<span class="hljs-string">{poll.id}</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);
</code></pre>
<p>If you look at the above code, you can observe that we use the <code>Result</code> component, which does not exist yet. In the next step, we will do just that!</p>
<p>You can find the complete code for the <code>Poll</code> file in this <a target="_blank" href="https://gist.github.com/catalinpit/f8015f660984f7f1997e3b8caedf6085">gist</a>.</p>
<h3 id="heading-result-component">Result Component</h3>
<p>Let's start by creating the <code>Result.jsx</code> in the <code>src</code> folder.</p>
<pre><code>📂 realtime-poll
 └ 📁 node_modules
   📁 package-lock.json
   📁 package.json
   📁 public
   📁 README.md
   📂 src
    └ Result.jsx
</code></pre><p>Open the file and add the following imports:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useSubscription } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Chart } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-google-charts"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-built_in">Error</span>, Loading } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Components"</span>;
<span class="hljs-keyword">import</span> { SUBSCRIPTION_RESULT } <span class="hljs-keyword">from</span> <span class="hljs-string">"./GraphQL"</span>;
</code></pre>
<p>The first line imports the <code>useSubscription</code> hook, which we will use to display the poll results in real-time. On the second line, we import React and on the third line we import the Chart component. The last two lines import two custom components and the GraphQL Subscription string.</p>
<p>Write the following code after the imports:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Result = <span class="hljs-function">(<span class="hljs-params">{ pollId }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { data, loading, error } = useSubscription(SUBSCRIPTION_RESULT, {
      <span class="hljs-attr">variables</span>: { pollId },
    });

    <span class="hljs-keyword">const</span> hasResults = data?.poll_results.length &gt; <span class="hljs-number">0</span>;

    <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loading</span> /&gt;</span></span>;
    <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Error</span> <span class="hljs-attr">message</span>=<span class="hljs-string">{error.message}</span> /&gt;</span></span>;

    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {hasResults ? <span class="hljs-tag">&lt;<span class="hljs-name">PollChart</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{data?.poll_results}</span> /&gt;</span> : <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>No result<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};
</code></pre>
<p>The "Result" component takes a poll ID as a prop so it can display the results for that specific poll.</p>
<p>In the first line, we call the <code>useSubscription</code> hook with the poll ID. If the call is successful, the <code>data</code> property will contain an array with the poll results. Also, all the new votes will be reflected in the <code>data</code> property. The array with the poll results updates each time a new vote is submitted.</p>
<p>Before displaying the poll results, we check if there are any results. If there are, we display the results. If not, we show a "No result" string.</p>
<p>If you look at the code, you can see that we use a <code>PollChart</code> component. You can find the code for <code>PollChart</code> and the complete code for <code>Result.jsx</code> in this <a target="_blank" href="https://gist.github.com/catalinpit/ede866d28e62928f58904447d9d4ba36">gist</a>.</p>
<p>The article focuses on the essential parts of the frontend implementation. It highlights how to implement the trickier bits. You can browse the complete application code in this <a target="_blank" href="https://github.com/catalinpit/graphql-engine/tree/master/community/sample-apps/realtime-poll">GitHub Repository</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>At this point, you have a full-stack application without writing any backend code. Hasura provides a helpful user interface that you can use to build your API. As a result, it simplifies and shortens the process of building a GraphQL API.</p>
<p>You can:</p>
<ul>
<li>see see a <a target="_blank" href="https://realtime-poll.demo.hasura.io/">live demo</a> of the application</li>
<li>explore the <a target="_blank" href="https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Frealtime-poll.hasura.app/v1/graphql">backend</a></li>
<li>browse the complete code in this <a target="_blank" href="https://github.com/catalinpit/graphql-engine/tree/master/community/sample-apps/realtime-poll">GitHub Repository</a></li>
</ul>
<p>If you are interested, I also wrote about <a target="_blank" href="https://catalins.tech/hasura-ecommerce-backend">building an e-commerce backend</a> with minimal code. </p>
<p>Thanks for reading! If you want to keep in touch, let's connect on Twitter <a target="_blank" href="https://twitter.com/intent/follow?screen_name=catalinmpit">@catalinmpit</a>. I also publish articles regularly on my blog <a target="_blank" href="https://catalins.tech">catalins.tech</a> if you want to read more content from me.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Chat App UI With Flutter and Dart ]]>
                </title>
                <description>
                    <![CDATA[ By Krissanawat These days, many people use chat applications to communicate with team members, friends, and family via their smart phones. This makes these messaging applications an essential medium of communication.  There is also high demand for in... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-chat-app-ui-with-flutter/</link>
                <guid isPermaLink="false">66d46015a326133d12440a25</guid>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 20 Jan 2021 22:14:57 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/6006d9e30a2838549dcb4bb3.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Krissanawat</p>
<p>These days, many people use chat applications to communicate with team members, friends, and family via their smart phones. This makes these messaging applications an essential medium of communication. </p>
<p>There is also high demand for intuitive and powerful user interfaces with state of the art features. The user interface (or UI) is the most impactful aspect of the overall user experience, so it's important to get right.</p>
<p>Flutter app development has taken the world by storm in terms of cross-platform mobile application development. You can use it to create pixel-perfect UIs, and many development companies use Flutter today. </p>
<p>In this tutorial, I am going to introduce you to a mix of both: we're going to build a chat app UI entirely on the Flutter/Dart coding environment. Along with learning the awesome Chat UI implementation in Flutter, we will also learn how its coding workflows and structures work.</p>
<p>So, let's get started!</p>
<h2 id="heading-how-to-create-a-new-flutter-project">How to Create a New Flutter Project</h2>
<p>First, we need to create a new Flutter project. For that, make sure that you've installed the Flutter SDK and other Flutter app development-related requirements. </p>
<p>If everything is properly set up, then in order to create a project we can simply run the following command in our desired local directory:</p>
<pre><code class="lang-bash">flutter create ChatApp
</code></pre>
<p>After we've set up the project, we can navigate inside the project directory and execute the following command in the terminal to run the project in either an available emulator or an actual device:</p>
<pre><code class="lang-bash">flutter run
</code></pre>
<p>After it's been successfully built, we will get the following result in the emulator screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-105.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-create-the-main-home-screen-ui"><strong>How to Create the Main Home Screen UI</strong></h2>
<p>Now, we are going to start building the UI for our chat application. The Main Home Screen will contain 2 sections: </p>
<ul>
<li>the conversation screen, which we are going to implement as a separate page, and </li>
<li>a bottom navigation bar.</li>
</ul>
<p>First, we need to make some simple configurations to the default boilerplate code in the <strong>main.dart</strong> file. We'll remove some default code and add the simple <code>MaterialApp</code> pointing to the empty <code>Container</code> as a home page for now:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(MyApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'Flutter Demo'</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
      home: Container(),
    );
  }
}
</code></pre>
<p>Now in place of the empty Container widget, we are going to call the <code>HomePage</code> screen widget. But first, we need to implement the screen.</p>
<h3 id="heading-how-to-build-the-main-home-screen">How to Build the Main Home Screen</h3>
<p>Inside the <strong>./lib</strong> directory of our root project folder, we need to create a folder called <strong>./screens</strong>. This folder will hold all the dart files for different screens.</p>
<p>Inside <strong>./lib/screens/</strong> directory, we need to create a file called <strong>homePage.dart</strong>. Inside the <strong>homePage.dart</strong> file, we need to add the basic Stateless widget code as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span></span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: Container(
        child: Center(child: Text(<span class="hljs-string">"Chat"</span>)),
      ),

    );
  }
}
</code></pre>
<p>Now, we need to call the <code>HomePage</code> class widget in the <strong>main.dart</strong> file as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'Flutter Demo'</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
      home: HomePage(),
    );
  }
}
</code></pre>
<p>Now we will get the result as shown in the emulator screenshot below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-106.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-the-bottom-navigator-bar">How to Build the Bottom Navigator Bar</h3>
<p>Now, we are going to place a Bottom Navigation menu on the <code>HomePage</code> screen. For that, we are going to use the <code>BottomNavigationBar</code> widget in the <code>bottomNavigationBar</code> parameter provided by the <code>Scaffold</code> widget. </p>
<p>Here's the overall code implementation:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">return</span> Scaffold(
      body: Container(
        child: Center(child: Text(<span class="hljs-string">"Chat"</span>)),
      ),
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Colors.red,
        unselectedItemColor: Colors.grey.shade600,
        selectedLabelStyle: TextStyle(fontWeight: FontWeight.w600),
        unselectedLabelStyle: TextStyle(fontWeight: FontWeight.w600),
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.message),
            title: Text(<span class="hljs-string">"Chats"</span>),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.group_work),
            title: Text(<span class="hljs-string">"Channels"</span>),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.account_box),
            title: Text(<span class="hljs-string">"Profile"</span>),
          ),
        ],
      ),
    );
</code></pre>
<p>Here, we have configured <code>BottomNavigationBar</code> with various style parameters and kept our Navigation menu item in the <code>items</code> parameter. For the <code>body</code> parameter, we have just used a simple <code>Container</code> with a <code>Text</code> widget.</p>
<p>Now we will get the bottom navbar as shown in the emulator screenshot below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-107.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that the bottom navigation is done, we can go ahead and implement the conversation list section just above the bottom navbar.</p>
<h2 id="heading-how-to-build-the-conversation-list-screen"><strong>How to Build the Conversation List Screen</strong></h2>
<p>Here, we are going to create the conversation list section which will contain a header section, a search bar, and the conversation list view.</p>
<p>First, inside the <strong>./lib/screens</strong> folder, we need to create a new dart file called <strong>chatPage.dart</strong>. Then, add a simple stateful widget class template inside as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _ChatPageState createState() =&gt; _ChatPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ChatPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ChatPage</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: SingleChildScrollView(
        child: Center(child: Text(<span class="hljs-string">"Chat"</span>)),
      ),
    );
  }
}
</code></pre>
<p>Now, we need to call the <code>chatPage</code> class widget in place of the <code>Container</code> widget in <strong>homePage.dart</strong> as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">return</span> Scaffold(
      body: ChatPage(),
      bottomNavigationBar: BottomNavigationBar(
</code></pre>
<p>This will give us the following result as shown in the emulator below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-108.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-a-conversation-list-page-header">How to Build a Conversation List Page Header</h3>
<p>Now, we are going to add the header to the Conversation list section which will have a text header and a button. The complete UI implementation code is provided in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">return</span> Scaffold(
      body: SingleChildScrollView(
        physics: BouncingScrollPhysics(),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: &lt;Widget&gt;[
            SafeArea(
              child: Padding(
                padding: EdgeInsets.only(left: <span class="hljs-number">16</span>,right: <span class="hljs-number">16</span>,top: <span class="hljs-number">10</span>),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: &lt;Widget&gt;[
                    Text(<span class="hljs-string">"Conversations"</span>,style: TextStyle(fontSize: <span class="hljs-number">32</span>,fontWeight: FontWeight.bold),),
                    Container(
                      padding: EdgeInsets.only(left: <span class="hljs-number">8</span>,right: <span class="hljs-number">8</span>,top: <span class="hljs-number">2</span>,bottom: <span class="hljs-number">2</span>),
                      height: <span class="hljs-number">30</span>,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(<span class="hljs-number">30</span>),
                        color: Colors.pink[<span class="hljs-number">50</span>],
                      ),
                      child: Row(
                        children: &lt;Widget&gt;[
                          Icon(Icons.add,color: Colors.pink,size: <span class="hljs-number">20</span>,),
                          SizedBox(width: <span class="hljs-number">2</span>,),
                          Text(<span class="hljs-string">"Add New"</span>,style: TextStyle(fontSize: <span class="hljs-number">14</span>,fontWeight: FontWeight.bold),),
                        ],
                      ),
                    )
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
</code></pre>
<p>Here, we have used the <code>SingleChildScrollView</code> so that the body section of the <strong>chatPage.dart</strong> is entirely scrollable. </p>
<p>Then, we have used the <code>BouncingScrollPhysics</code> instance to give the bouncing effect when a user's scrolling reaches the end or beginning. </p>
<p>Next, we have added a Text widget and a Container to display the bottom at the right-hand side. </p>
<p>Lastly, we have a <code>Column</code> widget as a child of the <code>SingleChildScrollView</code> widget so everything will appear vertically on the screen.</p>
<p>This will give us the following result as shown in the emulator below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-109.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, we are going to add a search bar just below the Header section.</p>
<h3 id="heading-how-to-add-the-search-bar"><strong>How to Add the Search Bar</strong></h3>
<p>In the <code>Column</code> widget from before, we are going to add a Search bar widget just below the Header UI section. So, as a second child of the <code>Column</code> widget, we need to insert the following code provided in the code snippet below:</p>
<pre><code class="lang-dart">Padding(
  padding: EdgeInsets.only(top: <span class="hljs-number">16</span>,left: <span class="hljs-number">16</span>,right: <span class="hljs-number">16</span>),
  child: TextField(
    decoration: InputDecoration(
      hintText: <span class="hljs-string">"Search..."</span>,
      hintStyle: TextStyle(color: Colors.grey.shade600),
      prefixIcon: Icon(Icons.search,color: Colors.grey.shade600, size: <span class="hljs-number">20</span>,),
      filled: <span class="hljs-keyword">true</span>,
      fillColor: Colors.grey.shade100,
      contentPadding: EdgeInsets.all(<span class="hljs-number">8</span>),
      enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(<span class="hljs-number">20</span>),
          borderSide: BorderSide(
              color: Colors.grey.shade100
          )
      ),
    ),
  ),
),
</code></pre>
<p>This will give us the following result as shown in the emulator below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-110.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-the-conversation-list"><strong>How to Build the Conversation List</strong></h3>
<p>Now that we have the Header section and a Search bar, we are going to implement the conversation list section.</p>
<p>For that, we need to implement a class object model to store the instances of conversation lists. </p>
<p>So inside the <strong>./lib</strong> folder, we need to create a new folder called <strong>./models</strong>. Inside <strong>./models</strong>, we need to create a file called <strong>chatUsersModel.dart</strong>. </p>
<p>In the model file, we need to create a model object class as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/cupertino.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatUsers</span></span>{
  <span class="hljs-built_in">String</span> name;
  <span class="hljs-built_in">String</span> messageText;
  <span class="hljs-built_in">String</span> imageURL;
  <span class="hljs-built_in">String</span> time;
  ChatUsers({<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.name,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.messageText,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.imageURL,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.time});
}
</code></pre>
<p>The object will house the user's name, text message, image URL, and time.</p>
<p>Next we need to create a list of users inside the <strong>chatPage.dart</strong> as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ChatPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ChatPage</span>&gt; </span>{
  <span class="hljs-built_in">List</span>&lt;ChatUsers&gt; chatUsers = [
    ChatUsers(text: <span class="hljs-string">"Jane Russel"</span>, secondaryText: <span class="hljs-string">"Awesome Setup"</span>, image: <span class="hljs-string">"images/userImage1.jpeg"</span>, time: <span class="hljs-string">"Now"</span>),
    ChatUsers(text: <span class="hljs-string">"Glady's Murphy"</span>, secondaryText: <span class="hljs-string">"That's Great"</span>, image: <span class="hljs-string">"images/userImage2.jpeg"</span>, time: <span class="hljs-string">"Yesterday"</span>),
    ChatUsers(text: <span class="hljs-string">"Jorge Henry"</span>, secondaryText: <span class="hljs-string">"Hey where are you?"</span>, image: <span class="hljs-string">"images/userImage3.jpeg"</span>, time: <span class="hljs-string">"31 Mar"</span>),
    ChatUsers(text: <span class="hljs-string">"Philip Fox"</span>, secondaryText: <span class="hljs-string">"Busy! Call me in 20 mins"</span>, image: <span class="hljs-string">"images/userImage4.jpeg"</span>, time: <span class="hljs-string">"28 Mar"</span>),
    ChatUsers(text: <span class="hljs-string">"Debra Hawkins"</span>, secondaryText: <span class="hljs-string">"Thankyou, It's awesome"</span>, image: <span class="hljs-string">"images/userImage5.jpeg"</span>, time: <span class="hljs-string">"23 Mar"</span>),
    ChatUsers(text: <span class="hljs-string">"Jacob Pena"</span>, secondaryText: <span class="hljs-string">"will update you in evening"</span>, image: <span class="hljs-string">"images/userImage6.jpeg"</span>, time: <span class="hljs-string">"17 Mar"</span>),
    ChatUsers(text: <span class="hljs-string">"Andrey Jones"</span>, secondaryText: <span class="hljs-string">"Can you please share the file?"</span>, image: <span class="hljs-string">"images/userImage7.jpeg"</span>, time: <span class="hljs-string">"24 Feb"</span>),
    ChatUsers(text: <span class="hljs-string">"John Wick"</span>, secondaryText: <span class="hljs-string">"How are you?"</span>, image: <span class="hljs-string">"images/userImage8.jpeg"</span>, time: <span class="hljs-string">"18 Feb"</span>),
  ];
</code></pre>
<p>Now that we have the mock users' conversation list data, we can apply it to the conversation list to create a list view.</p>
<h3 id="heading-how-to-make-a-separate-class-widget-for-individual-conversation"><strong>How to Make a Separate Class Widget for Individual Conversation</strong></h3>
<p>Here, we are going to make a separate component widget for the individual items in the conversation list view.</p>
<p>For that, inside <strong>./lib</strong> create a folder called <strong>./widgets</strong>. And inside <strong>./widgets</strong> we need to create a file called <strong>conversationList.dart.</strong> Inside the new widget file, we can use the code from the following code snippet:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConversationList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span></span>{
  <span class="hljs-built_in">String</span> name;
  <span class="hljs-built_in">String</span> messageText;
  <span class="hljs-built_in">String</span> imageUrl;
  <span class="hljs-built_in">String</span> time;
  <span class="hljs-built_in">bool</span> isMessageRead;
  ConversationList({<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.name,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.messageText,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.imageUrl,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.time,<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.isMessageRead});
  <span class="hljs-meta">@override</span>
  _ConversationListState createState() =&gt; _ConversationListState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ConversationListState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ConversationList</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> GestureDetector(
      onTap: (){
      },
      child: Container(
        padding: EdgeInsets.only(left: <span class="hljs-number">16</span>,right: <span class="hljs-number">16</span>,top: <span class="hljs-number">10</span>,bottom: <span class="hljs-number">10</span>),
        child: Row(
          children: &lt;Widget&gt;[
            Expanded(
              child: Row(
                children: &lt;Widget&gt;[
                  CircleAvatar(
                    backgroundImage: NetworkImage(widget.imageUrl),
                    maxRadius: <span class="hljs-number">30</span>,
                  ),
                  SizedBox(width: <span class="hljs-number">16</span>,),
                  Expanded(
                    child: Container(
                      color: Colors.transparent,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: &lt;Widget&gt;[
                          Text(widget.name, style: TextStyle(fontSize: <span class="hljs-number">16</span>),),
                          SizedBox(height: <span class="hljs-number">6</span>,),
                          Text(widget.messageText,style: TextStyle(fontSize: <span class="hljs-number">13</span>,color: Colors.grey.shade600, fontWeight: widget.isMessageRead?FontWeight.bold:FontWeight.normal),),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            ),
            Text(widget.time,style: TextStyle(fontSize: <span class="hljs-number">12</span>,fontWeight: widget.isMessageRead?FontWeight.bold:FontWeight.normal),),
          ],
        ),
      ),
    );
  }
}
</code></pre>
<p>This widget file takes the user's name, text message, image URL, time, and a Boolean message type value as parameters. And it returns the template containing the values.</p>
<p>In the <strong>chatPage.dart,</strong> inside the <code>ListView</code> widget, we need to call the <code>ConversationList</code> widget by passing the required parameters as shown in the code snippet below:</p>
<pre><code class="lang-dart">ListView.builder(
  itemCount: chatUsers.length,
  shrinkWrap: <span class="hljs-keyword">true</span>,
  padding: EdgeInsets.only(top: <span class="hljs-number">16</span>),
  physics: NeverScrollableScrollPhysics(),
  itemBuilder: (context, index){
    <span class="hljs-keyword">return</span> ConversationList(
      name: chatUsers[index].name,
      messageText: chatUsers[index].messageText,
      imageUrl: chatUsers[index].imageURL,
      time: chatUsers[index].time,
      isMessageRead: (index == <span class="hljs-number">0</span> || index == <span class="hljs-number">3</span>)?<span class="hljs-keyword">true</span>:<span class="hljs-keyword">false</span>,
    );
  },
),
</code></pre>
<p>Note that this <code>ListView</code> widget is to keep as the first child of the <code>Column</code> widget in the <code>chatPage</code> screen.</p>
<p>This will give us the following result as shown in the emulator below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-111.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This completes the UI implementation of our Conversation List Screen and Main Home page screen as a whole. Now, we'll move on to the implementation of the chat details screen.</p>
<h2 id="heading-how-to-build-a-chat-detail-screen"><strong>How to Build a Chat Detail Screen</strong></h2>
<p>Now, we are going to create a chat detail screen. For that, we need to create a new file called <strong>chatDetailPage.dart</strong> inside the <strong>./lib/screens/</strong> folder. For now, we are just going to add the basic code as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatDetailPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span></span>{
  <span class="hljs-meta">@override</span>
  _ChatDetailPageState createState() =&gt; _ChatDetailPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ChatDetailPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ChatDetailPage</span>&gt; </span>{

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(<span class="hljs-string">"Chat Detail"</span>),
      ),
      body: Container()
    );
  }
}
</code></pre>
<p>Here, we have returned a basic <code>AppBar</code> with Text and an empty Container as the <code>body</code> of the <code>Scaffold</code> widget.</p>
<p>Now, we are going to add navigation to <code>ChatDetailPage</code> on the <code>GestureHandler</code> widget's <code>onTap</code> method in the <strong>conversationList.dart</strong> widget file as shown in the code snippet below:</p>
<pre><code class="lang-dart">GestureDetector(
      onTap: (){
        Navigator.push(context, MaterialPageRoute(builder: (context){
          <span class="hljs-keyword">return</span> ChatDetailPage();
        }));
      },
</code></pre>
<p>We can now navigate to the Chat detail screen as shown in the emulator demo below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/2021-01-20-13.40.11.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-a-custom-app-bar-for-chat-detail-screen">How to Build a Custom App Bar for Chat Detail Screen</h3>
<p>Here, we are going to add a custom App bar at the top of the chat detail screen. For that, we are going to use the <code>AppBar</code> widget with various parameter configurations as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        elevation: <span class="hljs-number">0</span>,
        automaticallyImplyLeading: <span class="hljs-keyword">false</span>,
        backgroundColor: Colors.white,
        flexibleSpace: SafeArea(
          child: Container(
            padding: EdgeInsets.only(right: <span class="hljs-number">16</span>),
            child: Row(
              children: &lt;Widget&gt;[
                IconButton(
                  onPressed: (){
                    Navigator.pop(context);
                  },
                  icon: Icon(Icons.arrow_back,color: Colors.black,),
                ),
                SizedBox(width: <span class="hljs-number">2</span>,),
                CircleAvatar(
                  backgroundImage: NetworkImage(<span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/5.jpg&gt;"</span>),
                  maxRadius: <span class="hljs-number">20</span>,
                ),
                SizedBox(width: <span class="hljs-number">12</span>,),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: &lt;Widget&gt;[
                      Text(<span class="hljs-string">"Kriss Benwat"</span>,style: TextStyle( fontSize: <span class="hljs-number">16</span> ,fontWeight: FontWeight.w600),),
                      SizedBox(height: <span class="hljs-number">6</span>,),
                      Text(<span class="hljs-string">"Online"</span>,style: TextStyle(color: Colors.grey.shade600, fontSize: <span class="hljs-number">13</span>),),
                    ],
                  ),
                ),
                Icon(Icons.settings,color: Colors.black54,),
              ],
            ),
          ),
        ),
      ),
      body: Container()
    );
</code></pre>
<p>This will give us the following result as shown in the emulator below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-112.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-implement-the-bottom-text-box"><strong>How to Implement the Bottom Text Box</strong></h3>
<p>At the bottom of the chat detail screen, we need to add a messaging section that will contain a text editor and a button to send the message. </p>
<p>For this, we are going to use the <code>Align</code> widget and align the child inside the widget with the bottom of the screen. The overall code is provided in the code snippet below:</p>
<pre><code class="lang-dart">body: Stack(
        children: &lt;Widget&gt;[
          Align(
            alignment: Alignment.bottomLeft,
            child: Container(
              padding: EdgeInsets.only(left: <span class="hljs-number">10</span>,bottom: <span class="hljs-number">10</span>,top: <span class="hljs-number">10</span>),
              height: <span class="hljs-number">60</span>,
              width: <span class="hljs-built_in">double</span>.infinity,
              color: Colors.white,
              child: Row(
                children: &lt;Widget&gt;[
                  GestureDetector(
                    onTap: (){
                    },
                    child: Container(
                      height: <span class="hljs-number">30</span>,
                      width: <span class="hljs-number">30</span>,
                      decoration: BoxDecoration(
                        color: Colors.lightBlue,
                        borderRadius: BorderRadius.circular(<span class="hljs-number">30</span>),
                      ),
                      child: Icon(Icons.add, color: Colors.white, size: <span class="hljs-number">20</span>, ),
                    ),
                  ),
                  SizedBox(width: <span class="hljs-number">15</span>,),
                  Expanded(
                    child: TextField(
                      decoration: InputDecoration(
                        hintText: <span class="hljs-string">"Write message..."</span>,
                        hintStyle: TextStyle(color: Colors.black54),
                        border: InputBorder.none
                      ),
                    ),
                  ),
                  SizedBox(width: <span class="hljs-number">15</span>,),
                  FloatingActionButton(
                    onPressed: (){},
                    child: Icon(Icons.send,color: Colors.white,size: <span class="hljs-number">18</span>,),
                    backgroundColor: Colors.blue,
                    elevation: <span class="hljs-number">0</span>,
                  ),
                ],

              ),
            ),
          ),
        ],
      ),
</code></pre>
<p>This will give us a messaging section with a text field to type the messages and a button to send the messages:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-113.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We also have a button to the left which we can use to add other menu options for messaging.</p>
<h3 id="heading-how-to-set-up-the-messages-list-section-in-chat-screen"><strong>How to Set Up the Messages List Section in Chat Screen</strong></h3>
<p>Now, we are going to create the UI for messages appearing in the chat detail screen.</p>
<p>First, we need to create a model that reflects the message instance object.</p>
<p>For that, we need to create a file called <strong>chatMessageModel.dart</strong> inside the <strong>./models</strong> folder and define the class object as follows:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/cupertino.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatMessage</span></span>{
  <span class="hljs-built_in">String</span> messageContent;
  <span class="hljs-built_in">String</span> messageType;
  ChatMessage({<span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.messageContent, <span class="hljs-meta">@required</span> <span class="hljs-keyword">this</span>.messageType});
}
</code></pre>
<p>The class object accepts the message content and message type (whether sender or receiver as instance values.</p>
<p>Now in the <strong>chatDetailPage.dart</strong>, we need to create a list of messages to display as shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-built_in">List</span>&lt;ChatMessage&gt; messages = [
    ChatMessage(messageContent: <span class="hljs-string">"Hello, Will"</span>, messageType: <span class="hljs-string">"receiver"</span>),
    ChatMessage(messageContent: <span class="hljs-string">"How have you been?"</span>, messageType: <span class="hljs-string">"receiver"</span>),
    ChatMessage(messageContent: <span class="hljs-string">"Hey Kriss, I am doing fine dude. wbu?"</span>, messageType: <span class="hljs-string">"sender"</span>),
    ChatMessage(messageContent: <span class="hljs-string">"ehhhh, doing OK."</span>, messageType: <span class="hljs-string">"receiver"</span>),
    ChatMessage(messageContent: <span class="hljs-string">"Is there any thing wrong?"</span>, messageType: <span class="hljs-string">"sender"</span>),
  ];
</code></pre>
<p>Next, we are going to create a list view for the messages on top of the <code>Stack</code> widget's children, above the <code>Align</code> widget, as shown in the code snippet below:</p>
<pre><code class="lang-dart">body: Stack(
        children: &lt;Widget&gt;[
          ListView.builder(
            itemCount: messages.length,
            shrinkWrap: <span class="hljs-keyword">true</span>,
            padding: EdgeInsets.only(top: <span class="hljs-number">10</span>,bottom: <span class="hljs-number">10</span>),
            physics: NeverScrollableScrollPhysics(),
            itemBuilder: (context, index){
              <span class="hljs-keyword">return</span> Container(
                padding: EdgeInsets.only(left: <span class="hljs-number">16</span>,right: <span class="hljs-number">16</span>,top: <span class="hljs-number">10</span>,bottom: <span class="hljs-number">10</span>),
                child: Text(messages[index].messageContent),
              );
            },
          ),
          Align(
</code></pre>
<p>Now the messages will appear in list form as shown in the emulator screenshot below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-114.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, we have the messages appearing on the screen but they are not styled the way we want in the chatting screen.</p>
<h3 id="heading-how-to-style-and-position-the-messages-based-on-sender-and-receiver">How to Style and Position the Messages Based on Sender and Receiver</h3>
<p>Now, we are going to style the message list so that it appears as a chat message bubble. We're also going to position them based on the message type using the <code>Align</code> widget as shown in the code snippet below:</p>
<pre><code class="lang-dart">ListView.builder(
  itemCount: messages.length,
  shrinkWrap: <span class="hljs-keyword">true</span>,
  padding: EdgeInsets.only(top: <span class="hljs-number">10</span>,bottom: <span class="hljs-number">10</span>),
  physics: NeverScrollableScrollPhysics(),
  itemBuilder: (context, index){
    <span class="hljs-keyword">return</span> Container(
      padding: EdgeInsets.only(left: <span class="hljs-number">14</span>,right: <span class="hljs-number">14</span>,top: <span class="hljs-number">10</span>,bottom: <span class="hljs-number">10</span>),
      child: Align(
        alignment: (messages[index].messageType == <span class="hljs-string">"receiver"</span>?Alignment.topLeft:Alignment.topRight),
        child: Container(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(<span class="hljs-number">20</span>),
            color: (messages[index].messageType  == <span class="hljs-string">"receiver"</span>?Colors.grey.shade200:Colors.blue[<span class="hljs-number">200</span>]),
          ),
          padding: EdgeInsets.all(<span class="hljs-number">16</span>),
          child: Text(messages[index].messageContent, style: TextStyle(fontSize: <span class="hljs-number">15</span>),),
        ),
      ),
    );
  },
),
</code></pre>
<p>This will give us the following result as shown in the emulator below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-115.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can see an overall demo of the app's entire UI below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/2021-01-20-13.52.45.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Congrats! We have built an intuitive and modern chat app UI entirely in the Flutter and Dart ecosystem.</p>
<h2 id="heading-recap">Recap</h2>
<p>Social messaging applications are an essential communication medium nowadays. Equipped with state of the art and powerful chat interfaces with audio and video calling, image and file attachments, and many more, these chat applications have made communication much more efficient. These apps have made the world smaller for us. </p>
<p>The major objective of this article was to show you how to develop a simple intuitive UI for chat applications with a modern design in the Flutter ecosystem. The step-by-step implementation provided a detailed showcase of the app's UI and also gave an overview of the Flutter coding environment. </p>
<p>I hope this tutorial helps you create your next chat application using Flutter.</p>
<p>You can also get inspiration for a chat app UI and feature development from top <a target="_blank" href="https://www.instaflutter.com/app-templates/flutter-chat-app/">Flutter chat app</a> templates out there in the market. Make sure to check them out as well.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ More project ideas to improve your coding skills ]]>
                </title>
                <description>
                    <![CDATA[ By Florin Pop https://www.youtube.com/watch?v=TNzCfgwIDCY Two weeks ago I published an article containing 15 project ideas that you can build to level up your coding skills, and people were very excited about that resource. Also, the app-ideas reposi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/more-project-ideas-to-improve-your-coding-skills-99f48d09bb4b/</link>
                <guid isPermaLink="false">66d45f0f182810487e0ce196</guid>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 18 Apr 2019 18:42:43 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*hL2XRi5uD_N7Pr8T.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Florin Pop</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/TNzCfgwIDCY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Two weeks ago I published an article containing <a target="_blank" href="https://medium.freecodecamp.org/here-are-some-app-ideas-you-can-build-to-level-up-your-coding-skills-39618291f672">15 project ideas</a> that you can build to level up your coding skills, and people were very excited about that resource.</p>
<p>Also, the <a target="_blank" href="https://github.com/florinpop17/app-ideas">app-ideas</a> repository has gotten almost 3000 stars since I published that article. That’s insane! ?</p>
<p>Thank you all for that! ?</p>
<p>In this post we’ll go over some <strong>new</strong> projects that were added to the repository since then.</p>
<p>As a quick reminder, all of the projects are divided into three <em>tiers</em> based on the knowledge and experience required to complete them. Check out the <em>tiers</em> description in the repository.</p>
<p>Below you’ll find 2 <strong>Beginner</strong>, 4 <strong>Intermediate</strong> and 3 <strong>Advanced</strong> project ideas.</p>
<h3 id="heading-1-calculator">1. CALCULATOR</h3>
<p><strong>Tier:</strong> 1 — Beginner</p>
<p>Calculators are not only one of the most useful tools available, but they are also a great way to understand UI and event processing in an application. In this problem you will create a calculator that supports basic arithmetic calculations on integers.</p>
<p>The styling is up to you so use your imagination and get creative! You might also find it worth your time to experiment with the calculator app on your mobile device to better understand basic functionality and edge cases.</p>
<h4 id="heading-constraints">Constraints</h4>
<ul>
<li>You may not use the <code>eval()</code> function to execute calculations</li>
</ul>
<h4 id="heading-user-stories">User Stories</h4>
<ul>
<li>User can see a display showing the current number entered or the result of the last operation.</li>
<li>User can see an entry pad containing buttons for the digits 0–9, operations — ‘+’, ‘-’, ‘/’, and ‘=’, a ‘C’ button (for clear), and an ‘AC’ button (for clear all).</li>
<li>User can enter numbers as sequences up to 8 digits long by clicking on digits in the entry pad. Entry of any digits more than 8 will be ignored.</li>
<li>User can click on an operation button to display the result of that operation on: <em> the result of the preceeding operation and the last number entered OR </em> the last two numbers entered OR * the last number entered</li>
<li>User can click the ‘C’ button to clear the last number or the last operation. If the users last entry was an operation the display will be updated to the value that preceded it.</li>
<li>User can click the ‘AC’ button to clear all internal work areas and to set the display to 0.</li>
<li>User can see ‘ERR’ displayed if any operation would exceed the 8 digit maximum.</li>
</ul>
<h4 id="heading-bonus-features">Bonus features</h4>
<ul>
<li>User can click a ‘+/-’ button to change the sign of the number that is currently displayed.</li>
<li>User can see a decimal point (.) button on the entry pad that allows floating point numbers up to 3 places to be entered and operations to be carried out to the maximum number of decimal places entered for any one number.</li>
</ul>
<h4 id="heading-useful-links-and-resources">Useful links and resources</h4>
<ul>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Calculator">Calculator (Wikipedia)</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/">MDN</a></li>
</ul>
<h4 id="heading-example-projects">Example projects</h4>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/giana/embed/GJMBEv" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/mjijackson/embed/xOzyGX" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h3 id="heading-2-recipe-app">2. RECIPE APP</h3>
<p><strong>Tier:</strong> 1 — Beginner</p>
<p>You might not have realized this, but recipes are nothing more than culinary algorithms. Like programs, recipes are a series of imperative steps which, if followed correctly, result in a tasty dish.</p>
<p>The objective of the Recipe app is to help the user manage recipes in a way that will make them easy to follow.</p>
<h4 id="heading-constraints-1">Constraints</h4>
<ul>
<li>For the initial version of this app, the recipe data may be encoded as a JSON file. After implementing the initial version of this app you may expand on this to maintain recipes in a file or database.</li>
</ul>
<h4 id="heading-user-stories-1">User Stories</h4>
<ul>
<li>User can see a list of recipe titles</li>
<li>User can click a recipe title to display a recipe card containing the recipe title, meal type (breakfast, lunch, supper, or snack), number of people it serves, its difficulty level (beginner, intermediate, advanced), the list of ingredients (including their amounts), and the preparation steps.</li>
<li>User click a new recipe title to replace the current card with a new recipe.</li>
</ul>
<h4 id="heading-bonus-features-1">Bonus features</h4>
<ul>
<li>User can see a photo showing what the item looks like after it has been prepared.</li>
<li>User can search for a recipe not in the list of recipe titles by entering the meal name into a search box and clicking a ‘Search’ button. Any open source recipe API may be used as the source for recipes (see The MealDB below).</li>
<li>User can see a list of recipes matching the search terms</li>
<li>User can click the name of the recipe to display its recipe card.</li>
<li>User can see a warning message if no matching recipe was found.</li>
<li>User can click a ‘Save’ button on the cards for recipes located through the API to save a copy to this apps recipe file or database.</li>
</ul>
<h4 id="heading-useful-links-and-resources-1">Useful links and resources</h4>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">Using Fetch</a></li>
<li><a target="_blank" href="https://www.npmjs.com/package/axios">Axios</a></li>
<li><a target="_blank" href="https://www.themealdb.com/api.php">The MealDB API</a></li>
</ul>
<h4 id="heading-example-projects-1">Example projects</h4>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/eddyerburgh/embed/xVeJvB" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/inkblotty/embed/oxWRme" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h3 id="heading-3-drawing-app">3. DRAWING APP</h3>
<p><strong>Tier:</strong> 2 — Intermediate</p>
<p>Create digital artwork on a canvas on the web to share online and also export as images.</p>
<h4 id="heading-user-stories-2">User Stories</h4>
<ul>
<li>User can draw in a <code>canvas</code> using the mouse</li>
<li>User can change the color</li>
<li>User can change the size of the tool</li>
<li>User can press a button to clear the <code>canvas</code></li>
</ul>
<h4 id="heading-bonus-features-2">Bonus features</h4>
<ul>
<li>User can save the artwork as an image (<code>.png</code>, <code>.jpg</code>, etc format)</li>
<li>User can draw different shapes (<code>rectangle</code>, <code>circle</code>, <code>star</code>, etc)</li>
<li>User can share the artwork on social media</li>
</ul>
<h4 id="heading-useful-links-and-resources-2">Useful links and resources</h4>
<ul>
<li><a target="_blank" href="https://www.florin-pop.com/blog/2019/04/drawing-app-built-with-p5js/">Learn how to create a Drawing Application using p5js</a></li>
</ul>
<h4 id="heading-example-projects-2">Example projects</h4>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/FlorinPop17/embed/VNYyZQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/t0mm4rx/embed/dLowvZ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h3 id="heading-4-emoji-translator">4. EMOJI TRANSLATOR</h3>
<p><strong>Tier:</strong> 2 — Intermediate</p>
<p>Emojis have become the <em>lingua franca</em> of modern society. They are a fun and fast way to communicate, but an also extremely expressive mechanism for communicating emotions and reactions.</p>
<p>The objective of the Emoji Translator app is to translate text entered by the user into an equivalent string of emojis, translated from one or more words in the original text, and words for which there is no corresponding emoji.</p>
<h4 id="heading-user-stories-3">User Stories</h4>
<ul>
<li>User can enter a string of words, numbers, and punctuation into a text box</li>
<li>User can click a ‘Translate’ button to translate words in the entered text into their corresponding emojis.</li>
<li>User can see a warning message if ‘Translate’ was clicked, but the input text box was empty or unchanged from the last translation.</li>
<li>User can see text elements in the entered text translated to their equivalent emojis in an output text box. Text elements for which there is no emoji will be left unchanged.</li>
<li>User can click a ‘Clear’ button to clear the input and output text boxes.</li>
</ul>
<h4 id="heading-bonus-features-3">Bonus features</h4>
<ul>
<li>Developer will implement an emoji synonym feature to allow the app to translate a wider variety of words to emoji.</li>
<li>User can select the language the input text is entered from a dropdown list of languages.</li>
</ul>
<h4 id="heading-useful-links-and-resources-3">Useful links and resources</h4>
<p><a target="_blank" href="https://unicode.org/emoji/charts/full-emoji-list.html">Full Emoji List v12.0</a></p>
<h4 id="heading-example-projects-3">Example projects</h4>
<p><a target="_blank" href="https://emojitranslate.com/">Emoji Translate</a></p>
<h3 id="heading-5-meme-generator-app">5. MEME GENERATOR APP</h3>
<p><strong>Tier:</strong> 2 — Intermediate</p>
<p>Allow users to generate custom memes by adding text over an image.</p>
<h4 id="heading-user-stories-4">User Stories</h4>
<ul>
<li>User can upload an image that will appear in a canvas</li>
<li>User can add text in the top part of the image</li>
<li>User can add text in the bottom part of the image</li>
<li>User can select the color of the text</li>
<li>User can select the size of the text</li>
<li>User can save the resulting meme</li>
</ul>
<h4 id="heading-bonus-features-4">Bonus features</h4>
<ul>
<li>User can select the font-family for the text</li>
<li>User can share the meme on social media (twitter, reddit, facebook, etc)</li>
<li>User can drag the text around and place it wherever he wants on top of the image</li>
<li>User can draw shapes on top of the image (circles, rectangles, or free drawing with the mouse)</li>
</ul>
<h4 id="heading-useful-links-and-resources-4">Useful links and resources</h4>
<p>Working with canvas is made very easy by the <a target="_blank" href="http://p5js.org/">p5js</a> library.</p>
<h4 id="heading-example-projects-4">Example projects</h4>
<p><a target="_blank" href="https://imgflip.com/memegenerator">Meme Generator by imgflip</a></p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/ninivert/embed/BpLKRx" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h3 id="heading-6-typing-practice">6. TYPING PRACTICE</h3>
<p><strong>Tier:</strong> 2 — Intermediate</p>
<p>Some things are so obvious they can be easily overlooked. As a developer your ability to type quickly and accurately is one factor that influences your development productivity. The objective of the Typing Practice app is to provide you with typing practice along with metrics to allow you to measure your progress.</p>
<p>Typing practice displays a word which you must then type within a specific interval of time. If the word is incorrectly typed it stays on screen and the time interval remains the same. But when the word is correctly typed a new word is displayed and the time interval is slightly reduced.</p>
<p>Hopefully, this repetitive practice will help you improve both your typing speed and accuracy.</p>
<h4 id="heading-user-stories-5">User Stories</h4>
<ul>
<li>User can see the time interval words must be typed in displayed in the app window.</li>
<li>User can see the number of successful attempts and the number of total attempts in a score box.</li>
<li>User can click a ‘Start Practice’ button to start the practice session.</li>
<li>User can see the prompt word displayed in a text box.</li>
<li>User can begin typing the word in a text input box.</li>
<li>User can see the letters that have been typed flash if an incorrect letter is entered and the text input box will be cleared.</li>
<li>User can see the a message adjacent to the text input box indicating the user should try again if an incorrect letter is entered.</li>
<li>User can see the number of total attempts incremented in the score box.</li>
<li>User can see a congratulations message if the word is correctly typed.</li>
<li>User can see the time interval words must be typed decremented by a small amount if the word is correctly typed.</li>
<li>User can see the number of successful attempts incremented in the score box if the word was correctly typed.</li>
<li>User can click a ‘Stop Practice’ button to stop the practice session.</li>
</ul>
<h4 id="heading-bonus-features-5">Bonus features</h4>
<ul>
<li>User can hear a unique audible tone signaling when a new word is displayed, a word is correctly entered, or an incorrect letter is typed in the word.</li>
<li>User can login to the app</li>
<li>User can see cumulative performance statistics across all of his/her practice sessions.</li>
</ul>
<h4 id="heading-useful-links-and-resources-5">Useful links and resources</h4>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Events/keydown">keydown</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval</a></li>
</ul>
<h4 id="heading-example-projects-5">Example projects</h4>
<p><a target="_blank" href="http://twiddler.tekgear.com/tutor/twiddler.html">Twiddler Typing Tutor</a></p>
<h3 id="heading-7-elevator">7. ELEVATOR</h3>
<p><strong>Tier:</strong> 3 — Advanced</p>
<p>It’s tough to think of a world without elevators. Especially if you have to walk up and down 20 flights of stairs each day. But, if you think about it elevators were one of the original implementations of events and event handlers long before web applications came on the scene.</p>
<p>The objective of the Elevator app is to simulate the operation of an elevator and more importantly, how to handle the events generated when the buildings occupants use it. This app simulates occupants calling for an elevator from any floor and pressing the buttons inside the elevator to indicate the floor they wish to visit.</p>
<h4 id="heading-constraints-2">Constraints</h4>
<ul>
<li>You must implement a single event handler for the up and down buttons on each floor. For example, if there are 4 floors a single event handler should be implemented rather than 8 (two buttons per floor).</li>
<li>Similarly, a single event handler should be implemented for all buttons on the control panel in the elevator rather than a unique event handler for each button.</li>
</ul>
<h4 id="heading-user-stories-6">User Stories</h4>
<ul>
<li>User can see a cross section diagram of a building with four floors, an elevator shaft, the elevator, and an up button on the first floor, up and down buttons on the second and third floors, and a down button on the fourth floor.</li>
<li>User can see the elevator control panel with a button for each of the floors to the side of the diagram.</li>
<li>User can click the up and down button on any floor to call the elevator.</li>
<li>User can expect that clicking the up and down buttons on any floor to request the elevator will be queued and serviced in the sequence they were clicked.</li>
<li>User can see the elevator move up and down the shaft to the floor it was called to.</li>
<li>User can click the elevator control panel to select the floor it should travel to.</li>
<li>User can expect the elevator to pause for 5 seconds waiting for a floor button on the control panel to be clicked. If a floor button isn’t clicked within that time the elevator will process the next call request.</li>
<li>User can expect the elevator to return to the first floor when there are no requests to process.</li>
</ul>
<h4 id="heading-bonus-features-6">Bonus features</h4>
<ul>
<li>User can see a warning notification if the number of elevator requests exceeds the maximum number allowed. This limit is left to the developer.</li>
<li>User can hear a sound when the elevator arrives at a floor.</li>
<li>User can see an occupant randomly arrive at a floor to indicate when the user should click the up or down elevator call button on that floor.</li>
<li>User can specify the time interval between new occupants arriving to call an elevator.</li>
</ul>
<h4 id="heading-useful-links-and-resources-6">Useful links and resources</h4>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)">First-in, first out queue (Wikipedia)</a></p>
<h4 id="heading-example-projects-6">Example projects</h4>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/nibalAn/embed/prWdjq" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h3 id="heading-8-fast-food-simulator-app">8. FAST FOOD SIMULATOR APP</h3>
<p><strong>Tier:</strong> 3 — Advanced</p>
<p>Fast Food app simulates the operation of a simple take-away restaurant and is designed to help the developer put their knowledge of Promises and SOLID design principles to work.</p>
<p>This app simulates customers of a take-away restaurant placing orders and and waiting for them to be prepared and delivered to a pickup counter. After placing the order the customer waits on the order to be announced before picking it up and proceeding to the dining area.</p>
<p>The user stories that make up this app center around four distinct roles:</p>
<ul>
<li>User — the end user using the application</li>
<li>Customer — the simulated Customer</li>
<li>Order Taker — the simulated Order Taker</li>
<li>Cook — the simulated Cook</li>
<li>Server — the simulated Server</li>
</ul>
<p>This app has quite a few User Stories. Don’t be overwhelmed though. Take the time to sketch out not just the UI, but how the different actors (roles) interact and incrementally build the app following Agile principles.</p>
<h4 id="heading-constraints-3">Constraints</h4>
<ul>
<li>Order tickets can be represented as two different types of Promises — one the Server waits on while the Cook prepares the order and another the Customer waits on while in the serving line.</li>
<li>Use the native equivalent of JS Promises in whichever language you choose to develop in. JS developers should use native Promises and not <code>async/await</code>.</li>
<li>Create this app using native language features. You may NOT use a simulation package or library.</li>
<li>New customers arrive in the order line at a fixed interval of time. In other words, new customers arrive at a constant rate.</li>
<li>Order tickets are fulfilled at a fixed interval of time as well. They are completed at a constant rate.</li>
</ul>
<h4 id="heading-user-stories-7">User Stories</h4>
<p><strong>Application Operation</strong></p>
<ul>
<li>User can see an input area that allows the entry of the time interval for customer arrival and a time interval for the fulfillment of an <em>order ticket</em> by the cook.</li>
<li>User can see a customized warning message if the customer arrival interval or the order fulfillment interval is incorrectly entered.</li>
<li>User can start the simulation by clicking on a Start button.</li>
<li>User can see an order line area containing a text box showing the number of customers waiting to place orders.</li>
<li>User can see an order area containing text boxes showing the <em>order number</em> currently being taken.</li>
<li>User can see a kitchen area containing a text box showing the <em>order number</em> that’s being prepared and a text box listing the waiting orders in sequence, along with a count of the number of waiting orders.</li>
<li>User can see a Pickup area containing a text box showing the <em>order number</em> that’s currently available for pickup by the Customer and a text box showing the number of Customers waiting in the serving line.</li>
<li>User can stop the simulation at any time by clicking a Stop button.</li>
</ul>
<h4 id="heading-bonus-features-7">Bonus features</h4>
<ul>
<li>User can specify how long it takes for an Order Taker to create an <em>order ticket</em>.</li>
<li>User can specify how long it takes for the Server to deliver an order to the customer.</li>
<li>User can specify the total amount of time the simulation is to run once the Start button has been clicked.</li>
<li>User can see an animated view of Customers and orders as they move through the workflow.</li>
</ul>
<h4 id="heading-useful-links-and-resources-7">Useful links and resources</h4>
<ul>
<li><a target="_blank" href="https://drive.google.com/file/d/1Thfm5cFDm1OjTg_0LsIt2j1uPL5fv-Dh/view?usp=sharing">Fast Food Simulator — Logical Workflow</a></li>
<li><a target="_blank" href="http://agilemanifesto.org/">Agile Manifesto &amp; 12 Principles of Agile Software</a></li>
<li><a target="_blank" href="https://blog.bitsrc.io/solid-principles-every-developer-should-know-b3bfa96bb688">SOLID Principles Every Developer Should Know</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">Using Promises</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a></li>
</ul>
<h3 id="heading-9-shell-game">9. SHELL GAME</h3>
<p><strong>Tier:</strong> 3 — Advanced</p>
<p>A Shell game is a classic gambling game that dates back to ancient Greece. Playing it requires three shells, a pea, hand dexterity by the operator, and keen observation skills on the part of the player. It’s also a classic con game since it’s easy for the operator to swindle the player. Thank goodness the latter isn’t our intent with this app.</p>
<p>This Shell game is intended to provide a graphical interface to the classical shell game and to provide the player with an honest game to play. Our game draws three shells on the canvas along with the pea, moves the pea under one, of the shells, and shuffles the shells for a specific interval of time. The user must then click on the shell they think the pea is hidden under. The user is allowed to continue guessing until the pea is located.</p>
<h4 id="heading-user-stories-8">User Stories</h4>
<ul>
<li>User can see the canvas with three shells and the pea.</li>
<li>User can click the shell the pea is to be hidden under.</li>
<li>User can see the pea move under the shell thats been clicked.</li>
<li>User can click on a ‘Shuffle’ button to start an animated shuffling of the shells for 5 seconds.</li>
<li>User can click on the shell she believes the pea is hidden under when the animation stops.</li>
<li>User can see the shell that has been clicked rise to reveal if the pea is hidden under it.</li>
<li>User can continue clicking shells until the pea is found.</li>
<li>User can see a congratulations message when the pea is located</li>
<li>User can start a new game by clicking a shell the pea is to be hidden under (step #2 above). The steps above are then repeated.</li>
</ul>
<h4 id="heading-bonus-features-8">Bonus features</h4>
<ul>
<li>User can see a score panel containing the number of wins and the number of games played.</li>
<li>User can see the number of games played increase when the pea is hidden under a shell</li>
<li>User can see the number of wins incremented when the pea is found on the first guess.</li>
<li>User can see the shell hiding the pea to animate (color, size, or some other effect) when it is clicked (a correct guess).</li>
</ul>
<h4 id="heading-useful-links-and-resources-8">Useful links and resources</h4>
<ul>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Shell_game">Shell Game (Wikipedia)</a></li>
<li><a target="_blank" href="https://www.w3schools.com/js/js_htmldom_animate.asp">Javascript HTML DOM Animation</a></li>
<li><a target="_blank" href="https://p5js.org/">p5js Animation Library</a></li>
</ul>
<h4 id="heading-example-projects-7">Example projects</h4>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/RedCactus/embed/dwEjXy" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Don’t forget to check out the <a target="_blank" href="https://www.freecodecamp.org/news/here-are-some-app-ideas-you-can-build-to-level-up-your-coding-skills-39618291f672/">previous article</a> and the <a target="_blank" href="https://github.com/florinpop17/app-ideas">repository</a> if you want to find more application/project ideas.</p>
<p>Also, if the information from this article and from the repo was useful to you in any way, make sure you give it a star ?; this way others can find it and benefit too! Thank you!</p>
<p>Do you have any suggestions on how we could improve this project overall? Let us know! We’d love to hear your feedback!</p>
<p>You are welcomed to contribute with your own ideas! We can make this repository the go-to resource when it comes to app ideas.</p>
<p><em>Originally published at <a target="_blank" href="https://www.florin-pop.com/blog/2019/04/more-project-ideas-to-improve-your-coding-skills/">www.florin-pop.com</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
