<?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[ Facebook Messenger - 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[ Facebook Messenger - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:25:18 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/facebook-messenger/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Flutter UI Tutorial – How to Build a Chat App with Stories Using the Flutter SDK ]]>
                </title>
                <description>
                    <![CDATA[ By Krissanawat Chat applications have become one of the easiest ways to communicate over the internet. As such, many applications incorporate chat features in them so that users can interact and engage in social communications.  These applications ha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/flutter-messenger-clone/</link>
                <guid isPermaLink="false">66d4601b55db48792eed3f77</guid>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Facebook Messenger ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 11 Feb 2021 00:42:18 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/60228d650a2838549dcc1f0b.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Krissanawat</p>
<p>Chat applications have become one of the easiest ways to communicate over the internet. As such, many applications incorporate chat features in them so that users can interact and engage in social communications. </p>
<p>These applications have made the world smaller with their powerful end to end audio/video and text-based communication. On top of that, other features such as sharing stories, sending attachments, and more have made these apps even more engaging and useful. </p>
<p>Most apps also have a stories feature. It lets users share their experiences in brief and the story disappears after a certain amount of time. These features get users hooked to that app and make them want to share their thoughts, ideas, memories, and experiences.</p>
<p>Facebook Messenger is one of the most widely-used chat applications. It is just behind WhatsApp in overall usage across the world. </p>
<p>In this tutorial, we are going to replicate the Messenger UI using the Flutter mobile application development framework. We'll also explore widget-based UI development using Flutter coding. This will get us familiar with the Flutter ecosystem as well as best practices for writing Flutter code. </p>
<p>Here, we are going to implement the main conversation home screen of the messenger app which will contain a top app bar, a search bar, a stories section, and a conversation list section. </p>
<p>Through this process, we will see how Flutter makes the UI development easier and we'll get a messenger clone chat application out of it.</p>
<p>So, let's get started!</p>
<h2 id="heading-create-a-new-flutter-project">Create a new Flutter project</h2>
<p>First, we need to create a new Flutter project. To do that, make sure that the Flutter SDK and other Flutter app development-related requirements are properly installed. </p>
<p>If everything is properly set up, then to create a project we can simply run the following command in the desired local directory:</p>
<pre><code class="lang-bash">flutter create messengerUI
</code></pre>
<p>After the project has been set up, 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 a successful build, we will get the following result in the emulator screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/tip1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, we need to replace the default template with our own project structure template. </p>
<p>First, we need to create a folder called <strong>./screens</strong> inside the <strong>./lib</strong> folder. Then, inside the ./lib/screens folder, we need to create a new file called <strong>conversations.dart</strong>. </p>
<p>Inside <strong>conversation.dart</strong>, we are going to implement a simple Stateful widget class returning a <code>Scaffold</code> widget with a basic App bar and an empty <code>Container</code> body. The code for <strong>conversations.dart</strong> is 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">Conversations</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _ConversationsState createState() =&gt; _ConversationsState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ConversationsState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">Conversations</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"</span>),
      ),
      body: Container(),
    );
  }
}
</code></pre>
<p>Now, we need to replace the default template in the <strong>main.dar</strong>t file and call the <code>Conversations</code> screen in the <code>home</code> option of <code>MaterialApp</code> widget 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-keyword">import</span> <span class="hljs-string">'package:messangerUI/screens/conversations.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">'Messenger Demo'</span>,
      debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Conversations(),
    );
  }
}
</code></pre>
<p>We get the result as shown in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/mess1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-add-the-appbar">How to Add the AppBar</h2>
<p>Now, we are going to customize the App bar at the top. Since the app bar needs to be scrollable, we are <strong>not</strong> going to use the <code>appBar</code> option provided by <code>Scaffold</code> widget. We are simply going to use the <code>ListView</code> widget in the body option of <code>Scaffold</code> and keep all other widgets as children of the <code>ListView</code> widget. </p>
<p>The overall implementation of the custom app bar is provided in the code snippet below:</p>
<pre><code class="lang-dart">Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: SafeArea(
          child: Container(
        padding: EdgeInsets.only(left: <span class="hljs-number">20</span>, right: <span class="hljs-number">20</span>, top: <span class="hljs-number">15</span>),
        child: ListView(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: &lt;Widget&gt;[
                Container(
                  width: <span class="hljs-number">40</span>,
                  height: <span class="hljs-number">40</span>,
                  decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      image: DecorationImage(
                          image: NetworkImage(
                              <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/11.jpg&gt;"</span>),
                          fit: BoxFit.cover)),
                ),
                Text(
                  <span class="hljs-string">"Chats"</span>,
                  style: TextStyle(fontSize: <span class="hljs-number">22</span>, fontWeight: FontWeight.bold),
                ),
                Icon(Icons.edit)
              ],
            ),
          ],
        ),
      )),
    );
  }
