<?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" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[MagsAI]]></title><description><![CDATA[Knowledge is power and it should be shared]]></description><link>https://mags.ai/</link><image><url>https://mags.ai/favicon.png</url><title>MagsAI</title><link>https://mags.ai/</link></image><generator>Ghost 5.49</generator><lastBuildDate>Sat, 24 Oct 2026 08:50:13 GMT</lastBuildDate><atom:link href="https://mags.ai/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Book Club: It's OK To Be Angry About Capitalism by Bernie Sanders]]></title><description><![CDATA[We're launching the Mags.ai Book Club to provide a platform for members to engage in meaningful conversations and expand their perspectives. The inaugural book selection, "It's Ok to Be Angry About Capitalism" by Bernie Sanders, offers an insightful analysis of our economic system.]]></description><link>https://mags.ai/book-club-its-ok-to-be-angry-about-capitalism-by-bernie-sanders/</link><guid isPermaLink="false">6404fcb200f4ca0210180719</guid><category><![CDATA[Politics]]></category><category><![CDATA[Book Club]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Sun, 05 Mar 2023 21:20:48 GMT</pubDate><media:content url="https://mags.ai/content/images/2023/03/sanders-with-books-2391548476-1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://mags.ai/content/images/2023/03/sanders-with-books-2391548476-1-.jpg" alt="Book Club: It&apos;s OK To Be Angry About Capitalism by Bernie Sanders"><p>Mags.ai is a multidisciplinary learning community focused on bettering ourselves through sharing knowledge with each other. Our interests include but are not limited to Science, Technology, Philosophy, and Politics. Mags.ai is committed to exploring these subjects in-depth and helping our community members become more informed and engaged in the world around them. Through our platform, members can connect with experts, engage in lively discussions, and access a wealth of curated resources, from articles and videos to podcasts and book recommendations.</p><p>Today, we&apos;re excited to announce our new book club that we believe will help us further our mission of learning and discovery. The book club will be open to all members of the Mags.ai community and will provide an opportunity for participants to delve deeper into these subjects, engage in meaningful discussions, and connect with fellow learners from around the world. We hope that this initiative will foster a sense of curiosity, collaboration, and intellectual growth among our community members, and we look forward to sharing our reading journey with you.</p><p>Our first book selection will be &quot;It&apos;s Ok to Be Angry About Capitalism&quot; by Bernie Sanders. This book is an insightful and passionate exploration of the problems inherent in our current economic system, and how we can work to build a fairer and more just society.</p><p>To participate in the discussion, become a member of our Discord community</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Book Club: It&apos;s OK To Be Angry About Capitalism by Bernie Sanders">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html--><p>In &quot;It&apos;s Ok to Be Angry About Capitalism&quot;, Senator Sanders draws on his decades of experience in public service and activism to make the case for a more equitable and democratic economy. He argues that the concentration of wealth and power in the hands of a few has led to widespread inequality, environmental degradation, and political dysfunction, and that we need to fundamentally rethink our approach to economics if we want to create a better future for everyone.</p><p>Throughout the book, Sanders offers a range of policy proposals and practical strategies for achieving these goals, from raising the minimum wage and strengthening workers&apos; rights to investing in green infrastructure and implementing a fairer tax system. He also draws on examples from other countries, such as Denmark and Germany, to show how alternative models of capitalism can work in practice.</p><p>&quot;It&apos;s Ok to Be Angry About Capitalism&quot; is a timely and important book that speaks to many of the pressing issues of our time. I hope you&apos;ll join us in reading and discussing it in the coming weeks.</p><p>Happy reading!</p><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p>]]></content:encoded></item><item><title><![CDATA[10 Canva Tricks You Should Know]]></title><description><![CDATA[Level up your Canva skills with these 10 tips and tricks you may not know]]></description><link>https://mags.ai/10-canva-tricks-you-should-know/</link><guid isPermaLink="false">63c410d042658e021185715e</guid><category><![CDATA[Canva]]></category><category><![CDATA[Design]]></category><category><![CDATA[Web Design]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Fri, 30 Sep 2022 07:41:14 GMT</pubDate><media:content url="https://mags.ai/content/images/2023/01/10-Canva-Tricks-You-Should-Know-1-.png" medium="image"/><content:encoded><![CDATA[<img src="https://mags.ai/content/images/2023/01/10-Canva-Tricks-You-Should-Know-1-.png" alt="10 Canva Tricks You Should Know"><p>Canva is one of the most popular online design and publishing tools.</p><p>Over 75 million people in more than 190 countries use Canva every month and 3.5 billion designs were created with Canva in 2021.</p><p>Level up your Canva skills with these 10 tips and tricks you may not know &#xA0;&#x1F447;</p><h2 id="1-create-a-website">1. Create a Website</h2><p>Create basic websites just using Canva &#x1F92F;</p><ol><li>Create a new design and select website</li><li>Choose any template</li><li>Click Publish as Website</li><li>Choose free or paid domain</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2X0FAacAErQdj.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="2-find-similar-elements">2. Find similar elements</h2><p>You can find more designs similar to ones you like by searching.</p><ol><li>Select your element</li><li>Click the information icon</li><li>Search by tags or select &quot;See more like this&quot;</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2X2b-aIAE00cS.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="3-copy-a-style">3. Copy a style</h2><p>You can easily copy the styles from one element to another</p><ol><li>Select an element</li><li>Click paint roller icon</li><li>Select the element you want the style to be copied to</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2X6RbaIAcBcv7.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="4-lock-elements">4. Lock elements</h2><p>Prevent mistakes by locking finalized elements</p><ol><li>Select all the elements to lock</li><li>Click the lock icon on the toolbar</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2X8wMacAUdPog.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="5-search-photos-by-color">5. Search photos by color</h2><p>You can search for photos based on color to match your design</p><ol><li>Click photos on the sidebar</li><li>Click the search filter controls</li><li>Select your desired color</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2X_NnaMAM2-Cs.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><hr><!--kg-card-begin: html--><h2>Continue Your Learning Journey</h2>
<div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="10 Canva Tricks You Should Know">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html--><hr><h2 id="6-generate-product-mockups">6. Generate product mockups</h2><p>Quickly turn your assets into high quality mockups!</p><ol><li>Select an element</li><li>Click edit image</li><li>Choose an option under Smartmockups</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2YAZ-agAM-IBJ.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="7-display-rulers-and-guides">7. Display rulers and guides</h2><p>Use rulers and guides to make aligning your elements easier</p><ol><li>Click File &#x279C; &quot;Show rulers and guides&quot;</li><li>Click and drag as many guidelines as needed rom the edges of the editor</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2YDpyacAYxuto.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="8-create-clickable-links">8. Create clickable links</h2><p>Add hyperlinks to a design that work even when exported as a PDF!</p><ol><li>Select an element</li><li>Click on the hyperlink icon</li><li>Enter the URL to link to</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2YHMMaMAAEpDv.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="9-generate-qr-codes">9. Generate QR codes</h2><p>You can create a QR code that promotes your website or app</p><ol><li>Click &quot;More&quot; on the sidebar</li><li>Select QR Code</li><li>Enter a URL</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2YJ9kagAAv1S-.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="10-plan-your-content">10. Plan your content</h2><p>Schedule content on your social media platforms (Pro only)</p><ol><li>Select &quot;Content Planner&quot; from the homepage</li><li>Hover over a data and click the + icon</li><li>Select a design and click Schedule</li></ol><!--kg-card-begin: html--><video autoplay muted playsinline loop style="width:100%">

    <source src="https://video.twimg.com/tweet_video/Fc2YMljagAEwgnR.mp4" type="video/mp4">

    Sorry, your browser doesn&apos;t support embedded videos.