</code></pre>
<p>For the app bar UI, we have used a <code>Row</code> widget inside the <code>ListView</code> widget. Inside the Row widget, we have placed a <code>Container</code> widget with <code>NetworkImage</code> as a child, a <code>Text</code> widget, and an <code>Icon</code> widget.</p>
<p>Hence, we will get the result as shown in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/mess2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-add-the-search-bar">How to Add the Search Bar</h2>
<p>Now, we are going to add a search input field just below the app bar. It will contain an <code>InputField</code> widget decorated with styles and a search icon. </p>
<p>Since we need a text controller for the <code>InputField</code> widget we need to initialize it first as shown in the code snippet below:</p>
<pre><code class="lang-dart">TextEditingController _searchController = <span class="hljs-keyword">new</span> TextEditingController();
</code></pre>
<p>Now, we are going to implement the UI for the search bar just below the <code>Row</code> widget which is inside the parent <code>ListView</code> widget. We'll use the <code>SizedBox</code> widget to give a tiny separation between the two sections. </p>
<p>The overall implementation of search bar using a <code>TextField</code> widget inside a <code>Container</code> widget with decoration is shown in the code snippet below:</p>
<pre><code class="lang-dart">body: SafeArea(
          child: Container(
        padding: EdgeInsets.only(left: <span class="hljs-number">20</span>, right: <span class="hljs-number">20</span>, top: <span class="hljs-number">15</span>),
        child: ListView(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: &lt;Widget&gt;[
                Container(
                  width: <span class="hljs-number">40</span>,
                  height: <span class="hljs-number">40</span>,
                  decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      image: DecorationImage(
                          image: NetworkImage(
                              <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/11.jpg&gt;"</span>),
                          fit: BoxFit.cover)),
                ),
                Text(
                  <span class="hljs-string">"Chats"</span>,
                  style: TextStyle(fontSize: <span class="hljs-number">22</span>, fontWeight: FontWeight.bold),
                ),
                Icon(Icons.edit)
              ],
            ),
            SizedBox(
              height: <span class="hljs-number">15</span>,
            ),
            Container(
              width: <span class="hljs-built_in">double</span>.infinity,
              height: <span class="hljs-number">40</span>,
              decoration: BoxDecoration(
                  color: Color(<span class="hljs-number">0xFFe9eaec</span>),
                  borderRadius: BorderRadius.circular(<span class="hljs-number">15</span>)),
              child: TextField(
                cursorColor: Color(<span class="hljs-number">0xFF000000</span>),
                controller: _searchController,
                decoration: InputDecoration(
                    prefixIcon: Icon(
                      Icons.search,
                      color: Color(<span class="hljs-number">0xFF000000</span>).withOpacity(<span class="hljs-number">0.5</span>),
                    ),
                    hintText: <span class="hljs-string">"Search"</span>,
                    border: InputBorder.none),
              ),
            ),
          ],
        ),
      )),
    );
</code></pre>
<p>Hence, we will get the search bar as shown in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/mess3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-implement-the-stories-section">How to Implement the Stories Section</h2>
<p>Now it is time to implement the stories section. Stories are popular feature in every social app these days. We are going to implement it just below the search bar. </p>
<p>This section will contain an image of the user with their name at the bottom. The users who have stories will have a blue circular ring around their image whereas others don't.</p>
<p>But first, we need to prepare a list of mock users to show them in the stories section. </p>
<p>For that, we are going to initialize a List called <code>storyList</code>. We'll keep some objects containing the user's information such as <code>name</code>, <code>imageUrl</code>, <code>isOnline</code> (to check if the user is online), and <code>hasStory</code> (to check it the user has a story). </p>
<p>The mock list data is provided in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-built_in">List</span> storyList = [
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Novac"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/31.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">true</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Derick"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/81.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Mevis"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/women/49.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Emannual"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/35.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">true</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Gracy"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/women/56.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Robert"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/36.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">true</span>,
    }
  ];
</code></pre>
<p>Now, we are going to implement a separate function that returns the overall UI for the Stories section. The implementation is provided in the code snippet below:</p>
<pre><code class="lang-dart">_stories() {
    <span class="hljs-keyword">return</span> SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Row(
        children: &lt;Widget&gt;[
          Padding(
            padding: EdgeInsets.only(right: <span class="hljs-number">20</span>),
            child: Column(
              children: &lt;Widget&gt;[
                Container(
                  width: <span class="hljs-number">60</span>,
                  height: <span class="hljs-number">60</span>,
                  decoration: BoxDecoration(
                      shape: BoxShape.circle, color: Color(<span class="hljs-number">0xFFe9eaec</span>)),
                  child: Center(
                    child: Icon(
                      Icons.add,
                      size: <span class="hljs-number">33</span>,
                    ),
                  ),
                ),
                SizedBox(
                  height: <span class="hljs-number">10</span>,
                ),
                SizedBox(
                  width: <span class="hljs-number">75</span>,
                  child: Align(
                      child: Text(
                    <span class="hljs-string">'Your Story'</span>,
                    overflow: TextOverflow.ellipsis,
                  )),
                )
              ],
            ),
          ),
          Row(
              children: <span class="hljs-built_in">List</span>.generate(storyList.length, (index) {
            <span class="hljs-keyword">return</span> Padding(
              padding: <span class="hljs-keyword">const</span> EdgeInsets.only(right: <span class="hljs-number">20</span>),
              child: Column(
                children: &lt;Widget&gt;[
                  Container(
                    width: <span class="hljs-number">60</span>,
                    height: <span class="hljs-number">60</span>,
                    child: Stack(
                      children: &lt;Widget&gt;[
                        storyList[index][<span class="hljs-string">'hasStory'</span>]
                            ? Container(
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        color: Colors.blueAccent, width: <span class="hljs-number">3</span>)),
                                child: Padding(
                                  padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">3.0</span>),
                                  child: Container(
                                    width: <span class="hljs-number">75</span>,
                                    height: <span class="hljs-number">75</span>,
                                    decoration: BoxDecoration(
                                        shape: BoxShape.circle,
                                        image: DecorationImage(
                                            image: NetworkImage(
                                                storyList[index][<span class="hljs-string">'imageUrl'</span>]),
                                            fit: BoxFit.cover)),
                                  ),
                                ),
                              )
                            : Container(
                                width: <span class="hljs-number">70</span>,
                                height: <span class="hljs-number">70</span>,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    image: DecorationImage(
                                        image: NetworkImage(
                                            storyList[index][<span class="hljs-string">'imageUrl'</span>]),
                                        fit: BoxFit.cover)),
                              ),
                        storyList[index][<span class="hljs-string">'isOnline'</span>]
                            ? Positioned(
                                top: <span class="hljs-number">38</span>,
                                left: <span class="hljs-number">42</span>,
                                child: Container(
                                  width: <span class="hljs-number">20</span>,
                                  height: <span class="hljs-number">20</span>,
                                  decoration: BoxDecoration(
                                      color: Color(<span class="hljs-number">0xFF66BB6A</span>),
                                      shape: BoxShape.circle,
                                      border: Border.all(
                                          color: Color(<span class="hljs-number">0xFFFFFFFF</span>), width: <span class="hljs-number">3</span>)),
                                ),
                              )
                            : Container()
                      ],
                    ),
                  ),
                  SizedBox(
                    height: <span class="hljs-number">10</span>,
                  ),
                  SizedBox(
                    width: <span class="hljs-number">75</span>,
                    child: Align(
                        child: Text(
                      storyList[index][<span class="hljs-string">'name'</span>],
                      overflow: TextOverflow.ellipsis,
                    )),
                  )
                ],
              ),
            );
          }))
        ],
      ),
    );
  }
</code></pre>
<p>Here, we returned a <code>SingleChildScrollView</code> as a parent widget with an option for horizontal scrolling. </p>
<p>Then, we used the <code>List.generate</code> widget inside the <code>Row</code> widget to iterate through our <code>storyList</code> array. For each item in the list, a template inside the <code>List.generate</code> is returned. The conditional rendering is used for online users and those users who have stories.</p>
<p>Now, we need to call the function inside the <code>ListView</code> children just below the <code>InputField</code> making a separation using the <code>SizedBox</code> widget as shown in the code snippet below:</p>
<pre><code class="lang-dart">body: SafeArea(
          child: Container(
        padding: EdgeInsets.only(left: <span class="hljs-number">20</span>, right: <span class="hljs-number">20</span>, top: <span class="hljs-number">15</span>),
        child: ListView(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: &lt;Widget&gt;[
                Container(
                  width: <span class="hljs-number">40</span>,
                  height: <span class="hljs-number">40</span>,
                  decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      image: DecorationImage(
                          image: NetworkImage(
                              <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/11.jpg&gt;"</span>),
                          fit: BoxFit.cover)),
                ),
                Text(
                  <span class="hljs-string">"Chats"</span>,
                  style: TextStyle(fontSize: <span class="hljs-number">22</span>, fontWeight: FontWeight.bold),
                ),
                Icon(Icons.edit)
              ],
            ),
            SizedBox(
              height: <span class="hljs-number">15</span>,
            ),
            Container(
              width: <span class="hljs-built_in">double</span>.infinity,
              height: <span class="hljs-number">40</span>,
              decoration: BoxDecoration(
                  color: Color(<span class="hljs-number">0xFFe9eaec</span>),
                  borderRadius: BorderRadius.circular(<span class="hljs-number">15</span>)),
              child: TextField(
                cursorColor: Color(<span class="hljs-number">0xFF000000</span>),
                controller: _searchController,
                decoration: InputDecoration(
                    prefixIcon: Icon(
                      Icons.search,
                      color: Color(<span class="hljs-number">0xFF000000</span>).withOpacity(<span class="hljs-number">0.5</span>),
                    ),
                    hintText: <span class="hljs-string">"Search"</span>,
                    border: InputBorder.none),
              ),
            ),
            SizedBox(
              height: <span class="hljs-number">20</span>,
            ),
            _stories(),
          ],
        ),
      )),
    );
</code></pre>
<p>We will get the result as shown in the demo below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/messGIF1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As you can see, the stories section is scrollable horizontally.</p>
<h2 id="heading-how-to-create-the-conversation-list-section">How to Create the Conversation List Section</h2>
<p>Now, we are going to create a list of conversations just below the Stories section. It will contain an image of the user, their name, message, and time. </p>
<p>For the conversation list as well we are going to create some mock data. The list is similar to <code>storiesList</code> but has two extra pieces of information for <code>message</code> and <code>time</code>. The mock data list <code>conversationList</code> is shown in the code snippet below:</p>
<pre><code class="lang-dart"><span class="hljs-built_in">List</span> conversationList = [
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Novac"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/31.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"Where are you?"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"5:00 pm"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Derick"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/81.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"It's good!!"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"7:00 am"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Mevis"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/women/49.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"I love You too!"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"6:50 am"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Emannual"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/35.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"Got to go!! Bye!!"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"yesterday"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Gracy"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/women/56.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"Sorry, I forgot!"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"2nd Feb"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Robert"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/36.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">true</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"No, I won't go!"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"28th Jan"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Lucy"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/women/56.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hahahahahaha"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"25th Jan"</span>
    },
    {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"Emma"</span>,
      <span class="hljs-string">"imageUrl"</span>: <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/women/56.jpg&gt;"</span>,
      <span class="hljs-string">"isOnline"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"hasStory"</span>: <span class="hljs-keyword">false</span>,
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"Been a while!"</span>,
      <span class="hljs-string">"time"</span>: <span class="hljs-string">"15th Jan"</span>
    }
  ];