</video><!--kg-card-end: html--><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="10 Canva Tricks You Should Know">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html--><h2 id="content-credit">Content Credit</h2><p>Thank you to Syed Huq for <a href="https://twitter.com/_syedhuq/status/1571078764284743682?ref=mags.ai">sharing their knowledge</a> on Twitter!</p>]]></content:encoded></item><item><title><![CDATA[How to Change the Status Bar Color in Flutter]]></title><description><![CDATA[Setting the color of the status bar can better align your application with your brand. With Flutter, there are several ways we can color the status bar. We'll cover 3 different ways to color the status bar.]]></description><link>https://mags.ai/how-to-change-the-status-bar-color-in-flutter/</link><guid isPermaLink="false">63c410d042658e021185715d</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Sun, 15 May 2022 21:56:52 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1627253781598-63b98c51da42?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDg5fHxwaG9uZSUyMGFwcHxlbnwwfHx8fDE2NTIxMzY3Mjc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1627253781598-63b98c51da42?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDg5fHxwaG9uZSUyMGFwcHxlbnwwfHx8fDE2NTIxMzY3Mjc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="How to Change the Status Bar Color in Flutter"><p>On mobile devices, the status bar appears along the upper edge of the screen and displays useful information about the device&#x2019;s current state, like the time, cellular carrier, and battery level. Setting the color of the status bar can better align your application with your brand. With Flutter, there are several ways we can color the status bar.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mags.ai/content/images/2022/05/image.png" class="kg-image" alt="How to Change the Status Bar Color in Flutter" loading="lazy" width="720" height="48" srcset="https://mags.ai/content/images/size/w600/2022/05/image.png 600w, https://mags.ai/content/images/2022/05/image.png 720w" sizes="(min-width: 720px) 720px"><figcaption>The Android status bar</figcaption></figure><h2 id="the-status-bar-color-hierarchy">The Status Bar Color Hierarchy</h2><p>We&apos;ll cover 3 different ways to color the status bar. In increasing order of specificity they are:</p><ul><li>Scaffold <code>backgroundColor</code></li><li>AppBar <code>backgroundColor</code></li><li>AppBar <code>systemOverlayStyle</code> (Android only!)</li></ul><h3 id="scaffold-background-color">Scaffold Background Color</h3><p>By setting a background color of a Scaffold <strong>without an AppBar</strong> the status bar will inherit from the specified color.</p><figure class="kg-card kg-code-card"><pre><code class="language-dart">  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            const Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: const Icon(
          Icons.add,
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }</code></pre><figcaption>Coloring the status bar with the Scaffold background color</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/05/Screenshot_1652144655.png" class="kg-image" alt="How to Change the Status Bar Color in Flutter" loading="lazy" width="231" height="500"></figure><h3 id="appbar-background-color">AppBar Background Color</h3><p>By setting a background color of an AppBar the status bar will inherit from the specified color, regardless of if the Scaffold has a background color.</p><figure class="kg-card kg-code-card"><pre><code class="language-dart">  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green,
      appBar: AppBar(
        backgroundColor: Colors.purple,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            const Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: const Icon(
          Icons.add,
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }</code></pre><figcaption>Coloring the status bar with the AppBar background color</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/05/Screenshot_1652144745-1.png" class="kg-image" alt="How to Change the Status Bar Color in Flutter" loading="lazy" width="231" height="500"></figure><h3 id="appbar-background-color-system-overlay-style">AppBar Background Color + System Overlay Style</h3><p>On Android, the status bar actually takes on a slightly darker color that what&apos;s specified by the Scaffold and AppBar. This is by design, but if you&apos;d like to specify the exact color value you can set it via the <code>systemOverlayStyle</code> property on an AppBar. </p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x2139;&#xFE0F;</div><div class="kg-callout-text">If you want to color the status bar but don&apos;t want to use and AppBar you can set the <code>toolbarHeight</code> to 0!</div></div><figure class="kg-card kg-code-card"><pre><code class="language-dart">  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green,
      appBar: AppBar(
        backgroundColor: Colors.purple,
        elevation: 0,
        toolbarHeight: 0,
        systemOverlayStyle: const SystemUiOverlayStyle(
          statusBarColor: Colors.purple,
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            const Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: const Icon(
          Icons.add,
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }</code></pre><figcaption>Coloring the status bar with the AppBar background color and system overlay style</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/05/Screenshot_1652145644-1.png" class="kg-image" alt="How to Change the Status Bar Color in Flutter" loading="lazy" width="231" height="500"></figure><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="How to Change the Status Bar Color in Flutter">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Creating Colors in Flutter with examples]]></title><description><![CDATA[Build a beautiful Flutter theme using custom HEX or RGB color codes instead of using the Material Design color palette.]]></description><link>https://mags.ai/creating-colors-in-flutter-with-examples/</link><guid isPermaLink="false">63c410d042658e021185715c</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Sat, 30 Apr 2022 20:08:21 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1631127887321-0a2cf902a002?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGNvbG9yJTIwcGFsbGV0dGV8ZW58MHx8fHwxNjUxMzQ0NzMz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1631127887321-0a2cf902a002?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGNvbG9yJTIwcGFsbGV0dGV8ZW58MHx8fHwxNjUxMzQ0NzMz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Creating Colors in Flutter with examples"><p>Flutter makes it very easy to create a custom theme for your application. This article will quickly outline the various ways of using colors in Flutter.</p><h2 id="the-colors-class">The Colors Class</h2><p>The <a href="https://api.flutter.dev/flutter/material/Colors-class.html?ref=mags.ai">Colors</a> class provides quick access to colors that match the Material Design color palette. </p><p><strong>Primary Colors</strong></p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/04/image-1.png" class="kg-image" alt="Creating Colors in Flutter with examples" loading="lazy" width="450" height="618"></figure><ul><li>Primary colors have shades from 100 - 900 in increments of one hundred with a starting shade of 50</li><li>The color without a shade (ex: <code>Colors.pink</code>) is equivalent to the 500 shade</li></ul><p><strong>Accent Colors</strong></p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/04/image.png" class="kg-image" alt="Creating Colors in Flutter with examples" loading="lazy" width="450" height="234"></figure><ul><li>Accent colors have shades of 100, 200, 400, and 700</li><li>The color without a shade (ex: <code>Colors.pinkAccent</code>) &#xA0;is equivalent to the 200 shade</li></ul><p><strong>Blacks and Whites</strong></p><p>The colors class also provides a series of black and white colors. The numbers included in the name describe their opacity level.</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/04/image-2.png" class="kg-image" alt="Creating Colors in Flutter with examples" loading="lazy" width="450" height="294"></figure><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/04/image-3.png" class="kg-image" alt="Creating Colors in Flutter with examples" loading="lazy" width="450" height="378"></figure><p><strong>Transparent</strong></p><p>The <code>Colors.transparent</code> color represents a fully transparent color.</p><h2 id="the-color-class">The Color Class</h2><p>The <a href="https://api.flutter.dev/flutter/dart-ui/Color-class.html?ref=mags.ai">Color</a> class (note the lack of a trailing s!) is what we&apos;ll use to create our own custom colors. </p><p><strong>HEX Colors</strong></p><p>We can create a color using HEX values with the <code>Color</code> constructor</p><pre><code class="language-dart">static const c = Color(0xFF42A5F5);</code></pre><ul><li>Prefix the value with <code>0x</code></li><li>The first 2 digits are the opacity, a hex code from 0 to FF</li><li>The last 6 digits is the HEX color code</li></ul><p><strong>ARGB Colors</strong></p><p>We can create a color using ARGB values with the <code>Color.fromARGB</code> constructor</p><pre><code class="language-dart">static const c = Color.fromARGB(255, 66, 165, 245);</code></pre><ul><li>The first value is the alpha value, an <code>int</code> from 0 to 255</li><li>The last 3 values are the RGB colors, an <code>int</code> from 0 to 255</li><li>Using HEX values is supported</li></ul><p><strong>RGBO Colors</strong></p><p>We can create a color using RGBO values with the <code>Color.fromRGBO</code> constructor</p><pre><code class="language-dart">static const c = Color.fromRGBO(66, 165, 245, 1.0);</code></pre><ul><li>The first 3 values are the RGB colors, an <code>int</code> from 0 to 255</li><li>The last value is the opacity, a <code>double</code> from 0.0 to 1.0</li><li>Using HEX values is supported for the first 3 values</li></ul><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Creating Colors in Flutter with examples">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger]]></title><description><![CDATA[Using ScaffoldMessenger to display a SnackBar in a Dialog with Flutter]]></description><link>https://mags.ai/revisiting-snackbars-in-dialogs-with-flutter/</link><guid isPermaLink="false">63c410d042658e021185715b</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Thu, 31 Mar 2022 16:21:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1607706189992-eae578626c86?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGNvZGluZyUyMHNjYWZmb2xkfGVufDB8fHx8MTY0ODc0Mzg1MQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1607706189992-eae578626c86?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGNvZGluZyUyMHNjYWZmb2xkfGVufDB8fHx8MTY0ODc0Mzg1MQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger"><p>In a previous article we looked at the surprisingly non-trivial way to display a SnackBar from within a Dialog context in Flutter. </p><p>The full article gives additional context but &#xA0;the TLDR was:</p><ul><li>A Scaffold context is required to display a SnackBar</li><li>Dialogs appear outside of Scaffold contexts</li><li>Wrap the Dialog with a Scaffold and handle the gesture to close edge cases</li></ul><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://mags.ai/blog/displaying-a-snackbar-in-a-dialog-with-flutter/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Displaying a Snackbar in a Dialog with Flutter</div><div class="kg-bookmark-description">A Flutter tutorial where we add a Snackbar to a Dialog</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://mags.ai/favicon.ico" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger"><span class="kg-bookmark-author">MagsAI</span><span class="kg-bookmark-publisher">Mags</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1551721434-8b94ddff0e6d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI1fHxtb2JpbGV8ZW58MHx8fHwxNjI5MzAyNTUy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger"></div></a></figure><p>Flutter 2.0 introduced ScaffoldMessenger as a replacement for displaying SnackBars directly from the Scaffold class. The main use case it seems to be trying to solve is synchronizing a SnackBar across navigation events. You can see this in action if you position two Scaffolds on the same page and dispatch a SnackBar from one of them:</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2022/03/Kapture-2022-03-31-at-15.16.21-2.gif" class="kg-image" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger" loading="lazy" width="321" height="733"></figure><p>When watching the <a href="https://www.youtube.com/watch?v=lytQi-slT5Y&amp;ref=mags.ai">Flutter Widget of the Week</a> on ScaffoldMessenger I was excited to see that it sits above all Scaffolds in the hierarchy and came built into the MaterialApp widget.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mags.ai/content/images/2022/03/Screen-Shot-2022-03-31-at-15.27.55.png" class="kg-image" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger" loading="lazy" width="1394" height="1020" srcset="https://mags.ai/content/images/size/w600/2022/03/Screen-Shot-2022-03-31-at-15.27.55.png 600w, https://mags.ai/content/images/size/w1000/2022/03/Screen-Shot-2022-03-31-at-15.27.55.png 1000w, https://mags.ai/content/images/2022/03/Screen-Shot-2022-03-31-at-15.27.55.png 1394w" sizes="(min-width: 720px) 720px"><figcaption>Credit: Flutter YouTube Channel</figcaption></figure><p>Perhaps this was the solution to the SnackBar in a Dialog problem? To save ourselves some time, unfortunately, it wasn&apos;t. &#x1F62D;</p><p>Despite that there are some notable differences that we can take a look at, and our final solution is a bit more streamlined.</p><h2 id="revised-solution">Revised Solution</h2><pre><code class="language-dart">class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          child: const Text(&quot;Show Dialog&quot;),
          onPressed: () {
            showDialog(
              useSafeArea: false,
              context: context,
              builder: (context) =&gt; Stack(
                children: [
                  const IgnorePointer(
                    child: Scaffold(
                      backgroundColor: Colors.transparent,
                    ),
                  ),
                  SafeArea(
                    child: AlertDialog(
                      title: const Text(&quot;SnackBar Dialog&quot;),
                      content: ElevatedButton(
                        child: const Text(&quot;Show SnackBar&quot;),
                        onPressed: () {
                          ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                              content: Text(&quot;Hello, SnackBar!&quot;),
                            ),
                          );
                        },
                      ),
                    ),
                  ),
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}
</code></pre><p>The full code is available on my GitHub. Major changes from the previous solution are broken down below</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/MagsMagnoli/flutter-scaffold-messenger?ref=mags.ai"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - MagsMagnoli/flutter-scaffold-messenger: A demonstration of displaying snackbars inside of a modal using ScaffoldMessenger</div><div class="kg-bookmark-description">A demonstration of displaying snackbars inside of a modal using ScaffoldMessenger - GitHub - MagsMagnoli/flutter-scaffold-messenger: A demonstration of displaying snackbars inside of a modal using ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">MagsMagnoli</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/efbf9e34eecc295faa15be3c57b4227e4c1a680cfcb747fa94ee8e9b31ba4c80/MagsMagnoli/flutter-scaffold-messenger" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger"></div></a></figure><h3 id="no-more-gesture-detectors">No More Gesture Detectors</h3><p>By wrapping the Dialog with a Scaffold we needed to introduce GestureDetectors to handle touch events on the Dialog backdrop. With ScaffoldMessenger we no longer need the Scaffold to be a direct parent of the AlertDialog and using a Stack widget allows us to place the &#xA0;outside of the Dialog widget tree. This allows us to leverage the IgnorePointer widget instead of GestureDetector.</p><h3 id="no-more-errors">No More Errors</h3><p>In the previous article, calling <code>Scaffold.of</code> in the Dialog threw an error and required us to wrap it with a Scaffold. With <code>ScaffoldMessenger.of</code> we no longer throw an error but now display a SnackBar in the Scaffold underneath it.</p><h3 id="caveat-safearea">Caveat: SafeArea</h3><p>The <code>showDialog</code> function wraps its content with a SafeArea widget by default. This causes SnackBars to be displayed above the bottom edge of the device. To fix this issue disable the <code>useSafeArea</code> flag on the <code>showDialog</code> function then wrap the AlertDialog with a SafeArea manually.</p><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Protect Yourself From the Twitch Security Breach]]></title><description><![CDATA[A brief walkthrough on how to protect your Twitch.tv account from the recent data breach]]></description><link>https://mags.ai/protect-yourself-from-the-twitch-security-breach/</link><guid isPermaLink="false">63c410d042658e0211857158</guid><category><![CDATA[Tech]]></category><category><![CDATA[Security]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Wed, 06 Oct 2021 13:12:55 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1604398525509-ce4af98fdb23?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEzfHxrZXklMjBsb2NrJTIwc2VjdXJpdHl8ZW58MHx8fHwxNjMzNTI2MzY1&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1604398525509-ce4af98fdb23?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEzfHxrZXklMjBsb2NrJTIwc2VjdXJpdHl8ZW58MHx8fHwxNjMzNTI2MzY1&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Protect Yourself From the Twitch Security Breach"><p>On October 6th, 2021 a massive leak of <a href="https://twitch.tv/?ref=mags.ai">Twitch.tv</a> data was revealed which among other proprietary information exposed sensitive user data. As a result all users should take necessary precautions to secure their accounts.</p><p>In this article we will walk through:</p><ul><li>How to change your password</li><li>How to enable two factor authentication</li></ul><h2 id="change-your-password">Change Your Password</h2><p>In order to change your password you&apos;ll need to have your current password as well as a desired new password handy. We recommend using a password manager such as <a href="https://bitwarden.com/?ref=mags.ai">bitwarden </a>to generate and store strong passwords securely. If you prefer to only create a password then use this <a href="https://bitwarden.com/password-generator/?ref=mags.ai">strong password generator</a></p><ul><li>Visit the <a href="https://www.twitch.tv/settings/security?ref=mags.ai">security and privacy</a> tab of the Twitch settings page</li><li>Click &apos;change password&apos; under the security heading</li></ul><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="931" height="87" srcset="https://mags.ai/content/images/size/w600/2021/10/image.png 600w, https://mags.ai/content/images/2021/10/image.png 931w" sizes="(min-width: 720px) 720px"></figure><ul><li>Fill out the form with your old and new passwords then click &apos;change password&apos;</li></ul><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image-1.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="648" height="527" srcset="https://mags.ai/content/images/size/w600/2021/10/image-1.png 600w, https://mags.ai/content/images/2021/10/image-1.png 648w"></figure><h2 id="enable-two-factor-authentication-2fa">Enable Two-Factor Authentication (2FA)</h2><p>Since passwords can be brute forced, two-factor authentication adds another layer of security to your password protected account. When enabled, a short-lived one-time code generated from a third party must be provided along with your password. We recommend using <a href="https://authy.com/?ref=mags.ai">Authy</a> which is available to download on most operating systems.</p><ul><li>Visit the <a href="https://www.twitch.tv/settings/security?ref=mags.ai">security and privacy</a> tab of the Twitch settings page</li><li>Click &apos;set up two-factor authentication&apos; under the security heading</li></ul><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image-2.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="906" height="136" srcset="https://mags.ai/content/images/size/w600/2021/10/image-2.png 600w, https://mags.ai/content/images/2021/10/image-2.png 906w" sizes="(min-width: 720px) 720px"></figure><ul><li>Click &apos;enable 2fa&apos; in the dialog</li></ul><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image-3.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="646" height="540" srcset="https://mags.ai/content/images/size/w600/2021/10/image-3.png 600w, https://mags.ai/content/images/2021/10/image-3.png 646w"></figure><ul><li>Enter your phone number then click &apos;continue&apos;</li></ul><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image-4.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="642" height="545" srcset="https://mags.ai/content/images/size/w600/2021/10/image-4.png 600w, https://mags.ai/content/images/2021/10/image-4.png 642w"></figure><ul><li>Enter the 7 digit code sent to your phone number via SMS</li></ul><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image-5.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="658" height="559" srcset="https://mags.ai/content/images/size/w600/2021/10/image-5.png 600w, https://mags.ai/content/images/2021/10/image-5.png 658w"></figure><p>If you&apos;ve set up <a href="https://authy.com/?ref=mags.ai">Authy</a> previously then you should be all set, otherwise you may be prompted with a QR code to scan</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/10/image-6.png" class="kg-image" alt="Protect Yourself From the Twitch Security Breach" loading="lazy" width="383" height="503"></figure><h2 id="further-reading">Further Reading</h2><p>The more we rely on the internet to power our daily lives the higher the risk to our personal well-being data breaches will become. It&apos;s important to become familiar with the latest security measures and to employ them whenever possible. </p><p>Some things that should be on your radar:</p><ol><li>Avoid using <a href="https://www.zdnet.com/article/microsoft-urges-users-to-stop-using-phone-based-multi-factor-authentication/?ref=mags.ai">SMS as your two-factor authenticator</a></li><li>Learn about <a href="https://www.pocket-lint.com/gadgets/news/150395-best-hardware-security-keys-for-two-factor-authentication-and-how-do-they-work?ref=mags.ai">security keys</a></li></ol><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Protect Yourself From the Twitch Security Breach">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Add reCAPTCHA to a Svelte App]]></title><description><![CDATA[A tutorial on how to add reCAPTCHA to a Svelte application]]></description><link>https://mags.ai/add-recaptcha-to-a-svelte-app/</link><guid isPermaLink="false">63c410d042658e0211857153</guid><category><![CDATA[Svelte]]></category><category><![CDATA[SvelteKit]]></category><category><![CDATA[reCAPTCHA]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Mon, 27 Sep 2021 00:40:33 GMT</pubDate><media:content url="https://mags.ai/content/images/2021/08/Artboard-1-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://mags.ai/content/images/2021/08/Artboard-1-1.png" alt="Add reCAPTCHA to a Svelte App"><p>Any unprotected forms on the public internet are susceptible to spam attacks by malicious bots. Solutions such as reCAPTCHA have emerged which mitigate such attacks by presenting suspected bots with a challenge to complete. This tutorial will walk through adding reCAPTCHA to a Svelte application. </p><p>Follow along in the repository on GitHub and consider <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">sponsoring my work</a> for more articles &#x2764;&#xFE0F;</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/MagsMagnoli/svelte-recaptcha-tutorial?ref=mags.ai"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - MagsMagnoli/svelte-recaptcha-tutorial: A tutorial for adding recaptcha to a svelte app</div><div class="kg-bookmark-description">A tutorial for adding recaptcha to a svelte app. Contribute to MagsMagnoli/svelte-recaptcha-tutorial development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Add reCAPTCHA to a Svelte App"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">MagsMagnoli</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/cbbc74ccd5b2d104b98ddcb654b4aba5a96ba9c5c0e9364b6b3d1cd3089ccf8b/MagsMagnoli/svelte-recaptcha-tutorial" alt="Add reCAPTCHA to a Svelte App"></div></a></figure><h2 id="setting-up-recaptcha">Setting up reCAPTCHA</h2><p>Head over to the reCAPTCHA <a href="https://www.google.com/recaptcha/admin/create?ref=mags.ai">admin console</a> to register your website and generate your site key and secret key. </p><p>For this tutorial we&apos;re going to be selecting the <strong>reCAPTCHA v2 invisible reCAPTCHA badge</strong>. reCAPTCHA v3 doesn&apos;t present users with a challenge and is best used for complex application flows. The invisible badge fits our simple requirements and is less intrusive than the &quot;I&apos;m not a robot&quot; checkbox.</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/Screen-Shot-2021-08-19-at-8.56.42-AM-1.png" class="kg-image" alt="Add reCAPTCHA to a Svelte App" loading="lazy" width="786" height="399" srcset="https://mags.ai/content/images/size/w600/2021/08/Screen-Shot-2021-08-19-at-8.56.42-AM-1.png 600w, https://mags.ai/content/images/2021/08/Screen-Shot-2021-08-19-at-8.56.42-AM-1.png 786w" sizes="(min-width: 720px) 720px"></figure><p>After completing the setup form you should be presented with your reCAPTCHA keys. The site key will be used on the frontend and the secret key will be used on the backend. </p><p><strong>Do not, under any circumstance, commit the secret key to source control or expose it in any way outside of your application environment!</strong></p><p>Limiting exposure of the site key is a good habit but since it will be visible on the frontend it isn&apos;t as important as securing the secret key.</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/Group-1-2.png" class="kg-image" alt="Add reCAPTCHA to a Svelte App" loading="lazy" width="817" height="319" srcset="https://mags.ai/content/images/size/w600/2021/08/Group-1-2.png 600w, https://mags.ai/content/images/2021/08/Group-1-2.png 817w" sizes="(min-width: 720px) 720px"></figure><h2 id="application-overview">Application Overview</h2><p>Now that we have everything we need to setup reCAPTCHA it&apos;s time to code! </p><p>The demo application we&apos;re going to be working with is a simple login form that records how many requests are sent to the server. We can see that each click of the login button sends a request to the backend. </p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/Kapture-2021-08-21-at-19.43.11-1.gif" class="kg-image" alt="Add reCAPTCHA to a Svelte App" loading="lazy" width="1000" height="1000" srcset="https://mags.ai/content/images/size/w600/2021/08/Kapture-2021-08-21-at-19.43.11-1.gif 600w, https://mags.ai/content/images/2021/08/Kapture-2021-08-21-at-19.43.11-1.gif 1000w" sizes="(min-width: 720px) 720px"></figure><p>In it&apos;s current state this is a prime target for abuse &#x1F613; but we&apos;re going to fix that with reCAPTCHA &#x1F601;</p><h2 id="implementing-recaptcha-on-the-frontend">Implementing reCAPTCHA on the frontend</h2><p>The first thing we need to do is to add the reCAPTCHA library dependency to our project. We&apos;ll do this by incorporating a script tag into the head of our html.</p><pre><code class="language-html">&lt;script src=&quot;https://www.google.com/recaptcha/api.js&quot; async defer&gt;&lt;/script&gt;</code></pre><p>Note that we are using <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script?ref=mags.ai#attr-async">async</a></code> and <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script?ref=mags.ai#attr-defer">defer</a></code> to prevent parser-blocking JavaScript.</p><p>With the library in place let&apos;s now add the following <code>div</code> element to our form:</p><pre><code class="language-html">&lt;div
    class=&quot;g-recaptcha&quot;
    data-sitekey={RECAPTCHA_SITE_KEY}
    data-callback=&quot;handleCaptchaCallback&quot;
    data-expired-callback=&quot;resetCaptcha&quot;
    data-error-callback=&quot;handleCaptchaError&quot;
    data-size=&quot;invisible&quot;
/&gt;</code></pre><p>This element employs a number of <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes?ref=mags.ai">data attributes</a> which are defined by the the reCAPTCHA library. The full list of available options are described in detail <a href="https://developers.google.com/recaptcha/docs/invisible?ref=mags.ai#render_param">here</a> so for brevity:</p><ul><li><code>data-sitekey</code> - the site key we obtained earlier</li><li><code>data-callback</code> - a function to call after a successful reCAPTCHA invocation</li><li><code>data-expired</code> - a function to call when the reCAPTCHA session has expired</li><li><code>data-error</code> - a function to call after a failed reCAPTCHA invocation</li><li><code>data-size</code> - instructs reCAPTCHA we&apos;ll be using an invisible widget</li></ul><p>Setting the site key can be as simple as hard coding it on the element but since we&apos;re here to learn let&apos;s introduce it as an environment variable &#x1F604;</p><h3 id="environment-variables-in-sveltekit">Environment variables in SvelteKit</h3><p>SvelteKit supports environment variables natively if we do the following:</p><ul><li>create a <code>.env</code> file at the root of our project</li><li>prefix any environment variable we want to use on the frontend with <code>VITE_</code></li><li>for typescript support, add the variable to <code>ImportMetaEnv</code> type definition</li><li>access the variable in code</li></ul><p>It&apos;s important to note that any environment variable we want to use on the server side only, such as the secret key, should <strong>NOT</strong> be prefixed with <code>VITE_</code> and need to be loaded with a library such as <a href="https://github.com/motdotla/dotenv?ref=mags.ai#readme">dotenv</a>.</p><p>Also remember to <strong>NEVER</strong> commit secret environment variables to version control!</p><p>First we create a <code>.env</code> file at the root of our project</p><pre><code class="language-bash">VITE_RECAPTCHA_SITE_KEY=YOUR_SITE_KEY
RECAPTCHA_SECRET_KEY=YOUR_SECRET_KEY</code></pre><p>Then we update our typescript definitions in <code>lib/types.d.ts</code>:</p><pre><code class="language-javascript">interface ImportMetaEnv {
  VITE_RECAPTCHA_SITE_KEY: string;
}</code></pre><p>And finally we can access the variable we set on the div element in <code>index.svelte</code>:</p><pre><code class="language-javascript">const RECAPTCHA_SITE_KEY = import.meta.env.VITE_RECAPTCHA_SITE_KEY;</code></pre><h3 id="recaptcha-callbacks">ReCAPTCHA callbacks</h3><p>There are three callback functions we defined on the reCAPTCHA element that we need to implement. Unfortunately it&apos;s not as easy as assigning each to a function we define in Svelte. reCAPTCHA looks for these functions on the <code>window</code> object so we need to assign our functions to it first. To enable Typescript support we must add the following type definition to our <code>lib/types.d.ts</code> file:</p><pre><code class="language-typescript">export declare global {
  interface Window {
    handleCaptchaCallback: (token: string) =&gt; Promise&lt;void&gt;;
    resetCaptcha: () =&gt; void;
    handleCaptchaError: () =&gt; void;
  }
}</code></pre><p>Then in the <code>onMount</code> of <code>index.svelte</code> we make the assignments:</p><pre><code class="language-typescript">onMount(() =&gt; {
    window.handleCaptchaCallback = handleCaptchaCallback;
    window.handleCaptchaError = handleCaptchaError;
    window.resetCaptcha = resetCaptcha;
});</code></pre><p>We&apos;ll implement <code>handleCaptchaError</code> as a function that displays an error string to the user:</p><pre><code class="language-javascript">const handleCaptchaError = () =&gt; {
    error = &apos;Recaptcha error. Please reload the page&apos;;
};</code></pre><p>We then add a simple error message inside our form. Be sure to initialize <code>error</code> as an empty string at the top of <code>index.svelte</code>:</p><pre><code class="language-html">{#if error}
    &lt;div&gt;
        &lt;small class=&quot;text-yellow-300 font-bold&quot;&gt;{error}&lt;/small&gt;
    &lt;/div&gt;
{/if}</code></pre><p>If the user doesn&apos;t fill out the form after a period of time their current session will expire. Instead of displaying an error when they submit the form after this point, we&apos;ll implement <code>resetCaptcha</code> to automatically refresh our session thereby keeping the form always in a stable state.</p><pre><code class="language-javascript">const resetCaptcha = () =&gt; {
    window.grecaptcha.reset();
};</code></pre><p><code>grecaptcha</code> is a global variable created by the reCAPTCHA library. Install the <a href="https://www.npmjs.com/package/@types/grecaptcha?ref=mags.ai">grecaptcha</a> package on npm to make it available in Typescript. </p><p>The final callback to implement is <code>handleCaptchaCallback</code> which is executed after reCAPTCHA completes successfully. It is called with a token that we will need to send to our backend. We&apos;ll move the networking logic from our <code>handleSubmit</code> function into this callback and replace it with logic that calls out to reCAPTCHA to process.</p><pre><code class="language-javascript">const handleCaptchaCallback = async (token: string) =&gt; {
    await fetch(&apos;/api/login&apos;, {
        method: &apos;POST&apos;,
        headers: {
        &apos;Content-Type&apos;: &apos;application/json&apos;
        },
        body: JSON.stringify({
            username,
            password,
            recaptchaToken: token
        })
    });

    requests += 1;
    
    // reset recaptcha for future requests
    resetCaptcha();
};

const handleSubmit = () =&gt; {
    // reset any errors
    error = &apos;&apos;;
    
    // tell recaptcha to process a request
    window.grecaptcha.execute();
};</code></pre><h2 id="implementing-recaptcha-on-the-backend">Implementing reCAPTCHA on the backend</h2><p>We&apos;re beginning the final stretch! The server is responsible for verifying the authenticity of the reCAPTCHA token. In order to do that we need to send a <code>POST</code> request to a special endpoint. This request requires our <code>RECAPTCHA_SECRET_KEY</code> environment variable in our <code>.env</code> file. To initialize environment variables on the server, first install dotenv then import it at the top of <code>login.ts</code>. Now our variables will be accessible on the <code>process.env</code>:</p><pre><code class="language-javascript">import dotenv from &apos;dotenv&apos;;

dotenv.config();

const { RECAPTCHA_SECRET_KEY } = process.env;</code></pre><p>Although not required, reCAPTCHA accepts an ip address in the verification request. This value is available to us as the SvelteKit <code>RequestHandler</code> object&apos;s <code>host</code> property.</p><p>With these three values in hand, we can now make a validation request, then either send the user a failure message or continue forward in our server processing:</p><pre><code class="language-javascript">const recaptchaVerifyResponse = await fetch(
    `https://www.google.com/recaptcha/api/siteverify?   secret=${RECAPTCHA_SECRET_KEY}&amp;response=${recaptchaToken}&amp;remoteip=${host}`,
    {
        method: &apos;POST&apos;
    }
);

const json: {
    success: boolean;
} = await recaptchaVerifyResponse.json();

if (!json.success) {
    return {
        status: 400,
        body: &apos;ReCAPTCHA failed. Please try again&apos;
    };
}</code></pre><p>On the frontend we modified our request to process the response and display the error string if present. We also made a user class for convenience in <code>types.d.ts</code></p><pre><code class="language-javascript">// index.svelte

const json: User | string = await response.json();

if (response.status === 200) {
    // do something with response
} else {
    // show error message
    error = json as string;
}

// types.d.ts

export type User = {
    id: number;
    username: string;
};</code></pre><p>We successfully secured our login form with reCAPTCHA! &#x1F389; &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/09/Kapture-2021-09-26-at-20.27.14.gif" class="kg-image" alt="Add reCAPTCHA to a Svelte App" loading="lazy" width="1106" height="1050" srcset="https://mags.ai/content/images/size/w600/2021/09/Kapture-2021-09-26-at-20.27.14.gif 600w, https://mags.ai/content/images/size/w1000/2021/09/Kapture-2021-09-26-at-20.27.14.gif 1000w, https://mags.ai/content/images/2021/09/Kapture-2021-09-26-at-20.27.14.gif 1106w" sizes="(min-width: 720px) 720px"></figure><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Add reCAPTCHA to a Svelte App">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Quick Tip: SvelteKit Endpoint Body Parsing]]></title><description><![CDATA[While building out the SvelteKit application for an upcoming tutorial I ran into a peculiar issue with the request body]]></description><link>https://mags.ai/sveltekit-endpoint-body-parsing/</link><guid isPermaLink="false">63c410d042658e0211857156</guid><category><![CDATA[SvelteKit]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Sun, 22 Aug 2021 13:22:15 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1544197150-b99a580bb7a8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDExfHxzZXJ2ZXJ8ZW58MHx8fHwxNjI5NjM4NTgx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1544197150-b99a580bb7a8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDExfHxzZXJ2ZXJ8ZW58MHx8fHwxNjI5NjM4NTgx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Quick Tip: SvelteKit Endpoint Body Parsing"><p>While building out the <a href="https://kit.svelte.dev/?ref=mags.ai">SvelteKit</a> application for an upcoming tutorial I ran into a peculiar issue with the request body: the content was populated but I was receiving <code>undefined</code> whenever I tried to access its properties.</p><pre><code class="language-typescript">export const post: RequestHandler&lt;void, { username: string }&gt; = 
async ({ body }) =&gt; {
  console.log(body); // {&quot;username&quot;:&quot;username&quot;, &quot;password&quot;:&quot;password&quot;}

  const { username } = body;

  console.log(username); // undefined

...</code></pre><p>If we take a look at the frontend code we can start to piece together what&apos;s happening:</p><pre><code class="language-typescript">const onSubmit = async () =&gt; {
  await fetch(&apos;/api/login&apos;, {
    method: &apos;POST&apos;,
    body: JSON.stringify({
      username,
      password
  })
});</code></pre><p>We&apos;re using <code>fetch</code> to send a POST request to our server and it requires us to send the body as a string. Despite typing the <code>RequestHandler</code> as an object it&apos;s simply passing along that string! </p><p>SvelteKit natively supports parsing incoming data as an object <strong>as long as the Content-Type: application/json</strong> header is added to the request. This is what the updated code looks like:</p><pre><code class="language-typescript">const onSubmit = async () =&gt; {
  await fetch(&apos;/api/login&apos;, {
    method: &apos;POST&apos;,
    headers: {
      &apos;Content-Type&apos;: &apos;application/json&apos;
    },
    body: JSON.stringify({
      username,
      password
  })
});</code></pre><p>And now we&apos;ll see that our problem is fixed! &#x1F389;</p><pre><code class="language-typescript">export const post: RequestHandler&lt;void, { username: string }&gt; = 
async ({ body }) =&gt; {
  console.log(body); // { username: &quot;username&quot;, password: &quot;password&quot;}

  const { username } = body;

  console.log(username); // username

...</code></pre><h3 id="alternate-solution">Alternate Solution</h3><p>Another approach would be to explicitly call <code>JSON.parse</code> on the incoming JSON string. Note that I switched to properly typing the <code>RequestHandler</code> body as a string.</p><pre><code class="language-typescript">export const post: RequestHandler&lt;void, string&gt; = async ({ body }) =&gt; {
  console.log(body); // {&quot;username&quot;:&quot;username&quot;, &quot;password&quot;:&quot;password&quot;}

  const { username } = JSON.parse(body);
	
  console.log(username); // username

...</code></pre><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Quick Tip: SvelteKit Endpoint Body Parsing">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Improving Flutter Performance with Provider]]></title><description><![CDATA[A Flutter tutorial where we are going to improve the performance of an application by leveraging the Provider package]]></description><link>https://mags.ai/improving-flutter-performance-with-provider/</link><guid isPermaLink="false">63c410d042658e0211857152</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Mon, 16 Aug 2021 16:24:09 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1495645260813-e4dc8f003a1e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEzfHxzcGVlZCUyMGZhc3R8ZW58MHx8fHwxNjI5MzAyNjIw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><img src="https://images.unsplash.com/photo-1495645260813-e4dc8f003a1e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEzfHxzcGVlZCUyMGZhc3R8ZW58MHx8fHwxNjI5MzAyNjIw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Improving Flutter Performance with Provider"><p>Every mobile developer knows the importance of delivering a buttery-smooth, 60fps (and increasingly 120fps) app. Flutter prides itself by being performant by default, but sometimes extra steps are needed to optimize particularly heavy UIs. We&#x2019;ll take a look at once such example and solve it using the <code>provider</code> package.</p><p>The code for this article can be found in the repository below:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/MagsMagnoli/flutter-provider-optimization?ref=mags.ai"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - MagsMagnoli/flutter-provider-optimization</div><div class="kg-bookmark-description">Contribute to MagsMagnoli/flutter-provider-optimization development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Improving Flutter Performance with Provider"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">MagsMagnoli</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/60e95a1d94ccf526aeddfd805a692a14ae4ee4461a1306146c9a4dc236c8063b/MagsMagnoli/flutter-provider-optimization" alt="Improving Flutter Performance with Provider"></div></a></figure><pre><code class="language-dart">class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() =&gt; _HomePageState();
}

class _HomePageState extends State&lt;HomePage&gt; {
  int counter = 0;
  int items = 10;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Text(&apos;$counter&apos;, style: TextStyle(fontSize: 20)),
                SizedBox(height: 16),
                Wrap(
                  children: Iterable.generate(items)
                      .map((e) =&gt; ExpensiveItem(
                            position: e + 1,
                            counter: counter,
                          ))
                      .toList(),
                )
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          setState(() {
            counter += 1;
          });
        },
      ),
    );
  }
}</code></pre><p>Each item initializes with a loading spinner to simulate being expensive to create. If the number contained in the list item is a factor of the counter then its background will be randomly assigned a color, otherwise it will be white.</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/1_zwfWzzoXPkEu4m9qEKfrSg-1-.gif" class="kg-image" alt="Improving Flutter Performance with Provider" loading="lazy" width="226" height="500"></figure><p>Currently when the counter is increased the call to <code>setState</code><strong><strong> </strong></strong>causes<strong><strong> every</strong></strong> list item to be rebuilt. This is unnecessary for items that will have the same state for consecutive values of the counter. In the next section we&#x2019;ll see how the <code>provider</code> package can help us optimize the rebuilding of these widgets.</p><h2 id="optimizing-with-provider">Optimizing With Provider</h2><h3 id="install-the-provider-package">Install The Provider Package</h3><p>Add the <a href="https://pub.dev/packages/provider/install?ref=mags.ai" rel="noopener nofollow">provider package</a> to your <code>pubspec.yaml</code> file</p><h3 id="optimize-the-list">Optimize The List</h3><p>The first thing we&#x2019;ll do is extract the list to its own widget with a <code>const</code> constructor so it won&#x2019;t be rebuilt every time we call <code>setState</code>. This is because in Dart <a href="https://dart.dev/guides/language/language-tour?ref=mags.ai#final-and-const" rel="noopener nofollow">a const variable becomes a compile-time constant</a>.</p><p>Now we&#x2019;ll wrap this new <code>ExpensiveItemList</code> widget with a <code>Provider</code> widget and pass it the <code>counter</code> variable. Doing so makes <code>counter</code> available to widgets further down the tree.</p><pre><code class="language-dart">class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() =&gt; _HomePageState();
}

class _HomePageState extends State&lt;HomePage&gt; {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Text(&apos;$counter&apos;, style: TextStyle(fontSize: 20)),
                SizedBox(height: 16),
                Provider&lt;int&gt;.value(
                    value: counter,
                    builder: (_, __) =&gt; const ExpensiveItemList())
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          setState(() {
            counter += 1;
          });
        },
      ),
    );
  }
}

class ExpensiveItemList extends StatelessWidget {
  final items = 10;

  const ExpensiveItemList();

  @override
  Widget build(BuildContext context) {
    return Wrap(
      children: Iterable.generate(items)
          .map((e) =&gt; ExpensiveItem(
                position: e + 1,
              ))
          .toList(),
    );
  }
}</code></pre><h3 id="optimize-the-list-item">Optimize The List Item</h3><p>The <code>ExpensiveItem</code> Widget no longer receives the <code>counter</code> variable in its constructor but it still needs access to it in order to know when to rebuild. We&#x2019;ll use the <code>select</code> extension on <code>BuildContext</code> which <code>provider</code> gives us to only listen to when the item switches from not being a factor to being a factor, and vice versa.</p><pre><code class="language-dart">class ExpensiveItem extends StatelessWidget {
  final int position;

  const ExpensiveItem({Key key, this.position}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final size = (MediaQuery.of(context).size.width / 5) - 32 / 5;
    final isFactor = context
        .select&lt;int, bool&gt;((value) =&gt; value &gt; 0 &amp;&amp; position % value == 0);
    ...
  }
}</code></pre><h2 id="results">Results</h2><p>After implementing the changes, re-run the app and you&#x2019;ll notice that only the items that need to update their UI are being rebuilt!</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/1_E-poHUSumWIlRibpMzNQaw-1-.gif" class="kg-image" alt="Improving Flutter Performance with Provider" loading="lazy" width="226" height="500"></figure><h2 id="conclusion">Conclusion</h2><p>We successfully used the provider package to optimize our Flutter app. Congratulations! &#x1F973;</p><p>Note that, as is usually the case, <strong><strong>this isn&#x2019;t the only way we could have done this. </strong></strong>There are other packages and state management solutions we could have used instead. What matters is that we chose a solution that got the job done in a reasonable amount of time.</p><p>The key takeaway here is this:</p><blockquote>When you have a Widget that is particularly expensive to create, isolate!</blockquote><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Improving Flutter Performance with Provider">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Displaying a SnackBar in a Dialog with Flutter]]></title><description><![CDATA[A Flutter tutorial where we add a Snackbar to a Dialog]]></description><link>https://mags.ai/displaying-a-snackbar-in-a-dialog-with-flutter/</link><guid isPermaLink="false">63c410d042658e0211857151</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Mags]]></dc:creator><pubDate>Mon, 16 Aug 2021 13:22:52 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1551721434-8b94ddff0e6d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI1fHxtb2JpbGV8ZW58MHx8fHwxNjI5MzAyNTUy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-red"><div class="kg-callout-emoji">&#x2757;</div><div class="kg-callout-text">This post uses an outdated API. Check out the revised post using the recommended <code>ScaffoldMessenger</code> below</div></div><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://mags.ai/blog/revisiting-snackbars-in-dialogs-with-flutter/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Revisiting Displaying a SnackBar in a Dialog with SnackBarMessenger</div><div class="kg-bookmark-description">Using ScaffoldMessenger to display a SnackBar in a Dialog with Flutter</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://mags.ai/favicon.ico" alt="Displaying a SnackBar in a Dialog with Flutter"><span class="kg-bookmark-author">MagsAI</span><span class="kg-bookmark-publisher">Mags</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1607706189992-eae578626c86?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGNvZGluZyUyMHNjYWZmb2xkfGVufDB8fHx8MTY0ODc0Mzg1MQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Displaying a SnackBar in a Dialog with Flutter"></div></a></figure><img src="https://images.unsplash.com/photo-1551721434-8b94ddff0e6d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDI1fHxtb2JpbGV8ZW58MHx8fHwxNjI5MzAyNTUy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Displaying a SnackBar in a Dialog with Flutter"><p>According to the <a href="https://material.io/components/dialogs?ref=mags.ai" rel="noopener">material design specification</a>, dialogs &#x201C;inform users about a task and can contain critical information, require decisions, or involve multiple tasks.&#x201D; Sometimes we&#x2019;ll want these tasks to perform network requests, and it&#x2019;s common practice to display errors in a <a href="https://material.io/components/snackbars?ref=mags.ai" rel="noopener">Snackbar</a>. I wanted to create this behavior in my recently released project for the 6x6 challenge (which you can read more about <a href="https://medium.com/@MagsMagnoli/6x6-project-1-dota-draft-simulator-995d7440c99d?ref=mags.ai">here</a>) and found that it was slightly non-trivial. In this article I document my journey towards a solution that I&#x2019;m happy with.</p><p>I&#x2019;ll be demonstrating each step using a simplified example app. Our goal is to display a Snackbar after clicking a button in a dialog. You can follow along by downloading the GitHub repo. Each branch represents a step of the process</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/MagsMagnoli/flutter-dialog-snackbar?ref=mags.ai"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - MagsMagnoli/flutter-dialog-snackbar</div><div class="kg-bookmark-description">Contribute to MagsMagnoli/flutter-dialog-snackbar development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Displaying a SnackBar in a Dialog with Flutter"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">MagsMagnoli</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/e3bff65223c2d8bfc531a6fbb70b4a94a3567c7ef679c8ff7f51083a82dd231d/MagsMagnoli/flutter-dialog-snackbar" alt="Displaying a SnackBar in a Dialog with Flutter"></div></a></figure><p>Let&#x2019;s begin.</p><h2 id="a-naive-first-attempt">A Naive First Attempt</h2><p>We know that a Snackbar must be shown using a context which contains a scaffold in its parent tree. One may naively attempt to trigger the Snackbar in the <em>onPressed</em> method of the button of the AlertDialog as demonstrated below:</p><pre><code class="language-dart">class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text(&apos;Show Dialog&apos;),
          onPressed: () {
            showDialog(
              context: context,
              builder: (context) =&gt; AlertDialog(
                title: Text(&apos;SnackBar Dialog&apos;),
                content: RaisedButton(
                  child: Text(&apos;Show SnackBar&apos;),
                  onPressed: () {
                    Scaffold.of(context).showSnackBar(
                      SnackBar(content: Text(&apos;Hello, SnackBar!&apos;)),
                    );
                  },
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}</code></pre><p>Doing so will result in the following error:</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/Screen-Shot-2021-08-16-at-6.22.17-AM.png" class="kg-image" alt="Displaying a SnackBar in a Dialog with Flutter" loading="lazy" width="2000" height="371" srcset="https://mags.ai/content/images/size/w600/2021/08/Screen-Shot-2021-08-16-at-6.22.17-AM.png 600w, https://mags.ai/content/images/size/w1000/2021/08/Screen-Shot-2021-08-16-at-6.22.17-AM.png 1000w, https://mags.ai/content/images/size/w1600/2021/08/Screen-Shot-2021-08-16-at-6.22.17-AM.png 1600w, https://mags.ai/content/images/2021/08/Screen-Shot-2021-08-16-at-6.22.17-AM.png 2008w" sizes="(min-width: 720px) 720px"></figure><p>We wrapped our <em><em>showDialog</em></em> call with a scaffold so what gives? By inspecting the widget hierarchy in DevTools we see our problem quite clearly:</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/image.png" class="kg-image" alt="Displaying a SnackBar in a Dialog with Flutter" loading="lazy" width="716" height="658" srcset="https://mags.ai/content/images/size/w600/2021/08/image.png 600w, https://mags.ai/content/images/2021/08/image.png 716w"></figure><p>AlertDialogs displayed using <em><em>showDialog</em></em> are placed above the MaterialApp and are not contained inside the calling MyHomePage which creates the Scaffold.</p><h2 id="iteration-one-scaffold">Iteration One: Scaffold</h2><p>We can easily solve the scaffold issue from the previous section by wrapping our AlertDialog in another Scaffold. Be sure to use a Builder widget as the root of the body parameter to make the Scaffold available to the context given to the <em><em>Scaffold.of</em></em> which is used to display the Snackbar. We also need to make the background of the Scaffold transparent to persist the translucent effect of the AlertDialog.</p><pre><code class="language-dart">class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text(&apos;Show Dialog&apos;),
          onPressed: () {
            showDialog(
              context: context,
              builder: (context) =&gt; Scaffold(
                backgroundColor: Colors.transparent,
                body: Builder(
                  builder: (context) =&gt; AlertDialog(
                    title: Text(&apos;SnackBar Dialog&apos;),
                    content: RaisedButton(
                      child: Text(&apos;Show SnackBar&apos;),
                      onPressed: () {
                        Scaffold.of(context).showSnackBar(
                          SnackBar(content: Text(&apos;Hello, SnackBar!&apos;)),
                        );
                      },
                    ),
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}</code></pre><p>Pulling up DevTools again after these changes shows that our AlertDialog now has a Scaffold widget as its parent:</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/image-1.png" class="kg-image" alt="Displaying a SnackBar in a Dialog with Flutter" loading="lazy" width="716" height="758" srcset="https://mags.ai/content/images/size/w600/2021/08/image-1.png 600w, https://mags.ai/content/images/2021/08/image-1.png 716w"></figure><p>And running the app gives us the visuals we were expecting! &#x1F389;</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/image-2.png" class="kg-image" alt="Displaying a SnackBar in a Dialog with Flutter" loading="lazy" width="1080" height="2160" srcset="https://mags.ai/content/images/size/w600/2021/08/image-2.png 600w, https://mags.ai/content/images/size/w1000/2021/08/image-2.png 1000w, https://mags.ai/content/images/2021/08/image-2.png 1080w" sizes="(min-width: 720px) 720px"></figure><p>Sadly we are not done yet. &#x1F613;</p><p>If you interact with the app you&#x2019;ll notice a problem. We&#x2019;ve lost the ability to dismiss the dialog by touching outside of the content area. This is because we now have an invisible Scaffold filling the screen.</p><h2 id="iteration-two-gestures">Iteration Two: Gestures</h2><p>The dialog can be programmatically dismissed by calling the <em><em>pop</em></em> method on the Navigator class. We need to make this available to taps on the scrim area of the dialog and <strong><strong>not on clicks onto the dialog itself. </strong></strong>This can be achieved by using <strong><strong>two</strong></strong> GestureDetectors, one which wraps the entire Scaffold and makes the call to dismiss, and another that wraps the AlertDialog and prevents taps from being propagated to the parent one.</p><pre><code class="language-dart">class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text(&apos;Show Dialog&apos;),
          onPressed: () {
            showDialog(
              context: context,
              builder: (context) =&gt; GestureDetector(
                onTap: () =&gt; Navigator.of(context).pop(),
                child: Scaffold(
                  backgroundColor: Colors.transparent,
                  body: Builder(
                    builder: (context) =&gt; GestureDetector(
                      onTap: () {},
                      child: AlertDialog(
                        title: Text(&apos;SnackBar Dialog&apos;),
                        content: RaisedButton(
                          child: Text(&apos;Show SnackBar&apos;),
                          onPressed: () {
                            Scaffold.of(context).showSnackBar(
                              SnackBar(content: Text(&apos;Hello, SnackBar!&apos;)),
                            );
                          },
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}</code></pre><p>This is the final widget hierarchy:</p><figure class="kg-card kg-image-card"><img src="https://mags.ai/content/images/2021/08/image-3.png" class="kg-image" alt="Displaying a SnackBar in a Dialog with Flutter" loading="lazy" width="662" height="838" srcset="https://mags.ai/content/images/size/w600/2021/08/image-3.png 600w, https://mags.ai/content/images/2021/08/image-3.png 662w"></figure><h2 id="wrapping-up">Wrapping Up</h2><p>Thanks for reading! If you enjoyed this article, please consider sharing it with others and <a href="https://github.com/sponsors/MagsMagnoli?ref=mags.ai">becoming a sponsor</a> on GitHub. &#x2764;&#xFE0F;</p><!--kg-card-begin: html--><div class="koenig-embed-bookmark flex justify-center relative" data-kg-embed style="margin-top:16px">
    <figure class="kg-card kg-bookmark-card also-new-tag">
    <a href="https://discord.gg/Q9PKgckMgA?ref=mags.ai" class="kg-bookmark-container">
        <div class="kg-bookmark-content">
            <h4 class="kg-bookmark-title">Join the MagsAI Discord Server!</h4>
            <div class="kg-bookmark-description">MagsAI is a community of thoughtful learners interested in sharing their knowledge and growing together</div>
        </div>
        <div class="kg-bookmark-thumbnail">
            <img src="https://cdn.discordapp.com/icons/871733230800338954/5bb7a096adb8cd5eaa55e32fa23dedd5.jpg?size=256" alt="Displaying a SnackBar in a Dialog with Flutter">
        </div>
        </a>
    </figure>
    <div class="koenig-card-click-overlay ba b--transparent" data-kg-overlay></div>
</div><!--kg-card-end: html-->]]></content:encoded></item></channel></rss>