</code></pre>
<p>Similar to the stories section, we are going to build the conversation list section as a separate function. </p>
<p>As a parent widget, we have returned the <code>Column</code> widget. The child of the <code>Column</code> widget contains the <code>List.generate</code> widget that iterates through the <code>conversationList</code> array and provides the UI for each item in the conversation list. </p>
<p>We use conditional rendering for online users and those users who have stories. The overall implementation of the function is provided in the code snippet below:</p>
<pre><code class="lang-dart">_conversations(BuildContext context) {
    <span class="hljs-keyword">return</span> Column(
      children: <span class="hljs-built_in">List</span>.generate(conversationList.length, (index) {
        <span class="hljs-keyword">return</span> InkWell(
          child: Padding(
            padding: <span class="hljs-keyword">const</span> EdgeInsets.only(bottom: <span class="hljs-number">20</span>),
            child: Row(
              children: &lt;Widget&gt;[
                Container(
                  width: <span class="hljs-number">60</span>,
                  height: <span class="hljs-number">60</span>,
                  child: Stack(
                    children: &lt;Widget&gt;[
                      conversationList[index][<span class="hljs-string">'hasStory'</span>] ? 
                      Container(
                        decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            border:
                                Border.all(color: Colors.blueAccent, width: <span class="hljs-number">3</span>)),
                        child: Padding(
                          padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">3.0</span>),
                          child: Container(
                            width: <span class="hljs-number">75</span>,
                            height: <span class="hljs-number">75</span>,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                image: DecorationImage(
                                    image: NetworkImage(
                                        conversationList[index][<span class="hljs-string">'imageUrl'</span>]),
                                    fit: BoxFit.cover)),
                          ),
                        ),
                      )
                      : Container(
                        width: <span class="hljs-number">70</span>,
                        height: <span class="hljs-number">70</span>,
                        decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            image: DecorationImage(
                                image: NetworkImage(
                                    conversationList[index][<span class="hljs-string">'imageUrl'</span>]),
                                fit: BoxFit.cover)),
                      ),
                      conversationList[index][<span class="hljs-string">'isOnline'</span>]
                          ? Positioned(
                              top: <span class="hljs-number">38</span>,
                              left: <span class="hljs-number">42</span>,
                              child: Container(
                                width: <span class="hljs-number">20</span>,
                                height: <span class="hljs-number">20</span>,
                                decoration: BoxDecoration(
                                    color: Color(<span class="hljs-number">0xFF66BB6A</span>),
                                    shape: BoxShape.circle,
                                    border: Border.all(color: Color(<span class="hljs-number">0xFFFFFFFF</span>), width: <span class="hljs-number">3</span>)),
                              ),
                            )
                          : Container()
                    ],
                  ),
                ),
                SizedBox(
                  width: <span class="hljs-number">20</span>,
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: &lt;Widget&gt;[
                    Text(
                      conversationList[index][<span class="hljs-string">'name'</span>],
                      style:
                          TextStyle(fontSize: <span class="hljs-number">17</span>, fontWeight: FontWeight.w500),
                    ),
                    SizedBox(
                      height: <span class="hljs-number">5</span>,
                    ),
                    SizedBox(
                      width: MediaQuery.of(context).size.width - <span class="hljs-number">135</span>,
                      child: Text(
                        conversationList[index][<span class="hljs-string">'message'</span>] +
                            <span class="hljs-string">" - "</span> +
                            conversationList[index][<span class="hljs-string">'time'</span>],
                        style: TextStyle(
                            fontSize: <span class="hljs-number">15</span>, color: Color(<span class="hljs-number">0xFF000000</span>).withOpacity(<span class="hljs-number">0.7</span>)),
                        overflow: TextOverflow.ellipsis,
                      ),
                    )
                  ],
                )
              ],
            ),
          ),
        );
      }),
    );
  }
</code></pre>
<p>Now we need to call the <code>_converstions()</code> function in the in <code>ListView</code> of <code>Scaffold</code> just below the Stories function as shown in the code snippet below:</p>
<pre><code class="lang-dart">body: SafeArea(
          child: Container(
        padding: EdgeInsets.only(left: <span class="hljs-number">20</span>, right: <span class="hljs-number">20</span>, top: <span class="hljs-number">15</span>),
        child: ListView(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: &lt;Widget&gt;[
                Container(
                  width: <span class="hljs-number">40</span>,
                  height: <span class="hljs-number">40</span>,
                  decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      image: DecorationImage(
                          image: NetworkImage(
                              <span class="hljs-string">"&lt;https://randomuser.me/api/portraits/men/11.jpg&gt;"</span>),
                          fit: BoxFit.cover)),
                ),
                Text(
                  <span class="hljs-string">"Chats"</span>,
                  style: TextStyle(fontSize: <span class="hljs-number">22</span>, fontWeight: FontWeight.bold),
                ),
                Icon(Icons.edit)
              ],
            ),
            SizedBox(
              height: <span class="hljs-number">15</span>,
            ),
            Container(
              width: <span class="hljs-built_in">double</span>.infinity,
              height: <span class="hljs-number">40</span>,
              decoration: BoxDecoration(
                  color: Color(<span class="hljs-number">0xFFe9eaec</span>),
                  borderRadius: BorderRadius.circular(<span class="hljs-number">15</span>)),
              child: TextField(
                cursorColor: Color(<span class="hljs-number">0xFF000000</span>),
                controller: _searchController,
                decoration: InputDecoration(
                    prefixIcon: Icon(
                      Icons.search,
                      color: Color(<span class="hljs-number">0xFF000000</span>).withOpacity(<span class="hljs-number">0.5</span>),
                    ),
                    hintText: <span class="hljs-string">"Search"</span>,
                    border: InputBorder.none),
              ),
            ),
            SizedBox(
              height: <span class="hljs-number">20</span>,
            ),
            _stories(),
            SizedBox(
              height: <span class="hljs-number">20</span>,
            ),
            **_conversations(context)**
          ],
        ),
      )),
    );
  }
</code></pre>
<p>We will get the result as shown in the demo below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/messGIF2.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As you can see, the overall screen is scrollable vertically with Conversation List view and the Stories section is scrollable horizontally.</p>
<p>Finally, we have successfully created the home screen of a messenger app using Flutter.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The main objective of this tutorial was to show you how to build a UI like Facebook messenger using the Flutter ecosystem. </p>
<p>If you look closely at the code, you'll see that most of the implementation was pretty easy because of the flexibility and structured layout build that Flutter provides. </p>
<p>With only a few widgets, we can place every component in the UI in the correct position. Along with this beautiful UI creation, you can also learn basic coding patterns for Flutter development. </p>
<p>Separating large sections of code into separate functions helps simplify and clean up our code. This demonstrated a best coding practice for UI development in Flutter. </p>
<p>The tutorial also highlights how some widgets in Flutter make things easier for us, such as horizontal scrolling and placement icons and images with styles. You can definitely take this information and use it to build your own chat application in the future.</p>
<p>Moreover, you can also get inspiration from state of the art <a target="_blank" href="https://www.instaflutter.com/app-templates/flutter-chat-app/">Flutter chat app templates</a> out there that provide beautiful UIs as well as powerful features. And in case you want to check out chat application templates built using other mobile application development frameworks, you peruse these <a target="_blank" href="https://www.instamobile.io/app-templates/react-native-chat-app-template/">React Native Chat app</a> templates as well.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to get Facebook messenger to notify you about the weather ]]>
                </title>
                <description>
                    <![CDATA[ By Ekapope Viriyakovithya A complete DIY guide to build your own weather alert bot. The morning routine is always stressful. Wouldn’t it be wonderful if you had one less thing to worry about in the morning? What if you had a customizable weather aler... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-facebook-messenger-to-notify-you-about-the-weather-8b5e87a64540/</link>
                <guid isPermaLink="false">66d45e3fd14641365a0508a0</guid>
                
                    <category>
                        <![CDATA[ Facebook Messenger ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ how-to ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ weather ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 24 Jan 2019 18:22:42 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*RBHbkOfMOzl7o_crT4Rg7g.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ekapope Viriyakovithya</p>
<h3 id="heading-a-complete-diy-guide-to-build-your-own-weather-alert-bot">A complete DIY guide to build your own weather alert bot.</h3>
<p>The morning routine is always stressful. Wouldn’t it be wonderful if you had one less thing to worry about in the morning?</p>
<p>What if you had a customizable weather alert bot that sent you a short message ONLY when there was a chance of rain above your pre-defined threshold?</p>
<p>Don’t waste your time checking the weather in a separate app. It can be live on your Facebook messenger chatbox!</p>
<h3 id="heading-what-do-you-need">What do you need?</h3>
<ul>
<li>Python 3.6 (or earlier) with pandas and <a target="_blank" href="https://github.com/carpedm20/fbchat">fbchat</a> packages installed</li>
</ul>
<pre><code class="lang-bash">pip install fbchat
</code></pre>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Uy6HxLRFNAOcpu40NDfXSw.png" alt="Image" width="249" height="318" loading="lazy">
<em>AccuWeather Free Account</em></p>
<ul>
<li><a target="_blank" href="https://developer.accuweather.com/packages">AccuWeather developer account</a>, the free package should be enough. It provides 50 calls/day with 1 key/developer account.</li>
</ul>
<h3 id="heading-lets-get-started">Let’s get started!</h3>
<p>At the end of this how-to, you will have 3 files in the scripts folder:</p>
<p><a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/tree/master/scripts">keys.py</a> : to store your facebook email, password, and accuweather API key</p>
<p><a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> : to store the threshold and weather forecast location id</p>
<p><a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/main.py">main.py</a> : this is the main script, it will call the keys.py and params.py</p>
<h4 id="heading-1-setup-facebook-account-and-accuweather-api-key">1. Setup Facebook account and AccuWeather API key</h4>
<p>First, let’s put your account detail in the <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/keys.py">keys.py</a> file.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Your Facebook usersname (email)</span>
FB_USERNAME= <span class="hljs-string">""</span> 

<span class="hljs-comment"># Your Facebook password</span>
FB_PASSWORD= <span class="hljs-string">""</span> 

<span class="hljs-comment"># Your AccuWeather API key</span>
ACCUWEATHER_API_KEY= <span class="hljs-string">""</span>
</code></pre>
<h4 id="heading-2-setup-parameters"><strong>2. Setup parameters</strong></h4>
<p>In this step, we will define the threshold for the probability of rain or snow, delay time between each request and message, and also location.</p>
<p>Currently, we set the threshold at 25% for both rain and snow. We will get the alert message only if the AccuWeather data shows the probability ≥ 25%.</p>
<p>The scripts below will request data from AccuWeather every 1 hour ( UPDATE_INTERVAL_HR= 1) and will send a message every 4 hours ( DELAY_TIME_HR= 4).</p>
<p>These parameters will be stored in the <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> file.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Define % threshold for probability of rain and snow. </span>
<span class="hljs-comment"># The msg will be sent out if the % chance exceed the value</span>
RAIN_THRESHOLD = <span class="hljs-number">25</span>
SNOW_THRESHOLD = <span class="hljs-number">25</span>

<span class="hljs-comment"># time between Accuweather request (in hour)</span>
UPDATE_INTERVAL_HR = <span class="hljs-number">1</span> 

<span class="hljs-comment"># delay time between msg (in hour)</span>
DELAY_TIME_HR = <span class="hljs-number">4</span> 

<span class="hljs-comment"># location id</span>
<span class="hljs-comment"># for example, https://www.accuweather.com/en/fr/lille/135564/weather-forecast/135564</span>
<span class="hljs-comment"># location id is 135564</span>
LOCATION_ID = <span class="hljs-string">"135564"</span>
</code></pre>
<h4 id="heading-3-retrieve-data-from-accuweather">3. Retrieve data from AccuWeather</h4>
<p>Now here comes the fun part. We will now work on the main script.</p>
<p>If you plan to run it locally, setup your directory and import keys and params. Make sure you put <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/keys.py">keys.py</a> and <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> in the same folder as this <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/main.py">main.py</a> script.</p>
<pre><code class="lang-py"><span class="hljs-comment">#set the current directory</span>
<span class="hljs-keyword">import</span> os
os.chdir(<span class="hljs-string">r".\YOUR_PATH"</span>)
<span class="hljs-comment">###############################################################################</span>
<span class="hljs-comment">#import keys and parameters other scripts in the same folder</span>
<span class="hljs-keyword">from</span> keys <span class="hljs-keyword">import</span> FB_USERNAME,FB_PASSWORD,ACCUWEATHER_API_KEY
<span class="hljs-keyword">from</span> params <span class="hljs-keyword">import</span> RAIN_THRESHOLD,SNOW_THRESHOLD,UPDATE_INTERVAL_HR,DELAY_TIME_HR,LOCATION_ID
</code></pre>
<p>Import required modules.</p>
<pre><code class="lang-py"><span class="hljs-comment">#import required modules</span>
<span class="hljs-keyword">import</span> urllib
<span class="hljs-keyword">import</span> urllib.parse
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">from</span> fbchat <span class="hljs-keyword">import</span> Client
<span class="hljs-keyword">from</span> fbchat.models <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
</code></pre>
<p>Define ‘url_page’ to be requested, in this example, we will retrieve 12-hour hourly forecast. Convert our update/delay time into seconds.</p>
<pre><code class="lang-py">url_page = <span class="hljs-string">"http://dataservice.accuweather.com/forecasts/v1/hourly/12hour/"</span>+str(LOCATION_ID)+<span class="hljs-string">"?apikey="</span>+ACCUWEATHER_API_KEY+<span class="hljs-string">"&amp;details=true&amp;metric=true"</span>
<span class="hljs-comment">#convert hours to seconds</span>
update_interval_sec = <span class="hljs-number">60</span>*<span class="hljs-number">60</span>*UPDATE_INTERVAL_HR 
delay_time_sec = <span class="hljs-number">60</span>*<span class="hljs-number">60</span>*DELAY_TIME_HR
</code></pre>
<p>Then, request the data and put in pandas DataFrame called ‘json_df’.</p>
<p>At this point, we can inspect the retrieved table. Extract and rename the elements that we need. In this example, we will need AccuWeather link, % rain, % snow, date, and time in the desired format.</p>
<pre><code class="lang-py">    json_page = urllib.request.urlopen(url_page)
    json_data = json.loads(json_page.read().decode())
    json_df = pd.DataFrame(json_data)

    <span class="hljs-comment"># set maximum width, so the links are fully shown and clickable</span>
    pd.set_option(<span class="hljs-string">'display.max_colwidth'</span>, <span class="hljs-number">-1</span>)
    json_df[<span class="hljs-string">'Links'</span>] = json_df[<span class="hljs-string">'MobileLink'</span>].apply(<span class="hljs-keyword">lambda</span> x: <span class="hljs-string">'&lt;a href='</span>+x+<span class="hljs-string">'&gt;Link&lt;/a&gt;'</span>)

    json_df[<span class="hljs-string">'Real Feel (degC)'</span>] = json_df[<span class="hljs-string">'RealFeelTemperature'</span>].apply(<span class="hljs-keyword">lambda</span> x: x.get(<span class="hljs-string">'Value'</span>))
    json_df[<span class="hljs-string">'Weather'</span>] = json_df[<span class="hljs-string">'IconPhrase'</span>] 
    json_df[<span class="hljs-string">'Percent_Rain'</span>] = json_df[<span class="hljs-string">'RainProbability'</span>] 
    json_df[<span class="hljs-string">'Percent_Snow'</span>] = json_df[<span class="hljs-string">'SnowProbability'</span>]
</code></pre>
<p>If we look closely, the ‘DateTime’ column is a bit tricky to extract and needs some work. After clean up, save it in ‘current_retrieved_datetime’ variable.</p>
<pre><code class="lang-py">json_df[[<span class="hljs-string">'Date'</span>,<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'DateTime'</span>].str.split(<span class="hljs-string">'T'</span>, expand=<span class="hljs-literal">True</span>)
<span class="hljs-comment"># trim the time to hh:mm format, change to str</span>
json_df[[<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'Time'</span>].str.split(<span class="hljs-string">'+'</span>, expand=<span class="hljs-literal">True</span>)[<span class="hljs-number">0</span>].astype(str).str[:<span class="hljs-number">5</span>]

current_retrieved_datetime = str(json_df[<span class="hljs-string">'Date'</span>][<span class="hljs-number">0</span>])+<span class="hljs-string">' '</span>+str(json_df[<span class="hljs-string">'Time'</span>][<span class="hljs-number">0</span>])
</code></pre>
<p>Next, write an if-else condition to customize the alert message. The retrieved table provides us a 12-hr forecast. We will check each element of both the rain and snow columns and return a message if the probability is above the threshold.</p>
<p>First, initialize the alert message for each case.</p>
<pre><code class="lang-py">rain_msg=<span class="hljs-string">""</span>
snow_msg=<span class="hljs-string">""</span>
</code></pre>
<p>Check ‘Percent_Rain’ and ‘ Percent_Snow’ columns, flag with 1 if the % probability is above threshold (or 0 otherwise).</p>
<p>Sum the columns and modify the ‘rain_msg’ and ‘snow_msg’.</p>
<pre><code class="lang-py">    <span class="hljs-comment"># check % Rain column, return rain_msg</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &gt;= RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &lt; RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Rain_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        rain_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Rain'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of rain'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])

    <span class="hljs-comment"># check % Snow column</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &gt;= SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &lt; SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Snow_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        snow_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Snow'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of snow'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])
</code></pre>
<p>Initialize ‘alert_msg’, modify the messages if there is any ‘rain_msg’ or ‘snow_msg’.</p>
<pre><code class="lang-py">alert_msg =<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span>(len(rain_msg)|len(snow_msg)!=<span class="hljs-number">0</span>):
     alert_msg = rain_msg +<span class="hljs-string">" "</span>+snow_msg
</code></pre>
<p>Add the link to variable ‘ link_for_click’ this will be attached to the message when we send later on.</p>
<pre><code class="lang-py">link_for_click = json_df[<span class="hljs-string">'MobileLink'</span>][<span class="hljs-number">0</span>]
</code></pre>
<p>Up until this point, we can now wrap them into a function. Don’t worry if you get lost, I have put them together below.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">func_get_weather</span>(<span class="hljs-params">url_page</span>):</span>

    json_page = urllib.request.urlopen(url_page)
    json_data = json.loads(json_page.read().decode())
    json_df = pd.DataFrame(json_data)

    <span class="hljs-comment"># set maximum width, so the links are fully shown and clickable</span>
    pd.set_option(<span class="hljs-string">'display.max_colwidth'</span>, <span class="hljs-number">-1</span>)
    json_df[<span class="hljs-string">'Links'</span>] = json_df[<span class="hljs-string">'MobileLink'</span>].apply(<span class="hljs-keyword">lambda</span> x: <span class="hljs-string">'&lt;a href='</span>+x+<span class="hljs-string">'&gt;Link&lt;/a&gt;'</span>)

    json_df[<span class="hljs-string">'Real Feel (degC)'</span>] = json_df[<span class="hljs-string">'RealFeelTemperature'</span>].apply(<span class="hljs-keyword">lambda</span> x: x.get(<span class="hljs-string">'Value'</span>))
    json_df[<span class="hljs-string">'Weather'</span>] = json_df[<span class="hljs-string">'IconPhrase'</span>] 
    json_df[<span class="hljs-string">'Percent_Rain'</span>] = json_df[<span class="hljs-string">'RainProbability'</span>] 
    json_df[<span class="hljs-string">'Percent_Snow'</span>] = json_df[<span class="hljs-string">'SnowProbability'</span>] 
    json_df[[<span class="hljs-string">'Date'</span>,<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'DateTime'</span>].str.split(<span class="hljs-string">'T'</span>, expand=<span class="hljs-literal">True</span>)
    <span class="hljs-comment"># trim the time to hh:mm format, change to str</span>
    json_df[[<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'Time'</span>].str.split(<span class="hljs-string">'+'</span>, expand=<span class="hljs-literal">True</span>)[<span class="hljs-number">0</span>].astype(str).str[:<span class="hljs-number">5</span>]

    current_retrieved_datetime = str(json_df[<span class="hljs-string">'Date'</span>][<span class="hljs-number">0</span>])+<span class="hljs-string">' '</span>+str(json_df[<span class="hljs-string">'Time'</span>][<span class="hljs-number">0</span>])

    rain_msg=<span class="hljs-string">""</span>
    snow_msg=<span class="hljs-string">""</span>

    <span class="hljs-comment"># check % Rain column, return rain_msg</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &gt;= RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &lt; RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Rain_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        rain_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Rain'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of rain'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])

    <span class="hljs-comment"># check % Snow column</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &gt;= SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &lt; SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Snow_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        snow_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Snow'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of snow'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])

    alert_msg =<span class="hljs-string">""</span>
    <span class="hljs-keyword">if</span>(len(rain_msg)|len(snow_msg)!=<span class="hljs-number">0</span>):
         alert_msg = rain_msg +<span class="hljs-string">" "</span>+snow_msg

    link_for_click = json_df[<span class="hljs-string">'MobileLink'</span>][<span class="hljs-number">0</span>]

    <span class="hljs-keyword">return</span>(current_retrieved_datetime,alert_msg,link_for_click)
</code></pre>
<h4 id="heading-4-automated-loop">4. Automated loop</h4>
<p>Finally, for the last part, we will automate the process by using loops. The scripts below are putting the number of loops as ‘num_repeat = 999’.</p>
<pre><code class="lang-py">num_repeat = <span class="hljs-number">999</span> <span class="hljs-comment"># number of loops to repeat</span>
previous_alert_msg = <span class="hljs-string">""</span> <span class="hljs-comment"># initialize alert msg</span>
</code></pre>
<p>Use try and except to overcome errors (just in case something goes wrong with connections). Call ‘func_get_weather’ function and assign to variables.</p>
<pre><code class="lang-py"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(num_repeat):
    <span class="hljs-keyword">try</span>:
        current_retrieved_datetime,alert_msg,link_for_click = func_get_weather(url_page)
    <span class="hljs-keyword">except</span> (RuntimeError, TypeError, NameError, ValueError, urllib.error.URLError):
        print(<span class="hljs-string">'error catched'</span>)
</code></pre>
<p>Then, check if there are any changes in the weather. If nothing has changed, print the message to the screen. No chat message will be sent.</p>
<pre><code class="lang-py">    <span class="hljs-comment">#if the weather forecast has not changed, no alert msg will be sent</span>
    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg == alert_msg:
        print(i, current_retrieved_datetime, <span class="hljs-string">'no changes in weather forecast'</span>)
</code></pre>
<p>The message will be sent only if there is any change in the weather.</p>
<p>We can finalize our message at this point. Fetch your user id of your friends and store in ‘friend_list’. Loop to send the message to each friend one-by-one. We put sleep time = 2 seconds between each message and logout after finish.</p>
<pre><code class="lang-py">    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg != alert_msg:    
        <span class="hljs-comment"># login and send facebook msg </span>
        client = Client(FB_USERNAME,FB_PASSWORD)
        users = client.fetchAllUsers()
        friend_list=[user.uid <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users <span class="hljs-keyword">if</span> user.uid!=<span class="hljs-string">"0"</span>]
        <span class="hljs-comment"># loop though all friends</span>
        <span class="hljs-keyword">for</span> id <span class="hljs-keyword">in</span> friend_list: 
            client.send(Message(text=current_retrieved_datetime+<span class="hljs-string">' '</span>+<span class="hljs-string">'12-hr Weather Forecast'</span> +<span class="hljs-string">' '</span>+ alert_msg +<span class="hljs-string">' '</span>+link_for_click),thread_id=id, thread_type=ThreadType.USER)
            <span class="hljs-comment">#sleep for 2 secs between each msg</span>
            time.sleep(<span class="hljs-number">2</span>) 
        <span class="hljs-comment">#logout after sent</span>
        client.logout()
</code></pre>
<p>Execute delay time for the next message. Already defined in <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> file — in this case, it is 4 hours. And another one for AccuWeather request delay is 1 hour.</p>
<pre><code class="lang-py">    time.sleep(delay_time_sec)                         
time.sleep(update_interval_sec)
</code></pre>
<p>Again, don’t worry if you get lost. I have put the complete loop together below.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Execute functions, retrieve data and send facebook msg</span>
num_repeat = <span class="hljs-number">999</span> <span class="hljs-comment"># number of loops to repeat</span>
previous_alert_msg = <span class="hljs-string">""</span> <span class="hljs-comment"># initialize alert msg</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(num_repeat):

    <span class="hljs-keyword">try</span>:
        current_retrieved_datetime,alert_msg,link_for_click = func_get_weather(url_page)
    <span class="hljs-keyword">except</span> (RuntimeError, TypeError, NameError, ValueError, urllib.error.URLError):
        print(<span class="hljs-string">'error catched'</span>)

    <span class="hljs-comment">#if the weather forecast has not changed, no alert msg will be sent</span>
    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg == alert_msg:
        print(i, current_retrieved_datetime, <span class="hljs-string">'no changes in weather forecast'</span>)
    <span class="hljs-comment">#if there is any changes in weather       </span>
    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg != alert_msg:    
        <span class="hljs-comment"># login and send facebook msg </span>
        client = Client(FB_USERNAME,FB_PASSWORD)
        users = client.fetchAllUsers()
        friend_list=[user.uid <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users <span class="hljs-keyword">if</span> user.uid!=<span class="hljs-string">"0"</span>]
        <span class="hljs-comment"># loop though all friends</span>
        <span class="hljs-keyword">for</span> id <span class="hljs-keyword">in</span> friend_list: 
            client.send(Message(text=current_retrieved_datetime+<span class="hljs-string">' '</span>+<span class="hljs-string">'12-hr Weather Forecast'</span> +<span class="hljs-string">' '</span>+ alert_msg +<span class="hljs-string">' '</span>+link_for_click),thread_id=id, thread_type=ThreadType.USER)
            <span class="hljs-comment">#sleep for 2 secs between each msg</span>
            time.sleep(<span class="hljs-number">2</span>) 
        <span class="hljs-comment">#logout after sent</span>
        client.logout()    
        time.sleep(delay_time_sec)                         
    time.sleep(update_interval_sec)
print(current_retrieved_datetime,<span class="hljs-string">'Run Completed'</span>)
</code></pre>
<p>Ta-da! After all our hard work, here is a snapshot of the message we will get.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*34jxVzSyVzO86-OKGlzVHw.png" alt="Image" width="431" height="503" loading="lazy">
<em>Facebook chatbox msg. The location id in this example is 135564.</em></p>
<p>In case we need to know more detail, we can directly click on the link. It will navigate to the AccuWeather mobile website.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*sZ6uWgjnGDF1rVBoKy44Ng.png" alt="Image" width="603" height="586" loading="lazy">
<em>AccuWeather Link</em></p>
<p>The completed script for this how-to is also <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat">documented on GitHub</a>.</p>
<p>Thank you for reading. Please give it a try, have fun and let me know your feedback!</p>
<p>If you like what I did, consider following me on <a target="_blank" href="https://ekapope.github.io/">GitHub</a>, <a target="_blank" href="https://medium.com/@ekapope.v">Medium</a>, and <a target="_blank" href="https://twitter.com/EkapopeV">Twitter</a>. Make sure <a target="_blank" href="https://github.com/Ekapope">to star it on GitHub</a> :D</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
