<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paul Halliday</title>
    <description>The latest articles on DEV Community by Paul Halliday (@paulhalliday).</description>
    <link>https://dev.to/paulhalliday</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F149998%2F45d55bb8-49d4-429d-a08b-4e0e1a3098eb.png</url>
      <title>DEV Community: Paul Halliday</title>
      <link>https://dev.to/paulhalliday</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulhalliday"/>
    <language>en</language>
    <item>
      <title>How to Create a BottomNavigationBar with Flutter</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Sun, 26 Apr 2020 14:30:17 +0000</pubDate>
      <link>https://dev.to/paulhalliday/how-to-create-a-bottomnavigationbar-with-flutter-9f2</link>
      <guid>https://dev.to/paulhalliday/how-to-create-a-bottomnavigationbar-with-flutter-9f2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YGZi-VGw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/tab.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YGZi-VGw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/tab.jpg" alt="How to Create a BottomNavigationBar with Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we'll be creating a &lt;code&gt;BottomNavigationBar&lt;/code&gt; with the ability to switch between different tabs using &lt;a href="https://api.flutter.dev/flutter/widgets/IndexedStack-class.html"&gt;IndexedStack&lt;/a&gt;. You'll want to use this when creating tab based user interface(s) with their own navigation stack.&lt;/p&gt;

&lt;p&gt;Our example application will be simple. It'll consist of four screens total, three of which are "main" tab pages, the final one being a detail page that we push on the navigation stack.&lt;/p&gt;

&lt;h4&gt;
  
  
  New Project
&lt;/h4&gt;

&lt;p&gt;Let's create a new Flutter project in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# New Flutter project
$ flutter create flutter_shopping

# Open in editor
$ cd flutter_tabs &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Page Creation
&lt;/h4&gt;

&lt;p&gt;We can start off by creating our &lt;em&gt;main&lt;/em&gt; pages that will be displayed in our tab system. These will be simple &lt;code&gt;StatelessWidget&lt;/code&gt;s and the content doesn't matter for our tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/presentation/shop/pages/shop_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_shopping/presentation/product_detail/pages/product_detail_page.dart';

class ShopPage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (context) =&amp;gt; ShopPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Shop"),
      ),
      body: Center(
        child: FlatButton(
          onPressed: () =&amp;gt; Navigator.of(context).push(
            ProductDetailPage.route(),
          ),
          child: Text("Navigate to Product Detail Page"),
        ),
      ),
    );
  }
}


/// lib/presentation/home/pages/home_page.dart
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (context) =&amp;gt; HomePage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
      ),
      body: Center(
        child: Text("Hello, Home!"),
      ),
    );
  }
}


/// lib/presentation/search/pages/search_page.dart
import 'package:flutter/material.dart';

class SearchPage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (context) =&amp;gt; SearchPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Search"),
      ),
      body: Center(
        child: Text("Hello, Search!"),
      ),
    );
  }
}


/// lib/presentation/product_detail/pages/product_detail_page.dart
import 'package:flutter/material.dart';

class ProductDetailPage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (context) =&amp;gt; ProductDetailPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Product Detail"),
      ),
      body: Center(
        child: Text("Hello, Product!"),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  TabPage and BottomNavigationBar
&lt;/h4&gt;

&lt;p&gt;Now that we've got the ability to show various pages (i.e. &lt;code&gt;HomePage&lt;/code&gt;, &lt;code&gt;SearchPage&lt;/code&gt;, and so on), we'll create a &lt;code&gt;TabPage&lt;/code&gt;. This page will use &lt;code&gt;IndexedStack&lt;/code&gt; to display a different body &lt;em&gt;depending&lt;/em&gt; on the current tab selected by the user.&lt;/p&gt;

&lt;p&gt;In order to make this easier to generate, let's create a &lt;code&gt;TabNavigationItem&lt;/code&gt; to hold information about our tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/presentation/tabs/models/tab_navigation_item.dart
import 'package:flutter/widgets.dart';

class TabNavigationItem {
  final Widget page;
  final Widget title;
  final Icon icon;

  TabNavigationItem({
    @required this.page,
    @required this.title,
    @required this.icon,
  });

  static List&amp;lt;TabNavigationItem&amp;gt; get items =&amp;gt; [
        TabNavigationItem(
          page: HomePage(),
          icon: Icon(Icons.home),
          title: Text("Home"),
        ),
        TabNavigationItem(
          page: ShopPage(),
          icon: Icon(Icons.shopping_basket),
          title: Text("Shop"),
        ),
        TabNavigationItem(
          page: SearchPage(),
          icon: Icon(Icons.search),
          title: Text("Search"),
        ),
      ];
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use this &lt;em&gt;and our other pages&lt;/em&gt; to create a &lt;code&gt;TabsPage&lt;/code&gt; which will use our aforementioned &lt;code&gt;IndexedStack&lt;/code&gt; with a &lt;code&gt;_currentIndex&lt;/code&gt; that changes whenever a user &lt;code&gt;taps&lt;/code&gt; a tab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_shopping/presentation/home/pages/home_page.dart';
import 'package:flutter_shopping/presentation/search/pages/search_page.dart';
import 'package:flutter_shopping/presentation/shop/pages/shop_page.dart';
import 'package:flutter_shopping/presentation/tabs/models/tab_navigation_item.dart';

class TabsPage extends StatefulWidget {
  @override
  _TabsPageState createState() =&amp;gt; _TabsPageState();
}

class _TabsPageState extends State&amp;lt;TabsPage&amp;gt; {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: [
          for (final tabItem in TabNavigationItem.items) tabItem.page,
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (int index) =&amp;gt; setState(() =&amp;gt; _currentIndex = index),
        items: [
          for (final tabItem in TabNavigationItem.items)
            BottomNavigationBarItem(
              icon: tabItem.icon,
              title: tabItem.title,
            )
        ],
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How does IndexedStack work?
&lt;/h4&gt;

&lt;p&gt;Given a list of Widgets (i.e. the &lt;code&gt;children&lt;/code&gt; of &lt;code&gt;HomePage&lt;/code&gt;, &lt;code&gt;ShopPage&lt;/code&gt; and &lt;code&gt;SearchPage&lt;/code&gt;), it'll display the Widget where the &lt;code&gt;children&lt;/code&gt; matches the &lt;code&gt;currentIndex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if our &lt;code&gt;currentIndex&lt;/code&gt; was &lt;code&gt;1&lt;/code&gt; and our &lt;code&gt;children&lt;/code&gt; array looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;children: [
  HomePage()
  ShopPage()
  SearchPage()
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;IndexedStack&lt;/code&gt; would display the &lt;code&gt;ShopPage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We'll now be able to update our &lt;code&gt;main.dart&lt;/code&gt; to place &lt;code&gt;TabsPage&lt;/code&gt; as our &lt;code&gt;home&lt;/code&gt; entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_shopping/presentation/tabs/pages/tabs_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'App',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: TabsPage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the following result:&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;We've now used the &lt;code&gt;IndexedStack&lt;/code&gt; to switch our displayed widget depending on the item selected by the user!&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/flutter_indexed_stack_tab_view"&gt;https://github.com/PaulHalliday/flutter_indexed_stack_tab_view&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>#2 Developing World of Warcraft AddOns - Music Player</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Sat, 25 Apr 2020 20:19:48 +0000</pubDate>
      <link>https://dev.to/paulhalliday/2-developing-world-of-warcraft-addons-music-player-156a</link>
      <guid>https://dev.to/paulhalliday/2-developing-world-of-warcraft-addons-music-player-156a</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7vsvHelw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/wow_p2-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7vsvHelw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/wow_p2-1.jpg" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/paulhalliday/1-developing-world-of-warcraft-addons-hello-world-2m7l-temp-slug-945809"&gt;Part One of our Developing World of Warcraft AddOns series&lt;/a&gt; we looked at how to create a basic "Hello, World!" AddOn. We managed to load it up in game and display a message on screen, both at initialisation time &lt;em&gt;and&lt;/em&gt; with a slash command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hVVDWyEl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-21.29.08-min.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hVVDWyEl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-21.29.08-min.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;Zolten gave me 16g whilst writing this article. He's a cool guy!&lt;/p&gt;

&lt;p&gt;Today we'll be looking at how to play music and sound file(s) with our AddOns and creating a sound board, of sorts. It'll be slash command based, so the user will be able to type in something like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;/playsound ding&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/playsound murlocs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/playsound main theme&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/playsound custom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/stopsound&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can imagine, each one allows us to play a particular sound &lt;em&gt;or&lt;/em&gt; music file. We'll also be able to play &lt;em&gt;custom&lt;/em&gt; sounds that we've added directly to our AddOn!&lt;/p&gt;

&lt;h4&gt;
  
  
  New AddOn
&lt;/h4&gt;

&lt;p&gt;Let's create a new AddOn folder at &lt;code&gt;Interface/AddOns/MusicPlayer&lt;/code&gt; with the following &lt;code&gt;MusicPlayer.toc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Interface: 11303
## Title: MusicPlayer
## Notes: Play sounds and music inside of the game.
## Author: Paul Halliday
## Version: 0.0.1

MusicPlayer.lua
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Finding Sounds
&lt;/h4&gt;

&lt;p&gt;The easiest way to find sounds from World of Warcraft is to head over to Wowhead and find the in-game ID. For example, if we wanted to add the &lt;code&gt;MurlocAggro&lt;/code&gt; sound, we'd likely search for &lt;code&gt;Murloc&lt;/code&gt; and then click &lt;code&gt;Links &amp;gt; Copy In-game Sound Command&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XOk72Aql--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-17.35.38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XOk72Aql--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-17.35.38.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives us the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/script PlaySound(416)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Try it in-game. You'll hear mrlrlrmrmrrrgllkrkglrl!





&lt;p&gt;We can also go ahead and do the same for the &lt;code&gt;Ding!&lt;/code&gt; noise:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--afgFxLf2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-17.44.25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--afgFxLf2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-17.44.25.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives us the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/script PlaySound(888)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&amp;gt; Ding! &amp;gt; Grats!





&lt;p&gt;We can also find game music with sites such as &lt;a href="https://wow.tools"&gt;wow.tools&lt;/a&gt; which we can play using &lt;code&gt;PlayMusic(id)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eJa_LD_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-18.59.42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eJa_LD_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-18.59.42.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This can be played in-game by typing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/script PlayMusic("Sound\\Music\\GlueScreenMusic\\wow_main_theme.mp3")
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll need to ensure that you've got music enabled for this to work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're making an AddOn for retail, you'll want to use the fileId instead, i.e. &lt;code&gt;PlayMusic(53223)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We're then able to stop the music by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/script StopMusic()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
That's enough title music for now... :)





&lt;p&gt;Given what we now know, we can create a little &lt;code&gt;MusicPlayer&lt;/code&gt; which allows us to play sounds with a slash command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's time to write some code!&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Registering Slash Commands
&lt;/h4&gt;

&lt;p&gt;The first thing that we're going to do is register a slash command for our sound player. We'll want to register both &lt;code&gt;/playsound&lt;/code&gt; and &lt;code&gt;/stopsound&lt;/code&gt; inside of &lt;code&gt;MusicPlayer.lua&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLASH_SOUND1 = "/playsound"
SLASH_STOPSOUND1 = "/stopsound"

local function playSoundHandler()
  print("Play sound!")
end

local function stopSoundHandler()
  print("Stop sound!")
end

SlashCmdList["SOUND"] = playSoundHandler;
SlashCmdList["STOPSOUND"] = stopSoundHandler;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we type &lt;code&gt;/reload&lt;/code&gt; in-game, we should be able to see our messages if we type either of our slash commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hYLjRWuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-20.24.39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hYLjRWuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-20.24.39.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Displaying Sounds
&lt;/h4&gt;

&lt;p&gt;In order to &lt;em&gt;play&lt;/em&gt; these sounds with our AddOn, we can make a &lt;code&gt;table&lt;/code&gt; named &lt;code&gt;sounds&lt;/code&gt; which contains information about the sound we'd like to play.&lt;/p&gt;

&lt;p&gt;This allows us to either make a decision on which method we want to call to &lt;em&gt;play the sound&lt;/em&gt;, or display information on screen to the user &lt;em&gt;about the sound&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local soundType = {
    SOUND = 1,
    GAME_MUSIC = 2,
    CUSTOM = 3
}

local sounds = {
    ["murloc"] = {
        ["sound"] = 416,
        ["description"] = "Mglrlrlrlrlrl!",
        ["type"] = soundType.SOUND
    },
    ["ding"] = {
        ["sound"] = 888,
        ["description"] = "Grats!",
        ["type"] = soundType.SOUND
    },
    ["main theme"] = {
        ["sound"] = "Sound\\Music\\GlueScreenMusic\\wow_main_theme.mp3",
        ["description"] = "DUN DUNNN... DUNNNNNNNNNN",
        ["type"] = soundType.GAME_MUSIC
    },
    ["custom"] = {
        ["sound"] = "Interface\\AddOns\\MusicPlayer\\Sounds\\custom.mp3",
        ["description"] = "Custom sound!",
        ["type"] = soundType.CUSTOM
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Feel free to add your own sounds here and experiment with different sounds from Wowhead.&lt;/p&gt;

&lt;p&gt;I've also added a sound called &lt;code&gt;custom.mp3&lt;/code&gt; inside of a new folder I created at &lt;code&gt;AddOns/MusicPlayer/Sounds&lt;/code&gt;. This represents the ability to play a sound outside of the standard game files.&lt;/p&gt;

&lt;p&gt;There isn't anything special about the folder name &lt;code&gt;Sounds&lt;/code&gt;, so feel free to add a custom sound wherever suits your use case. If you'd like to play your own sounds, go ahead and add it to the directory and &lt;code&gt;sounds&lt;/code&gt; table like I did.&lt;/p&gt;

&lt;p&gt;We can now create a new function named &lt;code&gt;displaySoundList&lt;/code&gt; which will be used to print the potential sounds on screen to the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local function displaySoundList()
    print("----------------------------")
    for command in pairs(sounds) do
        local description = sounds[command].description
        print("Command: /playsound " .. command .. " - Description: " .. description)
    end
    print("----------------------------")
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to use this, update your &lt;code&gt;playSoundHandler&lt;/code&gt; to call the &lt;code&gt;displaySoundList&lt;/code&gt; whenever we activate this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local function playSoundHandler()
  displaySoundList()
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eZuHBYZz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-20.40.30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eZuHBYZz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-20.40.30.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Playing Sounds
&lt;/h4&gt;

&lt;p&gt;Now that we've got a &lt;em&gt;very helpful sound list&lt;/em&gt;, the user knows that they can type a particular command to play a sound!&lt;/p&gt;

&lt;p&gt;Let's now wire up that functionality to actually &lt;em&gt;play&lt;/em&gt; a sound by updating our &lt;code&gt;playSoundHandler&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local function playSoundHandler(trackId)
  if(string.len(trackId) &amp;gt; 0) then
      local matchesKnownTrack = sounds[trackId] ~= nil

      if (matchesKnownTrack) then
          local track = sounds[trackId]

          playTrack(track)
      else
          displaySoundList()

          print(trackId .. " - Doesn't match a known track.")
      end
  else
      displaySoundList()
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our control flow now looks like the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If there has been a &lt;code&gt;track&lt;/code&gt; passed in by the user &lt;em&gt;and&lt;/em&gt; it &lt;em&gt;exists&lt;/em&gt;, play the track.&lt;/li&gt;
&lt;li&gt;If there has been a &lt;code&gt;track&lt;/code&gt; passed in by the user &lt;em&gt;and&lt;/em&gt; it &lt;em&gt;doesn't exist,&lt;/em&gt; display the sound list and a message.&lt;/li&gt;
&lt;li&gt;If there is no &lt;code&gt;track&lt;/code&gt;, display the sound list.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's write our &lt;code&gt;playTrack&lt;/code&gt; method so that we can actually &lt;em&gt;play&lt;/em&gt; a sound!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local customSoundId

local function playTrack(track)
  print(track.description)

  if(track.type == soundType.GAME_MUSIC) then
      PlayMusic(track.sound)

      print("To stop the music type /stopsound")
  elseif(track.type == soundType.SOUND) then
      PlaySound(track.sound)
  elseif(track.type == soundType.CUSTOM) then
      stopSoundHandler()
      customSoundId = select(2, PlaySoundFile(track.sound))
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We're now able to either &lt;code&gt;PlayMusic(track.sound)&lt;/code&gt;, &lt;code&gt;PlaySound(track.sound)&lt;/code&gt; or &lt;code&gt;PlaySoundFile(track.sound)&lt;/code&gt; depending on the type that we determined inside of our &lt;code&gt;sounds&lt;/code&gt; table earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AkHrCE-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-21.26.45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AkHrCE-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-25-at-21.26.45.png" alt="#2 Developing World of Warcraft AddOns - Music Player"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how we're creating a &lt;code&gt;customSoundId&lt;/code&gt; and only defining a value when we use &lt;code&gt;PlaySoundFile&lt;/code&gt; as this returns us an &lt;code&gt;id&lt;/code&gt; which we can use to &lt;em&gt;stop&lt;/em&gt; the sound later in the next step.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stopping Sounds
&lt;/h4&gt;

&lt;p&gt;For the next step, I'm going to assume that any &lt;em&gt;sound&lt;/em&gt; is just a few seconds long and a user &lt;em&gt;wouldn't want&lt;/em&gt; to type &lt;code&gt;/stopsound&lt;/code&gt; to cancel it. A user may want to stop &lt;em&gt;music&lt;/em&gt; and custom sound files though.&lt;/p&gt;

&lt;p&gt;Let's update our &lt;code&gt;stopSoundHandler&lt;/code&gt; to support this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local function stopSoundHandler()
  StopMusic()

  if(customSoundId ~= nil) then
      StopSound(customSoundId)
      customSoundId = nil
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We're now able to &lt;em&gt;stop&lt;/em&gt; sounds using &lt;code&gt;/stopsound&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;You can try this out if you either play a custom sound file &lt;em&gt;or&lt;/em&gt; some music with &lt;code&gt;PlayMusic&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;In the second part of our Developing WoW AddOns series we created a little Music Player! It's not going to win &lt;em&gt;AddOn of the year award&lt;/em&gt; by any stretch just yet, but we hopefully learned a little more about playing sound files.&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/wow_addon_music_player"&gt;https://github.com/PaulHalliday/wow_addon_music_player&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wowaddons</category>
    </item>
    <item>
      <title>#1 Developing World of Warcraft AddOns - Hello World</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Wed, 22 Apr 2020 16:24:06 +0000</pubDate>
      <link>https://dev.to/paulhalliday/1-developing-world-of-warcraft-addons-hello-world-1bk3</link>
      <guid>https://dev.to/paulhalliday/1-developing-world-of-warcraft-addons-hello-world-1bk3</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ytrjOm6a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/wow_p1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ytrjOm6a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/wow_p1.jpg" alt="#1 Developing World of Warcraft AddOns - Hello World"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been playing quite a bit of World of Warcraft Classic lately. I used to play way back in ~2005 and it's been a large part of my life. I've never developed any AddOns for WoW before, but I figured it'd be a fun experiment to document my progress learning how to create WoW AddOns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The video for this article is on the way! Subscribe &lt;a href="https://youtube.com/c/PaulHalliday?sub_confirmation=1"&gt;here&lt;/a&gt; to make sure you don't miss it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;WoW AddOns are created using the &lt;a href="https://www.lua.org/"&gt;Lua scripting language&lt;/a&gt; and development can be done in the editor of your choice. I'm using &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; for this with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Septh.wow-bundle"&gt;WoW Bundle&lt;/a&gt; plugin installed for colorisation and other features.&lt;/p&gt;

&lt;p&gt;With that said - let's dive right in and create our first AddOn!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating our WoW Addon
&lt;/h2&gt;

&lt;p&gt;Start off by navigating to your World of Warcraft AddOns directory (this will be  &lt;code&gt;World of Warcraft/_classic_/Interface/AddOns&lt;/code&gt; in my instance) and make a new folder called &lt;code&gt;HelloWorld&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;This is where we'll be placing all of the code related to our AddOn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;p&gt;Make a new file here named &lt;code&gt;HelloWorld.toc&lt;/code&gt; with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Interface: 11304
## Title: HelloWorld
## Notes: Prints "Hello, World" to the chat.
## Author: YourName
## Version: 0.0.1

HelloWorld.lua
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: The .toc file must have the &lt;em&gt;same name&lt;/em&gt; as the AddOn's folder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is known as a &lt;code&gt;Table of Contents&lt;/code&gt; file and allows us to provide metadata about our AddOn as well as files to be loaded at runtime. Here we're essentially stating that we want to load &lt;code&gt;HelloWorld.lua&lt;/code&gt; and our AddOn is named &lt;code&gt;HelloWorld&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;Interface&lt;/code&gt; number will match the current WoW patch and allows the WoW client to determine whether an AddOn is out of date. This number is different for Classic and Retail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are numerous ways to get the current &lt;code&gt;Interface&lt;/code&gt; id, but the easiest way is to type the following in-game:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/run print((select(4, GetBuildInfo())));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZrvFNajj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-23-at-13.38.32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZrvFNajj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-23-at-13.38.32.png" alt="#1 Developing World of Warcraft AddOns - Hello World"&gt;&lt;/a&gt;Build Number: 11304&lt;/p&gt;

&lt;p&gt;Alternatively, you could copy this from another AddOn that &lt;em&gt;isn't&lt;/em&gt; out of date from within your AddOns folder.&lt;/p&gt;

&lt;p&gt;If we boot up the game and click &lt;code&gt;AddOns&lt;/code&gt; on the bottom left of the character selection screen, we should be able to see our &lt;code&gt;HelloWorld&lt;/code&gt; AddOn!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bvSY_qtD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-22-at-16.15.58-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bvSY_qtD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-22-at-16.15.58-1.png" alt="#1 Developing World of Warcraft AddOns - Hello World"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hello, World!
&lt;/h3&gt;

&lt;p&gt;Before we test this with our AddOn, let's verify what we're expecting to see by running it as a &lt;code&gt;/script message("Hello, World!")&lt;/code&gt; inside of the WoW Chat UI.&lt;/p&gt;

&lt;p&gt;We should see a box come up on our screen saying &lt;code&gt;Hello, World!&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Great. We can now reproduce this with our &lt;code&gt;HelloWorld&lt;/code&gt; AddOn.&lt;/p&gt;

&lt;p&gt;Our AddOn will load the &lt;code&gt;HelloWorld.toc&lt;/code&gt; look for the &lt;code&gt;HelloWorld.lua&lt;/code&gt; that we've stated should be loaded at runtime. We can go ahead and create this in the same &lt;code&gt;HelloWorld&lt;/code&gt; folder with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;message("Hello, World!")
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Any time we make changes to our AddOn, we'll need to run &lt;code&gt;/reload&lt;/code&gt; in the chat to be able to see our changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this circumstance, because we've added a brand new file, we'll have to restart the WoW client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we've done everything correctly, we should see &lt;code&gt;Hello, World&lt;/code&gt; in game &lt;em&gt;without&lt;/em&gt; having to type anything in the chat!&lt;/p&gt;

&lt;h4&gt;
  
  
  Player Name
&lt;/h4&gt;

&lt;p&gt;We can also make this a little more special by using the current &lt;code&gt;player&lt;/code&gt; name by accessing the &lt;code&gt;UnitName&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local name = UnitName("player")

message("Hello, " .. name .. "!")
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This should now say, &lt;code&gt;Hello {name}!&lt;/code&gt; where the &lt;code&gt;name&lt;/code&gt; is equal to your &lt;code&gt;player&lt;/code&gt; name. We can see this in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qXBHy_SZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/hello_player_name.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qXBHy_SZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/hello_player_name.png" alt="#1 Developing World of Warcraft AddOns - Hello World"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Slash Command
&lt;/h3&gt;

&lt;p&gt;We could even add a slash command to make this more dynamic. What if we had a slash command that we could type &lt;code&gt;/helloworld&lt;/code&gt; or &lt;code&gt;/helloworld name&lt;/code&gt; and this would show a custom message?&lt;/p&gt;

&lt;p&gt;We can update our &lt;code&gt;HelloWorld.lua&lt;/code&gt; to support this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLASH_HELLO1 = "/helloworld"

local function HelloWorldHandler(name)
    if(string.len(name) &amp;gt; 0) then
        message("Hello, " .. name .. "!")
    else
        local playerName = UnitName("player")
        message("Hello, " .. playerName .. "!")
    end
end

SlashCmdList["HELLO"] = HelloWorldHandler;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We now get one of two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If we pass &lt;em&gt;nothing&lt;/em&gt; into &lt;code&gt;/helloworld&lt;/code&gt; it'll show &lt;code&gt;Hello, {playerName}!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If we pass a name into &lt;code&gt;/helloworld john&lt;/code&gt; it'll say &lt;code&gt;Hello, John!&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;SlashCmdList["HELLO"]&lt;/code&gt; will point any slash command registered with &lt;code&gt;SLASH_HELLO{x}&lt;/code&gt; toward our &lt;code&gt;HelloWorldHandler&lt;/code&gt;, so we could register another command such as &lt;code&gt;/msg&lt;/code&gt; with and get the same result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLASH_HELLO1 = "/helloworld"
SLASH_HELLO2 = "/msg"

local function HelloWorldHandler(name)
    if(string.len(name) &amp;gt; 0) then
        message("Hello, " .. name .. "!")
    else
        local playerName = UnitName("player")
        message("Hello, " .. playerName .. "!")
    end
end

SlashCmdList["HELLO"] = HelloWorldHandler;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, we can clean up our &lt;code&gt;HelloWorldHandler&lt;/code&gt; to instead have a &lt;code&gt;showGreeting(name)&lt;/code&gt; which stops our repetitive &lt;code&gt;message&lt;/code&gt; call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLASH_HELLO1 = "/helloworld"
SLASH_HELLO2 = "/msg"

local function showGreeting(name)
    local greeting = "Hello, " .. name .. "!"

    message(greeting)
end

local function HelloWorldHandler(name)
    local nameExists = string.len(name) &amp;gt; 0

    if(nameExists) then
        showGreeting(name)
    else
        local playerName = UnitName("player")

        showGreeting(playerName)
    end
end

SlashCmdList["HELLO"] = HelloWorldHandler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;We've now made our first WoW AddOn! You can find the code for this article here: &lt;a href="https://github.com/PaulHalliday/wow_addon_hello_world"&gt;https://github.com/PaulHalliday/wow_addon_hello_world&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.school/developing-world-of-warcraft-addons-music-player-part-two/"&gt;Next up, let's make a Music Player in part two of this series!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wowaddons</category>
    </item>
    <item>
      <title>How to Use ProxyProvider with Flutter</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Tue, 21 Apr 2020 14:04:44 +0000</pubDate>
      <link>https://dev.to/paulhalliday/how-to-use-proxyprovider-with-flutter-3ifo</link>
      <guid>https://dev.to/paulhalliday/how-to-use-proxyprovider-with-flutter-3ifo</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FProxyProvider.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FProxyProvider.png" alt="How to Use ProxyProvider with Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we're going to look at how to use &lt;code&gt;ProxyProvider&lt;/code&gt; to inject data into &lt;em&gt;other&lt;/em&gt; providers. This is useful if we're wanting to inject an auth token or other piece of dynamic data into another &lt;code&gt;Provider&lt;/code&gt; at some point in the future.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ProxyProvider&lt;/code&gt; has an &lt;code&gt;update&lt;/code&gt; method which is called whenever one of its dependencies has updated. We'll see this in action in our example application by passing a &lt;code&gt;GreetingService&lt;/code&gt; a &lt;code&gt;UserService&lt;/code&gt; which is able to provide the current user.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2Fproxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2Fproxy.png" alt="How to Use ProxyProvider with Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;Let's create a new Flutter project in the terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# New Flutter project
$ flutter create proxyprovider

# Open in VS Code
$ cd proxyprovider &amp;amp;&amp;amp; code .


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We'll then need to add the &lt;code&gt;provider&lt;/code&gt; dependency to our &lt;code&gt;pubspec.yaml&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

dependencies:
  flutter:
    sdk: flutter

  provider: ^4.0.5


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's all the packages we need. You can now open the project up on the platform of your choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Login
&lt;/h3&gt;

&lt;p&gt;The first thing we'll do is create our &lt;code&gt;LoginForm&lt;/code&gt;. In our example we're using it to gather a &lt;code&gt;username&lt;/code&gt; to be greeted on the &lt;code&gt;HomePage&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/presentation/widgets/login_form.dart
import 'package:flutter/material.dart';
import 'package:proxyprovider/domain/entities/user.dart';

class LoginForm extends StatefulWidget {
  final Function(User) onFormSaved;

  const LoginForm({Key key, @required this.onFormSaved}) : super(key: key);

  @override
  _LoginFormState createState() =&amp;gt; _LoginFormState();
}

class _LoginFormState extends State&amp;lt;LoginForm&amp;gt; {
  bool _autoValidate;

  GlobalKey&amp;lt;FormState&amp;gt; _formKey;

  TextEditingController _usernameTextEditingController;
  TextEditingController _passwordTextEditingController;

  @override
  void initState() {
    super.initState();

    _autoValidate = false;

    _formKey = GlobalKey&amp;lt;FormState&amp;gt;();

    _usernameTextEditingController = TextEditingController();
    _passwordTextEditingController = TextEditingController();
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      autovalidate: _autoValidate,
      child: Column(
        children: [
          TextFormField(
              controller: _usernameTextEditingController,
              decoration: InputDecoration(labelText: "Username"),
              validator: (String value) =&amp;gt;
                  _validateFormField(value, "Username")),
          TextFormField(
              controller: _passwordTextEditingController,
              obscureText: true,
              decoration: InputDecoration(labelText: "Password"),
              validator: (String value) =&amp;gt;
                  _validateFormField(value, "Password")),
          FlatButton(
            onPressed: _onLoginPressed,
            child: Text("Login"),
          )
        ],
      ),
    );
  }

  _onLoginPressed() {
    setState(() {
      _autoValidate = true;
    });

    if (_formKey.currentState.validate()) {
      widget.onFormSaved(
        User(
          username: _usernameTextEditingController.text,
        ),
      );
    }
  }

  String _validateFormField(String value, String fieldName) {
    if (value.isEmpty) {
      return "$fieldName cannot be empty.";
    }

    return null;
  }

  @override
  void dispose() {
    _usernameTextEditingController.dispose();
    _passwordTextEditingController.dispose();

    super.dispose();
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our &lt;code&gt;User&lt;/code&gt; entity will be extremely bare. It'll have one property - &lt;code&gt;username&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/domain/entities/user.dart
import 'package:flutter/foundation.dart';

class User {
  final String username;

  User({@required this.username});
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can then create our &lt;code&gt;LoginPage&lt;/code&gt; which will use the &lt;code&gt;LoginForm&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/presentation/pages/login_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:proxyprovider/application/services/user_service.dart';
import 'package:proxyprovider/domain/entities/user.dart';
import 'package:proxyprovider/presentation/pages/home_page.dart';
import 'package:proxyprovider/presentation/widgets/login_form.dart';

class LoginPage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (BuildContext context) =&amp;gt; LoginPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Login"),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            LoginForm(
              onFormSaved: (User user) =&amp;gt; _onFormSaved(context, user),
            ),
          ],
        ),
      ),
    );
  }

  _onFormSaved(BuildContext context, User user) {
    Provider.of&amp;lt;UserService&amp;gt;(context, listen: false).setUser(user);
    Navigator.of(context).pushReplacement(HomePage.route());
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A review of what we've got so far:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We've got the ability to capture a &lt;code&gt;User&lt;/code&gt; object from our &lt;code&gt;LoginForm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;LoginPage&lt;/code&gt; shows the form, and when the &lt;code&gt;onFormSaved&lt;/code&gt; callback is fired we're calling &lt;code&gt;UserService.setUser(user)&lt;/code&gt; and navigating to the &lt;code&gt;HomePage&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Services
&lt;/h3&gt;

&lt;p&gt;We haven't created the &lt;code&gt;UserService&lt;/code&gt; or the &lt;code&gt;HomePage&lt;/code&gt; to support this use case. Let's do that now:&lt;/p&gt;

&lt;h4&gt;
  
  
  UserService
&lt;/h4&gt;

&lt;p&gt;Our &lt;code&gt;UserService&lt;/code&gt; will be a simple class that is able to &lt;code&gt;set&lt;/code&gt; and &lt;code&gt;get&lt;/code&gt; the current &lt;code&gt;user&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/application/services/user_service.dart
import 'package:proxyprovider/domain/entities/user.dart';

class UserService {
  User _user;
  User get user =&amp;gt; _user;

  setUser(User user) {
    _user = user;
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can update &lt;code&gt;main.dart&lt;/code&gt; to add our &lt;code&gt;UserService&lt;/code&gt; as a &lt;code&gt;Provider&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:proxyprovider/application/services/user_service.dart';
import 'package:proxyprovider/presentation/pages/login_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        Provider(
          create: (_) =&amp;gt; UserService(),
        ),
      ],
      child: MaterialApp(
        title: 'ProxyProvider',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          brightness: Brightness.dark,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: LoginPage(),
      ),
    );
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  GreetingService
&lt;/h4&gt;

&lt;p&gt;Our &lt;code&gt;GreetingService&lt;/code&gt; will take a &lt;code&gt;UserService&lt;/code&gt; in as a parameter and we'll use &lt;code&gt;ProxyProvider&lt;/code&gt; to inject this with the latest value from our &lt;code&gt;UserService&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/application/services/greeting_service.dart
import 'package:flutter/foundation.dart';
import 'package:proxyprovider/application/services/user_service.dart';

class GreetingService {
  GreetingService({@required UserService userService})
      : _userService = userService;

  UserService _userService;

  String get greeting =&amp;gt; "Hello, ${_userService.user.username}";
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  ProxyProvider
&lt;/h3&gt;

&lt;p&gt;Now that we've got both our services, we can update our &lt;code&gt;providers&lt;/code&gt; list inside of &lt;code&gt;main.dart&lt;/code&gt; to return the &lt;code&gt;GreetingService&lt;/code&gt; as a &lt;code&gt;Provider&lt;/code&gt; with the latest value from &lt;code&gt;UserService&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

MultiProvider(
  providers: [
    Provider(
      create: (_) =&amp;gt; UserService(),
    ),
    ProxyProvider&amp;lt;UserService, GreetingService&amp;gt;(
      update: (BuildContext context, UserService userService,
              GreetingService greetingService) =&amp;gt;
          GreetingService(userService: userService),
    ),
  ],
 //


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This means that we're now able to access the value of &lt;code&gt;GreetingService&lt;/code&gt; as a &lt;code&gt;Provider&lt;/code&gt; and we can be assured that any time our &lt;code&gt;UserService&lt;/code&gt; updates, our &lt;code&gt;GreetingService&lt;/code&gt; will be updated to match.&lt;/p&gt;

&lt;p&gt;We can see this in our &lt;code&gt;HomePage&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/presentation/pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:proxyprovider/application/services/greeting_service.dart';

class HomePage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (BuildContext context) =&amp;gt; HomePage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home Page"),
      ),
      body: Center(
        child: Text(
          Provider.of&amp;lt;GreetingService&amp;gt;(context).greeting,
        ),
      ),
    );
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Whatever we typed inside of our &lt;code&gt;LoginForm&lt;/code&gt; as a &lt;code&gt;username&lt;/code&gt; will now appear in the &lt;code&gt;Center&lt;/code&gt; of our &lt;code&gt;HomePage&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FSimulator-Screen-Shot---iPhone-11-Pro-Max---2020-04-21-at-14.24.29-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FSimulator-Screen-Shot---iPhone-11-Pro-Max---2020-04-21-at-14.24.29-1.png" alt="How to Use ProxyProvider with Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Injections
&lt;/h3&gt;

&lt;p&gt;What if we have more than one item that we want to inject as a &lt;code&gt;ProxyProvider&lt;/code&gt;? As of now we're only injecting the &lt;code&gt;UserService&lt;/code&gt;, but there may be times when we want to add more than one object.&lt;/p&gt;

&lt;p&gt;For this we have to do the same as before, but use &lt;code&gt;ProxyProvider2&lt;/code&gt;, &lt;code&gt;ProxyProvider2&lt;/code&gt;, &lt;code&gt;ProxyProvider3&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;Here's an example of how this may look with the use of &lt;code&gt;ProxyProvider2&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

MultiProvider(
  providers: [
    Provider(
      create: (_) =&amp;gt; UserService(),
    ),
    ProxyProvider&amp;lt;UserService, GreetingService&amp;gt;(
      update: (BuildContext context, UserService userService,
              GreetingService greetingService) =&amp;gt;
          GreetingService(userService: userService),
    ),
    ProxyProvider2&amp;lt;UserService, GreetingService, CartService&amp;gt;(
      update: (BuildContext context, UserService userService,
              GreetingService greetingService, CartService cartService) =&amp;gt;
          CartService(
        userService: userService,
        greetingService: greetingService,
      ),
    ),
  ],


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our &lt;code&gt;CartService&lt;/code&gt; does nothing interesting:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/foundation.dart';
import 'package:proxyprovider/application/services/greeting_service.dart';
import 'package:proxyprovider/application/services/user_service.dart';
import 'package:proxyprovider/domain/entities/user.dart';

class CartService {
  CartService({
    @required GreetingService greetingService,
    @required UserService userService,
  }) : _greetingService = greetingService,
        _userService = userService;

  GreetingService _greetingService;
  UserService _userService;

  String get cartGreeting =&amp;gt; _greetingService.greeting;
  User get user =&amp;gt; _userService.user;
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we were to update our &lt;code&gt;HomePage&lt;/code&gt; to instead use our &lt;code&gt;CartService&lt;/code&gt;, it'd look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

///lib/presentation/pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:proxyprovider/application/services/cart_service.dart';

class HomePage extends StatelessWidget {
  static Route&amp;lt;dynamic&amp;gt; route() =&amp;gt; MaterialPageRoute(
        builder: (BuildContext context) =&amp;gt; HomePage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home Page"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              Provider.of&amp;lt;CartService&amp;gt;(context).user.username,
            ),
            Text(
              Provider.of&amp;lt;CartService&amp;gt;(context).cartGreeting,
            ),
          ],
        ),
      ),
    );
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FSimulator-Screen-Shot---iPhone-11-Pro-Max---2020-04-21-at-15.00.42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FSimulator-Screen-Shot---iPhone-11-Pro-Max---2020-04-21-at-15.00.42.png" alt="How to Use ProxyProvider with Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In this article we looked at how to get started with &lt;code&gt;ProxyProvider&lt;/code&gt; to inject values that can be provided across our widget tree. I hope you found it useful!&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts in the comments section below!&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/flutter_proxyprovider" rel="noopener noreferrer"&gt;https://github.com/PaulHalliday/flutter_proxyprovider&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>UI Design: Sparkline Bar Chart in Flutter (YouTube Studio)</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Mon, 20 Apr 2020 17:45:49 +0000</pubDate>
      <link>https://dev.to/paulhalliday/ui-design-sparkline-bar-chart-in-flutter-youtube-studio-1kdg</link>
      <guid>https://dev.to/paulhalliday/ui-design-sparkline-bar-chart-in-flutter-youtube-studio-1kdg</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z6wP2t90--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/UI-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z6wP2t90--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/UI-1.png" alt="UI Design: Sparkline Bar Chart in Flutter (YouTube Studio)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we're going to look at how to replicate the Sparkline Bar Chart as seen in the Realtime Views section of the YouTube Studio application. This is quite a common chart and represents datasets at a small scale, allowing you to see results at a glance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Video
&lt;/h4&gt;

&lt;p&gt;Prefer to watch a video? Here's one I made for this article:&lt;/p&gt;

&lt;p&gt;Here's an example of the UI we'll be making in comparison to the YouTube Studio application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zS2jZeIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/UI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zS2jZeIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/UI.png" alt="UI Design: Sparkline Bar Chart in Flutter (YouTube Studio)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: I'm still learning how to best take advantage of the &lt;code&gt;CustomPainter&lt;/code&gt; and &lt;code&gt;Canvas&lt;/code&gt; with Flutter and I'm documenting my learning here. I'd love your feedback!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;With that in mind - let's make a new Flutter project in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# New Flutter project
$ flutter create flutter_sparkline

# Open in VS Code
$ cd flutter_sparkline &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll then create a new page at &lt;code&gt;presentation/pages/home_page.dart&lt;/code&gt; named &lt;code&gt;HomePage&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Hello, Flutter!"),
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then update our &lt;code&gt;main.dart&lt;/code&gt; accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_sparkline/presentation/pages/home_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sparkline',
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
        primaryColor: Colors.black,
      ),
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the Sparkline
&lt;/h3&gt;

&lt;p&gt;In order to build our Sparkline Chart we're going to use the &lt;code&gt;CustomPaint&lt;/code&gt; widget which takes a &lt;code&gt;CustomPainter&lt;/code&gt; which we'll define as &lt;code&gt;SparklinePainter&lt;/code&gt; at &lt;code&gt;lib/presentation/painters/sparkline_painter.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:math';

import 'package:flutter/material.dart';

class SparklinePainter extends CustomPainter {
  List&amp;lt;num&amp;gt; data;
  Color color;

  SparklinePainter({@required this.data, this.color});

  @override
  void paint(Canvas canvas, Size size) {
    num index = 0;
    num barSize = size.width / data.length;
    num maxValue = data.reduce(max).toDouble();

    for (num point in data) {
      num barHeight = (size.height * point / maxValue).roundToDouble();

      _drawBar(
        canvas: canvas,
        left: barSize * index,
        top: size.height - barHeight,
        width: barSize,
        height: barHeight,
      );
      index++;
    }
  }

  _drawBar({Canvas canvas, num left, num top, num width, num height}) {
    Paint paint = Paint()
      ..color = color ?? Colors.red
      ..style = PaintingStyle.fill
      ..strokeWidth = 8.0;

    Rect rect = Rect.fromLTWH(left, top, width, height);

    canvas.drawRect(rect, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) =&amp;gt; true;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can break the above down by the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're getting our initial set-up data such as the &lt;code&gt;barSize&lt;/code&gt; (depending on how many data points there are). We're also getting the &lt;code&gt;maxValue&lt;/code&gt; from the &lt;code&gt;data&lt;/code&gt; points passed in. This will be used to determine our &lt;code&gt;barHeight&lt;/code&gt; in a moment.&lt;/li&gt;
&lt;li&gt;For a particular list of numerical data (i.e. &lt;code&gt;[1, 2, 3, 4, 5]&lt;/code&gt;), firstly, determine the &lt;code&gt;barHeight&lt;/code&gt; from a &lt;code&gt;0-height&lt;/code&gt; scale.&lt;/li&gt;
&lt;li&gt;Then we're drawing a &lt;code&gt;Rect&lt;/code&gt; on screen with our specified coordinates.&lt;/li&gt;
&lt;li&gt;Finally, we're incrementing our &lt;code&gt;index&lt;/code&gt; in order to move the next &lt;code&gt;Rect&lt;/code&gt; along one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we've got our &lt;code&gt;SparklinePainter&lt;/code&gt;, we can use this inside of our &lt;code&gt;SparklineChart&lt;/code&gt; widget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_sparkline/presentation/painters/sparkline_painter.dart';

class SparklineChart extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 40,
      width: 100,
      child: CustomPaint(
        painter: SparklinePainter(
          color: Color(0xFF00a7cf),
          data: List.generate(
            40,
            (index) {
              Random rnd = Random();
              return rnd.nextInt(20) + 6;
            },
          ),
        ),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This currently generates some fake dummy data using &lt;code&gt;List.generate&lt;/code&gt;, but feel free to swap out the &lt;code&gt;data&lt;/code&gt; with some of your choosing.&lt;/p&gt;

&lt;h3&gt;
  
  
  YouTube UI
&lt;/h3&gt;

&lt;p&gt;Now all we need to do is update our &lt;code&gt;HomePage&lt;/code&gt; with some crude UI code that represents the top of the YouTube Studio app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_sparkline/sparkline_chart.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 5,
      child: Scaffold(
        appBar: buildAppBar(),
        backgroundColor: Colors.white.withOpacity(0.85),
        body: Column(children: [
          SizedBox(
            height: 8,
          ),
          buildRealtimeSparklineCard(),
        ]),
      ),
    );
  }

  Container buildRealtimeSparklineCard() {
    return Container(
      color: Colors.white,
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: &amp;lt;Widget&amp;gt;[
            Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: &amp;lt;Widget&amp;gt;[
                Text(
                  "Realtime views",
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                ),
                SizedBox(
                  height: 6,
                ),
                Text(
                  "48 Hours • Estimated views",
                  style: TextStyle(color: Colors.black54, fontSize: 12),
                ),
                SizedBox(
                  height: 6,
                ),
                Text("6,500"),
              ],
            ),
            SparklineChart(),
          ],
        ),
      ),
    );
  }

  AppBar buildAppBar() {
    return AppBar(
      title: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: &amp;lt;Widget&amp;gt;[
          Text("Analytics", style: TextStyle(color: Colors.black)),
          Row(
            children: &amp;lt;Widget&amp;gt;[
              Container(
                padding: EdgeInsets.all(6),
                decoration:
                    BoxDecoration(color: Colors.grey, shape: BoxShape.circle),
                child: Text(
                  "24",
                  style: TextStyle(color: Colors.white, fontSize: 12),
                ),
              ),
              SizedBox(
                width: 14,
              ),
              CircleAvatar(
                maxRadius: 13,
                backgroundImage: NetworkImage(
                  "https://yt3.ggpht.com/a-/AOh14GhB7xAslx6hmlYqG9-NDLyRj8ycHFUn7ypBMXv52Q=s288-c-k-c0xffffffff-no-rj-mo",
                ),
              )
            ],
          )
        ],
      ),
      centerTitle: false,
      brightness: Brightness.light,
      leading: IconButton(
        icon: Icon(Icons.menu),
        onPressed: () {},
      ),
      bottom: PreferredSize(
        preferredSize: Size.fromHeight(40.0),
        child: Column(
          children: &amp;lt;Widget&amp;gt;[
            Divider(
              height: 2,
            ),
            Container(
              margin: EdgeInsets.only(left: 20),
              child: TabBar(
                isScrollable: true,
                indicatorColor: Colors.black,
                tabs: [
                  Tab(
                    child:
                        Text("OVERVIEW", style: TextStyle(color: Colors.black)),
                  ),
                  Tab(
                    child:
                        Text("REVENUE", style: TextStyle(color: Colors.black)),
                  ),
                  Tab(
                    child: Text("DISCOVERY",
                        style: TextStyle(color: Colors.black)),
                  ),
                  Tab(
                    child:
                        Text("AUDIENCE", style: TextStyle(color: Colors.black)),
                  ),
                  Tab(
                    child: Text("INTERACTIVE CONTENT",
                        style: TextStyle(color: Colors.black)),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      iconTheme: IconThemeData(color: Colors.black),
      backgroundColor: Colors.white,
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's the final results of our work:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2_FdxqeF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-20-at-18.43.00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2_FdxqeF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-20-at-18.43.00.png" alt="UI Design: Sparkline Bar Chart in Flutter (YouTube Studio)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In this article we looked at how to create a Sparkline chart with the Flutter &lt;code&gt;CustomPainter&lt;/code&gt;. I hadn't used the &lt;code&gt;CustomPainter&lt;/code&gt; or Flutter &lt;code&gt;Canvas&lt;/code&gt; before this, so it was a fun experiment!&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts on Twitter or in the comments.&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/flutter_sparkline"&gt;https://github.com/PaulHalliday/flutter_sparkline&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>My Favourite "Top 5" Programming Fonts</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Mon, 20 Apr 2020 12:23:20 +0000</pubDate>
      <link>https://dev.to/paulhalliday/my-favourite-top-5-programming-fonts-30e0</link>
      <guid>https://dev.to/paulhalliday/my-favourite-top-5-programming-fonts-30e0</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2Fimage-1-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2Fimage-1-1.png" alt="My Favourite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I often find myself playing around with my editor on the weekend, whenever I get a new machine, or, pretty much anytime I find a new cool font. As it turns out, there's lots of interesting fonts out there that improve day-to-day work experience.&lt;/p&gt;

&lt;p&gt;Let's take a look... in no particular order!&lt;/p&gt;

&lt;h2&gt;
  
  
  Operator Mono
&lt;/h2&gt;

&lt;p&gt;This font is one of the more popular &lt;em&gt;premium&lt;/em&gt; fonts (~$200) and is one of my personal favourites. It has custom ligatures and a distinctly cursive style. You can see how it looks in the video below, &lt;em&gt;or&lt;/em&gt; by &lt;a href="https://www.typography.com/fonts/operator/styles" rel="noopener noreferrer"&gt;checking it out on the website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You'll likely find that this font is polarising. Either people love the cursive style or hate it.&lt;/p&gt;

&lt;p&gt;It's even been referred to as the &lt;a href="https://www.reddit.com/r/webdev/comments/8dyn99/operator_mono_is_the_comic_sans_of_2018/" rel="noopener noreferrer"&gt;Comic Sans of 2018&lt;/a&gt; by one redditor.  &lt;/p&gt;

&lt;p&gt;I like it, and that's enough for me! :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Fira Code
&lt;/h2&gt;

&lt;p&gt;Fira Code is a free, &lt;a href="https://github.com/tonsky/FiraCode" rel="noopener noreferrer"&gt;open source font&lt;/a&gt; that supports ligatures. It's derived from &lt;a href="http://mozilla.github.io/Fira/" rel="noopener noreferrer"&gt;Fira Mono&lt;/a&gt; (hence the name) but has additional spicy features.&lt;/p&gt;

&lt;p&gt;You can see it in action here:&lt;/p&gt;

&lt;p&gt;If you're looking for a free font that's pleasant to read for a long period of time, I'd recommend Fira Code.&lt;/p&gt;

&lt;p&gt;If you'd like to sponsor the development of Fira Code, you can do so on &lt;a href="https://github.com/sponsors/tonsky" rel="noopener noreferrer"&gt;GitHub Sponsors!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cascadia Code
&lt;/h2&gt;

&lt;p&gt;One of the more recent additions to the font world &lt;em&gt;and&lt;/em&gt; my favourites list is &lt;a href="https://github.com/microsoft/cascadia-code" rel="noopener noreferrer"&gt;Cascadia Code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2Fimage-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2Fimage-1.png" alt="My Favourite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was created by Microsoft and is used in the new Windows Terminal.&lt;/p&gt;

&lt;p&gt;It's open source and has the &lt;a href="https://github.com/microsoft/cascadia-code/blob/master/LICENSE" rel="noopener noreferrer"&gt;SIL OPEN FONT LICENSE&lt;/a&gt;, allowing for free use as long as you don't sell it seperately.&lt;/p&gt;

&lt;p&gt;I usually use this when I've been looking at Operator Mono for too long... 🤔&lt;/p&gt;

&lt;h2&gt;
  
  
  MonoLisa
&lt;/h2&gt;

&lt;p&gt;If you're looking for a more affordable premium font, I'd recommend MonoLisa. It supports ligatures, italics, and isn't fatiguing to read for a long period of time.&lt;/p&gt;

&lt;p&gt;It was created by &lt;a href="https://component-driven.io/" rel="noopener noreferrer"&gt;Component Driven&lt;/a&gt; in Vienna and comes in at &lt;a href="https://www.monolisa.dev/buy" rel="noopener noreferrer"&gt;$59 for the Personal Edition / $299 for the Professional Edition&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FScreenshot-2020-04-20-at-12.52.35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FScreenshot-2020-04-20-at-12.52.35.png" alt="My Favourite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're only going to be using this in your editor, then I'd recommend only investigating the Personal Edition.&lt;/p&gt;

&lt;p&gt;You can find the optimal settings for your preferred editor of choice in the &lt;a href="https://www.monolisa.dev/faq" rel="noopener noreferrer"&gt;MonoLisa FAQ&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  JetBrains Mono
&lt;/h2&gt;

&lt;p&gt;The final font I'd like to recommend here is &lt;a href="https://www.jetbrains.com/lp/mono/" rel="noopener noreferrer"&gt;JetBrains Mono&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It supports ligatures and italics and was released earlier this year and is a relatively new addition to my list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FScreenshot-2020-04-20-at-12.56.25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.school%2Fcontent%2Fimages%2F2020%2F04%2FScreenshot-2020-04-20-at-12.56.25.png" alt="My Favourite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's open source and &lt;em&gt;free for both personal and commercial usage,&lt;/em&gt; following the &lt;a href="https://www.apache.org/licenses/LICENSE-2.0" rel="noopener noreferrer"&gt;APACHE 2.0&lt;/a&gt; license.&lt;/p&gt;

&lt;p&gt;You can get it on the JetBrains Mono site: &lt;a href="https://www.jetbrains.com/lp/mono/#how-to-install" rel="noopener noreferrer"&gt;https://www.jetbrains.com/lp/mono/#how-to-install&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;We're fortunate to be living in a time where there's &lt;em&gt;a wealth of choices when it comes to editor customisation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you want a fancy premium font you can splash out (&lt;em&gt;I mean, you are looking at this for a large portion of your work week&lt;/em&gt;) or you can simply get a free one that's just as good (or subjectively better!).&lt;/p&gt;

</description>
      <category>visualstudiocode</category>
    </item>
    <item>
      <title>Building a Top Ten List: Using ReorderableListView to Reorder List Items</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Fri, 17 Apr 2020 07:27:05 +0000</pubDate>
      <link>https://dev.to/paulhalliday/building-a-top-ten-list-using-reorderablelistview-to-reorder-list-items-2jhf</link>
      <guid>https://dev.to/paulhalliday/building-a-top-ten-list-using-reorderablelistview-to-reorder-list-items-2jhf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iCLQg56T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/top_ten.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iCLQg56T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/top_ten.png" alt="Building a Top Ten List: Using ReorderableListView to Reorder List Items"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we'll be looking at how to use &lt;code&gt;ReorderableListView&lt;/code&gt; to reorder list items inside of our Flutter applications. The &lt;code&gt;ReorderableListView&lt;/code&gt; is part of the &lt;code&gt;Material&lt;/code&gt; library and should be used for smaller list(s) without a substantial amount of items.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ReorderableListView&lt;/code&gt; doesn't support the use of a &lt;code&gt;builder&lt;/code&gt; method as it uses a &lt;code&gt;SingleChildScrollView&lt;/code&gt; under the hood. As a result, you could see performance issue(s) with larger lists - keep this in mind!&lt;/p&gt;

&lt;h3&gt;
  
  
  Video
&lt;/h3&gt;

&lt;p&gt;Prefer to watch a video? Here's one I recorded for this article:&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;The project we'll be creating is a simple "Top 10" list where the user can rank a list of items. Here's an example of it in action:&lt;/p&gt;

&lt;p&gt;Reorderables!&lt;/p&gt;

&lt;p&gt;We'll start by creating a new Flutter project in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# New Flutter project
$ flutter create fl_reorderable

# Open in editor
$ cd fl_reorderable &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating our Top Ten List
&lt;/h3&gt;

&lt;p&gt;Firstly, we'll start off by creating a new &lt;code&gt;Stateful Widget&lt;/code&gt; named &lt;code&gt;TopTenList&lt;/code&gt; at &lt;code&gt;lib/top_ten_list.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

class TopTenList extends StatefulWidget {
  @override
  _TopTenListState createState() =&amp;gt; _TopTenListState();
}

class _TopTenListState extends State&amp;lt;TopTenList&amp;gt; {
  List&amp;lt;String&amp;gt; topTenGames = [
    "World of Warcraft",
    "Final Fantasy VII",
    "Animal Crossing",
    "Diablo II",
    "Overwatch",
    "Valorant",
    "Minecraft",
    "Dota 2",
    "Half Life 3",
    "Grand Theft Auto: Vice City"
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Top Ten"),
      ),
      body: ReorderableListView(
        onReorder: (int oldIndex, int newIndex) {},
        children: getListItems(),
      ),
    );
  }

  List&amp;lt;ListTile&amp;gt; getListItems() =&amp;gt; topTenGames
      .asMap()
      .map((i, item) =&amp;gt; MapEntry(i, buildTenableListTile(item, i)))
      .values
      .toList();

  ListTile buildTenableListTile(String item, int index) {
    return ListTile(
      key: ValueKey(item),
      title: Text(item),
      leading: Text("#${index + 1}"),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We've established a &lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt; which contains our &lt;code&gt;topTenGames&lt;/code&gt; (i.e. the data we'll be using to generate our &lt;code&gt;_reorderableListItems&lt;/code&gt;). This'll likely be a more complex model on your end, but a &lt;code&gt;String&lt;/code&gt; should suffice for our demo.&lt;/p&gt;

&lt;p&gt;Go ahead and add the &lt;code&gt;TopTenList&lt;/code&gt; Widget to your &lt;code&gt;main.dart&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:fl_reorderable/top_ten_list.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ReorderableListView',
      theme: ThemeData(
        brightness: Brightness.dark,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      debugShowCheckedModeBanner: false,
      home: TopTenList(),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Reordering Items
&lt;/h3&gt;

&lt;p&gt;You'll be able to initiate the reorder functionality on our &lt;code&gt;TopTenList&lt;/code&gt; by holding down (long press tapping) an item within the list. If you attempt to move this either above or below, it won't work and will snap back to its original position.&lt;/p&gt;

&lt;p&gt;This is because we haven't implemented the &lt;code&gt;onReorder&lt;/code&gt; function. Let's do exactly that!&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;onReorder&lt;/code&gt; function below and update the &lt;code&gt;ReorderableListView&lt;/code&gt; to use this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text("Top Ten"),
    ),
    body: ReorderableListView(
      onReorder: onReorder,
      children: getListItems(),
    ),
  );
}

void onReorder(int oldIndex, int newIndex) {
  if (newIndex &amp;gt; oldIndex) {
    newIndex -= 1;
  }

  setState(() {
    String game = topTenGames[oldIndex];

    topTenGames.removeAt(oldIndex);
    topTenGames.insert(newIndex, game);
  });
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This now allows us to directly compare the state of the list depending on whether the user ordered it above &lt;em&gt;or&lt;/em&gt; below the current item.  &lt;/p&gt;

&lt;p&gt;Why does this matter?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that if [oldIndex] is before [newIndex], removing the item at [oldIndex] from the list will reduce the list's length by one.   &lt;/p&gt;

&lt;p&gt;Implementations used by [ReorderableListView] will need to account for this when inserting before [newIndex].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://chromium.googlesource.com/external/github.com/flutter/flutter/+/v0.8.7/packages/flutter/lib/src/material/reorderable_list.dart"&gt;https://chromium.googlesource.com/external/github.com/flutter/flutter/+/v0.8.7/packages/flutter/lib/src/material/reorderable_list.dart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're then able to remove the item at the &lt;code&gt;oldIndex&lt;/code&gt; and replace this with the &lt;code&gt;newIndex&lt;/code&gt;, and as we're using &lt;code&gt;setState&lt;/code&gt; we're able to trigger a rebuild of the UI. This'll call our &lt;code&gt;getListItems()&lt;/code&gt; once more, rendering our new list and surrounding rankings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;This completes our Top Ten List! We've now got a list that we're able to reorder &lt;em&gt;and&lt;/em&gt; display the current ranking. Some possible improvements to this could be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give the user an option to create multiple lists with items &lt;em&gt;per&lt;/em&gt; list&lt;/li&gt;
&lt;li&gt;Allow users to add, remove and edit items in the list&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/paulhalliday/how-to-save-data-to-localstorage-and-sharedpreferences-in-flutter-6el-temp-slug-79980"&gt;Persist list items&lt;/a&gt; to &lt;code&gt;localstorage&lt;/code&gt; or &lt;code&gt;shared_preferences&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What would you like to see next? I'd love to hear your feedback at &lt;a href="https://twitter.com/paulhalliday_io"&gt;https://twitter.com/paulhalliday_io&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/flutter_top_ten_list"&gt;https://github.com/PaulHalliday/flutter_top_ten_list&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>Epic Toasts with FlutterToast</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Thu, 16 Apr 2020 12:56:22 +0000</pubDate>
      <link>https://dev.to/paulhalliday/epic-toasts-with-fluttertoast-3fmj</link>
      <guid>https://dev.to/paulhalliday/epic-toasts-with-fluttertoast-3fmj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iOxLHfqd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Toast.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iOxLHfqd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Toast.png" alt="Epic Toasts with FlutterToast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fluttertoast&lt;/code&gt; is a library that allows you to create Toast popups easily across iOS, Android and Web.&lt;/p&gt;

&lt;h3&gt;
  
  
  Video
&lt;/h3&gt;

&lt;p&gt;Would you prefer to watch a video instead of reading this article? Here you go:&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;To get started, we'll go ahead and create a new Flutter project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# New Flutter project
$ flutter create fl_toast

# Open in editor
$ cd fl_toast &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;Next up, we'll need to add the &lt;code&gt;fluttertoast&lt;/code&gt; dependency to our &lt;code&gt;pubspec.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  flutter:
    sdk: flutter

  fluttertoast: ^4.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Showing a Toast
&lt;/h3&gt;

&lt;p&gt;Here's a typical example of the &lt;code&gt;Fluttertoast&lt;/code&gt; call that we'll be using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fluttertoast.showToast(
  msg: _messageTextEditingController.text,
  timeInSecForIosWeb: 10,
  toastLength: Toast.LENGTH_LONG,
  webShowClose: true,
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll be using a text box and button which displays a toast with the message a user has typed. Our newly created &lt;code&gt;HomePage&lt;/code&gt; that does exactly that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() =&amp;gt; _HomePageState();
}

class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
  TextEditingController _messageTextEditingController;

  GlobalKey&amp;lt;FormState&amp;gt; _formKey;

  @override
  void initState() {
    super.initState();

    _formKey = GlobalKey&amp;lt;FormState&amp;gt;();
    _messageTextEditingController = TextEditingController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter Toast"),
      ),
      body: Padding(
        padding: const EdgeInsets.all(12.0),
        child: Form(
          key: _formKey,
          child: Column(children: [
            TextFormField(
              controller: _messageTextEditingController,
            ),
            FlatButton(
              onPressed: () =&amp;gt; Fluttertoast.showToast(
                msg: _messageTextEditingController.text,
                timeInSecForIosWeb: 10,
                toastLength: Toast.LENGTH_LONG,
                webShowClose: true,
              ),
              child: Text("Show Toast"),
            )
          ]),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _messageTextEditingController.dispose();
    super.dispose();
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll also need to update the &lt;code&gt;home&lt;/code&gt; within our &lt;code&gt;main.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:fl_toast/presentation/pages/home_page.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Toastie',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        brightness: Brightness.dark,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can configure our toasts even further with various other options as seen here: &lt;a href="https://pub.dev/documentation/fluttertoast/latest/"&gt;https://pub.dev/documentation/fluttertoast/latest/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I3S2eatV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Toastie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I3S2eatV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Toastie.png" alt="Epic Toasts with FlutterToast"&gt;&lt;/a&gt;Example on Flutter Web&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Next time you're looking for a library to display toasts inside of your Flutter apps, give &lt;code&gt;fluttertoast&lt;/code&gt; a try!&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/fl_toast"&gt;https://github.com/PaulHalliday/fl_toast&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>Flutter &amp; MobX: Dark/Light Mode Switcher</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Thu, 16 Apr 2020 07:50:37 +0000</pubDate>
      <link>https://dev.to/paulhalliday/flutter-mobx-dark-light-mode-switcher-2k0</link>
      <guid>https://dev.to/paulhalliday/flutter-mobx-dark-light-mode-switcher-2k0</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4MNWUTJ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/mobxDark.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4MNWUTJ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/mobxDark.png" alt="Flutter &amp;amp; MobX: Dark/Light Mode Switcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we're going to create a small application that uses  &lt;code&gt;Flutter MobX&lt;/code&gt; and &lt;code&gt;Provider&lt;/code&gt; to toggle between two &lt;code&gt;ThemeData&lt;/code&gt; states. We'll look at the following key concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to change between dark and light mode.&lt;/li&gt;
&lt;li&gt;How to create a &lt;code&gt;ThemeStore&lt;/code&gt; which will be responsible for firing action(s) and managing observable values.&lt;/li&gt;
&lt;li&gt;How to inject the &lt;code&gt;ThemeStore&lt;/code&gt; into our Widget tree&lt;/li&gt;
&lt;li&gt;How to show a &lt;code&gt;SnackBar&lt;/code&gt; based on a &lt;code&gt;reaction&lt;/code&gt; between when the application is in &lt;code&gt;dark&lt;/code&gt; or &lt;code&gt;light&lt;/code&gt; mode.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;Let's go ahead and create a new Flutter project and install our required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# New Flutter project
$ flutter create mobx_theme

# Open in VS Code
$ cd mobx_theme &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we've got the project open, we can update &lt;code&gt;pubspec.yaml&lt;/code&gt; with the following &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;dev_dependencies&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  flutter:
    sdk: flutter

  provider: ^4.0.5
  mobx: ^1.1.1
  flutter_mobx: ^1.1.0
  shared_preferences: ^0.5.6+3

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner:
  mobx_codegen: ^1.0.3

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Switching Between Dark and Light Mode
&lt;/h3&gt;

&lt;p&gt;Before implementing any state management solutions, &lt;em&gt;how do we switch between dark and light mode?&lt;/em&gt; Flutter makes it easy with changes to &lt;code&gt;brightness&lt;/code&gt; within a selected &lt;code&gt;ThemeData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example of a &lt;code&gt;lightTheme&lt;/code&gt; and &lt;code&gt;darkTheme&lt;/code&gt; respectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ThemeData get lightTheme =&amp;gt; ThemeData(
        primarySwatch: Colors.teal,
        accentColor: Colors.deepPurpleAccent,
        brightness: Brightness.light,
        scaffoldBackgroundColor: Color(0xFFecf0f1),
        visualDensity: VisualDensity.adaptivePlatformDensity,
      );

  ThemeData get darkTheme =&amp;gt; ThemeData(
        primarySwatch: Colors.teal,
        accentColor: Colors.tealAccent,
        brightness: Brightness.dark,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      );
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This only represents a small amount of the overall configurable theming options, and it's encouraged that you come up with your own theme(s) here. :)&lt;/p&gt;

&lt;h3&gt;
  
  
  Theme Repository
&lt;/h3&gt;

&lt;p&gt;We'll start off by creating an &lt;code&gt;IThemeRepository&lt;/code&gt; interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/domain/theme/interfaces/i_theme_repository.dart
import 'package:flutter/material.dart';

abstract class IThemeRepository {
  Future&amp;lt;String&amp;gt; getThemeKey();
  Future&amp;lt;void&amp;gt; setThemeKey(Brightness brightness);
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll then create a &lt;code&gt;ThemeKey&lt;/code&gt; class to hold our constant key(s) related to Theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ThemeKey {
  static const String THEME = "theme";
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the future, you may want to abstract this functionality out into a Preferences repository as seen in my other article: &lt;a href="https://dev.to/paulhalliday/how-to-save-data-to-localstorage-and-sharedpreferences-in-flutter-6el-temp-slug-79980"&gt;https://developer.school/how-to-save-data-to-localstorage-shared-prefs-in-flutter-flutter-web/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our implementation of this can be seen here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/infrastructure/theme/datasources/theme_repository.dart
import 'dart:ui';

import 'package:mobx_theme/domain/theme/constants/theme_keys.dart';
import 'package:mobx_theme/domain/theme/interfaces/i_theme_repository.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ThemeRepository implements IThemeRepository {
  @override
  Future&amp;lt;void&amp;gt; setThemeKey(Brightness brightness) async {
    (await SharedPreferences.getInstance()).setString(
      ThemeKey.THEME,
      brightness == Brightness.light ? "light" : "dark",
    );
  }

  @override
  Future&amp;lt;String&amp;gt; getThemeKey() async {
    return (await SharedPreferences.getInstance()).getString(ThemeKey.THEME);
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Essentially, we're either setting a &lt;code&gt;SharedPreferences&lt;/code&gt; value of either &lt;code&gt;light&lt;/code&gt; or &lt;code&gt;dark&lt;/code&gt; depending on the brightness passed in. We're also able to retrieve this value to set the appropriate theme in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Theme Service
&lt;/h3&gt;

&lt;p&gt;We've now got the ability to save and retrieve theme keys. We can go ahead and create a &lt;code&gt;ThemeService&lt;/code&gt; which uses this to return an appropriate theme for our user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/application/theme/services/theme_service.dart
import 'package:flutter/material.dart';
import 'package:mobx_theme/domain/theme/interfaces/i_theme_repository.dart';

class ThemeService {
  ThemeService(IThemeRepository themeRepository)
      : _themeRepository = themeRepository;

  IThemeRepository _themeRepository;

  ThemeData get lightTheme =&amp;gt; ThemeData(
        primarySwatch: Colors.teal,
        accentColor: Colors.deepPurpleAccent,
        brightness: Brightness.light,
        scaffoldBackgroundColor: Color(0xFFecf0f1),
        visualDensity: VisualDensity.adaptivePlatformDensity,
      );

  ThemeData get darkTheme =&amp;gt; ThemeData(
        primarySwatch: Colors.teal,
        accentColor: Colors.tealAccent,
        brightness: Brightness.dark,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      );

  Future&amp;lt;ThemeData&amp;gt; getTheme() async {
    final String themeKey = await _themeRepository.getThemeKey();

    if (themeKey == null) {
      await _themeRepository.setThemeKey(lightTheme.brightness);

      return lightTheme;
    } else {
      return themeKey == "light" ? lightTheme : darkTheme;
    }
  }

  Future&amp;lt;ThemeData&amp;gt; toggleTheme(ThemeData theme) async {
    if (theme == lightTheme) {
      theme = darkTheme;
    } else {
      theme = lightTheme;
    }

    await _themeRepository.setThemeKey(theme.brightness);
    return theme;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next up, we'll use this service inside of our &lt;code&gt;ThemeStore&lt;/code&gt; to handle reactivity on these value(s):&lt;/p&gt;

&lt;h3&gt;
  
  
  Theme Store
&lt;/h3&gt;

&lt;p&gt;The Store will be responsible for exposing our current &lt;code&gt;theme&lt;/code&gt; to our &lt;code&gt;MaterialApp&lt;/code&gt;. We've also created a &lt;code&gt;computed&lt;/code&gt; getter for &lt;code&gt;isDark&lt;/code&gt; which we can use at any point to determine whether we're currently in &lt;code&gt;dark&lt;/code&gt; mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/application/theme/store/theme_store.dart
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
import 'package:mobx_theme/application/theme/services/theme_service.dart';

part 'theme_store.g.dart';

class ThemeStore extends _ThemeStore with _$ThemeStore {
  ThemeStore(ThemeService themeService) : super(themeService);
}

abstract class _ThemeStore with Store {
  _ThemeStore(this._themeService);

  final ThemeService _themeService;

  @computed
  bool get isDark =&amp;gt; theme.brightness == Brightness.dark;

  @observable
  ThemeData theme;

  @action
  Future&amp;lt;void&amp;gt; getTheme() async {
    theme = _themeService.lightTheme;
    theme = await _themeService.getTheme();
  }

  @action
  Future&amp;lt;void&amp;gt; toggleTheme() async {
    theme = await _themeService.toggleTheme(theme);
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As MobX requires some code generation, we'll need to run the &lt;code&gt;build_runner&lt;/code&gt; with &lt;code&gt;mobx_codegen&lt;/code&gt;. Run the following in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ flutter pub run build_runner build 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Providing our Store
&lt;/h3&gt;

&lt;p&gt;We're now able to switch brightness, &lt;em&gt;however,&lt;/em&gt; we need to provide our &lt;code&gt;MaterialApp&lt;/code&gt; with the current value of our &lt;code&gt;theme&lt;/code&gt;. We can do that by using &lt;code&gt;Provider&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;///lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx_theme/application/theme/services/theme_service.dart';
import 'package:mobx_theme/application/theme/store/theme_store.dart';
import 'package:mobx_theme/infrastructure/theme/datasources/theme_repository.dart';
import 'package:mobx_theme/presentation/pages/splash_screen.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        Provider&amp;lt;ThemeStore&amp;gt;(
            create: (_) =&amp;gt;
                ThemeStore(ThemeService(ThemeRepository()))..getTheme())
      ],
      child: Consumer&amp;lt;ThemeStore&amp;gt;(
        builder: (_, ThemeStore value, __) =&amp;gt; Observer(
          builder: (_) =&amp;gt; MaterialApp(
            debugShowCheckedModeBanner: false,
            title: 'MobX Theme Switcher',
            theme: value.theme,
            home: SplashPage(),
          ),
        ),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we're providing the &lt;code&gt;ThemeStore&lt;/code&gt; into our Widget tree and immediately using &lt;code&gt;Consumer&lt;/code&gt; to get the current &lt;code&gt;theme.value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As we've created &lt;code&gt;theme&lt;/code&gt; as an &lt;code&gt;@observable&lt;/code&gt; using MobX, any changes to &lt;code&gt;theme&lt;/code&gt; will be reactive as we've wrapped our &lt;code&gt;MaterialApp&lt;/code&gt; in an &lt;code&gt;Observer&lt;/code&gt; widget.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our SplashPage
&lt;/h3&gt;

&lt;p&gt;As we're strictly dealing with the ability to change between dark and light mode in this article, I've created one page - &lt;code&gt;SplashPage&lt;/code&gt; which has a simple title/subtitle. We're able to switch between &lt;code&gt;dark&lt;/code&gt; and &lt;code&gt;light&lt;/code&gt; mode by clicking the Floating Action Button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--piRAOHe9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/DarkLight.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--piRAOHe9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/DarkLight.png" alt="Flutter &amp;amp; MobX: Dark/Light Mode Switcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how we achieve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/presentation/pages/splash_page.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobx_theme/application/theme/store/theme_store.dart';
import 'package:provider/provider.dart';

class SplashPage extends StatefulWidget {
  @override
  _SplashPageState createState() =&amp;gt; _SplashPageState();
}

class _SplashPageState extends State&amp;lt;SplashPage&amp;gt; {
  ThemeStore themeStore;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    themeStore ??= Provider.of&amp;lt;ThemeStore&amp;gt;(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: themeStore.toggleTheme,
        child: themeStore.isDark
            ? Icon(Icons.brightness_high)
            : Icon(Icons.brightness_2),
      ),
      body: buildSplash(context),
    );
  }

  Widget buildSplash(BuildContext context) {
    return AnnotatedRegion&amp;lt;SystemUiOverlayStyle&amp;gt;(
      value: themeStore.isDark
          ? SystemUiOverlayStyle.light
          : SystemUiOverlayStyle.dark,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            SizedBox(
              height: 20,
            ),
            Text(
              "Foodie",
              style: TextStyle(fontSize: 26),
            ),
            SizedBox(
              height: 4,
            ),
            Text(
              "The best way to track your nutrition.",
              style: TextStyle(fontSize: 16),
            ),
          ],
        ),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There isn't too much out of the ordinary here. We're getting access to our &lt;code&gt;ThemeStore&lt;/code&gt; in &lt;code&gt;didChangeDependencies&lt;/code&gt; and using the &lt;code&gt;themeStore.toggleTheme&lt;/code&gt; action when our FAB is pressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using MobX Reactions
&lt;/h3&gt;

&lt;p&gt;I can't think of many use cases for this, but what if you wanted to show a Snackbar (or another &lt;code&gt;reaction&lt;/code&gt;) whenever the theme has been changed? Here's an example of what this could look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kn-xnpnl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/DarkLight_Reaction.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kn-xnpnl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/DarkLight_Reaction.png" alt="Flutter &amp;amp; MobX: Dark/Light Mode Switcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is made easy with MobX. We have to register our &lt;code&gt;ReactionDisposer&lt;/code&gt; with the &lt;code&gt;Observable&lt;/code&gt; that we want to react against:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:mobx_theme/application/theme/store/theme_store.dart';
import 'package:provider/provider.dart';

class SplashPage extends StatefulWidget {
  @override
  _SplashPageState createState() =&amp;gt; _SplashPageState();
}

class _SplashPageState extends State&amp;lt;SplashPage&amp;gt; {
  ThemeStore themeStore;

  GlobalKey&amp;lt;ScaffoldState&amp;gt; _scaffoldKey;
  List&amp;lt;ReactionDisposer&amp;gt; _disposers;

  @override
  void initState() {
    super.initState();
    _scaffoldKey = GlobalKey&amp;lt;ScaffoldState&amp;gt;();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    themeStore ??= Provider.of&amp;lt;ThemeStore&amp;gt;(context);
    _disposers ??= [
      reaction((fn) =&amp;gt; themeStore.isDark, (isDark) {
        _scaffoldKey.currentState?.removeCurrentSnackBar();

        if (isDark) {
          _scaffoldKey.currentState.showSnackBar(SnackBar(
            content: Text("Hello, Dark!"),
          ));
        } else {
          _scaffoldKey.currentState.showSnackBar(SnackBar(
            content: Text("Hello, Light!"),
          ));
        }
      })
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      floatingActionButton: FloatingActionButton(
        onPressed: themeStore.toggleTheme,
        child: themeStore.isDark
            ? Icon(Icons.brightness_high)
            : Icon(Icons.brightness_2),
      ),
      body: buildSplash(context),
    );
  }

  Widget buildSplash(BuildContext context) {
    return AnnotatedRegion&amp;lt;SystemUiOverlayStyle&amp;gt;(
      value: themeStore.isDark
          ? SystemUiOverlayStyle.light
          : SystemUiOverlayStyle.dark,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            SizedBox(
              height: 20,
            ),
            Text(
              "Foodie",
              style: TextStyle(fontSize: 26),
            ),
            SizedBox(
              height: 4,
            ),
            Text(
              "The best way to track your nutrition.",
              style: TextStyle(fontSize: 16),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _disposers.forEach((disposer) =&amp;gt; disposer());
    super.dispose();
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Whenever we register a &lt;code&gt;reaction&lt;/code&gt; it returns a &lt;code&gt;ReactionDisposer&amp;lt;T&amp;gt;&lt;/code&gt; which can be called to &lt;code&gt;dispose&lt;/code&gt; of the &lt;code&gt;reaction&lt;/code&gt;. We're only registering one &lt;code&gt;reaction&lt;/code&gt; here, but for simplicity sake we've used a &lt;code&gt;List&lt;/code&gt; to make it more flexible.&lt;/p&gt;

&lt;p&gt;Our application is now able to react to &lt;code&gt;isDark&lt;/code&gt; changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In this article we looked at one potential way to implement dynamic theming with MobX. I'd love to hear your thoughts as to how this could be improved and/or any future libraries you'd like me to investigate.&lt;/p&gt;

&lt;p&gt;Code for this article: &lt;a href="https://github.com/PaulHalliday/mobx_theme"&gt;https://github.com/PaulHalliday/mobx_theme&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>How to Save Data to LocalStorage and SharedPreferences in Flutter</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Mon, 13 Apr 2020 16:44:13 +0000</pubDate>
      <link>https://dev.to/paulhalliday/how-to-save-data-to-localstorage-and-sharedpreferences-in-flutter-mnn</link>
      <guid>https://dev.to/paulhalliday/how-to-save-data-to-localstorage-and-sharedpreferences-in-flutter-mnn</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8pwSHV1H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Flutter--5-.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8pwSHV1H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Flutter--5-.jpg" alt="How to Save Data to LocalStorage and SharedPreferences in Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we're going to investigate how we can create a simple integration with the &lt;code&gt;localstorage&lt;/code&gt; and &lt;code&gt;shared_preferences&lt;/code&gt; plugin inside of our Flutter applications. We'll be creating a &lt;code&gt;StorageRepository&lt;/code&gt; and &lt;code&gt;StorageService&lt;/code&gt; as their own package so we can include them in our other project(s) easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;Let's create a new Flutter package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create a new package with the --template flag
$ flutter create --template=package my_storage

# Open in editor
$ cd my_storage &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then head over to &lt;code&gt;pubspec.yaml&lt;/code&gt; and add the &lt;code&gt;localstorage&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  flutter:
    sdk: flutter

  localstorage: ^3.0.1+4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Using &lt;code&gt;localstorage&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we've got created package and installed &lt;code&gt;localstorage&lt;/code&gt;, we can make an abstraction over &lt;code&gt;localstorage&lt;/code&gt; in the event that we want to swap this out with another persistence library in the future.&lt;/p&gt;

&lt;p&gt;For now, we'll be concerned with two methods: &lt;code&gt;getAll&lt;/code&gt; and &lt;code&gt;save&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// i_local_storage_repository.dart
abstract class ILocalStorageRepository {
  Future getAll(String key);
  Future&amp;lt;void&amp;gt; save(String key, dynamic item);
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can now create an implementation of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:localstorage/localstorage.dart';
import 'package:my_storage/i_local_storage_repository.dart';

class LocalStorageRepository implements ILocalStorageRepository {
  final LocalStorage _storage;

  LocalStorageRepository(String storageKey)
      : _storage = LocalStorage(storageKey);

  @override
  Future getAll(String key) async {
    await _storage.ready;

    return _storage.getItem(key);
  }

  @override
  Future&amp;lt;void&amp;gt; save(String key, dynamic value) async {
    await _storage.ready;

    return _storage.setItem(key, value);
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great. We've now have a &lt;code&gt;LocalStorageRepository&lt;/code&gt; which we can use to save and get data. We still don't want to interface with this repository directly, so we can create a &lt;code&gt;LocalStorageService&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// local_storage_service.dart
import 'package:flutter/foundation.dart';
import 'package:my_storage/i_local_storage_repository.dart';

class LocalStorageService {
  LocalStorageService(
      {@required ILocalStorageRepository localStorageRepository})
      : _localStorageRepository = localStorageRepository;

  ILocalStorageRepository _localStorageRepository;

  Future&amp;lt;dynamic&amp;gt; getAll(String key) async {
    return await _localStorageRepository.getAll(key);
  }

  Future&amp;lt;void&amp;gt; save(String key, dynamic item) async {
    await _localStorageRepository.save(key, item);
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This takes in an injected &lt;code&gt;ILocalStorageRepository&lt;/code&gt; and allows us to call the contract. Now, providing we use the &lt;code&gt;LocalStorageService&lt;/code&gt; directly, we could swap out &lt;code&gt;localstorage&lt;/code&gt; for something different (like &lt;code&gt;shared_preferences&lt;/code&gt; but our usage would remain the same).&lt;/p&gt;

&lt;h3&gt;
  
  
  Persisting Counter State
&lt;/h3&gt;

&lt;p&gt;We can see this in action by persisting counter state if we create a new Flutter project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# New Flutter project
$ flutter create storage_counter

# Open in editor
$ cd my_counter &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then add our &lt;code&gt;my_storage&lt;/code&gt; package in the &lt;code&gt;pubspec.yaml&lt;/code&gt; by pointing it to the directory, GitHub repo, or other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  flutter: 
    sdk: flutter

  my_storage:  
    path: '../my_storage' 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In our typical application we'll have a service layer which we can use to call out to our &lt;code&gt;LocalStorageService&lt;/code&gt; and our preferred method of persistence. Here's an example of our small &lt;code&gt;CounterService&lt;/code&gt; which is able to get the current stored counter state, increment, and decrement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// lib/services/counter_service.dart
import 'package:flutter/foundation.dart';
import 'package:my_storage/i_local_storage_repository.dart';
import 'package:my_storage/local_storage_service.dart';

class CounterService {
  final LocalStorageService _localStorageService;
  final String _countKey = "count";

  CounterService({
    @required ILocalStorageRepository localStorageRepository,
  }) : _localStorageService =
            LocalStorageService(localStorageRepository: localStorageRepository);

  Future&amp;lt;int&amp;gt; getCount() async {
    return await _localStorageService.getAll(_countKey) ?? 0;
  }

  Future&amp;lt;int&amp;gt; incrementAndSave(int count) async {
    count += 1;
    await _localStorageService.save(_countKey, count);

    return count;
  }

  Future&amp;lt;int&amp;gt; decrementAndSave(int count) async {
    count -= 1;
    await _localStorageService.save(_countKey, count);

    return count;
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can use this if we update our &lt;code&gt;main.dart&lt;/code&gt; to contain a new &lt;code&gt;CounterPage&lt;/code&gt; at &lt;code&gt;counter_page.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:storage_counter/pages/counter_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Counter',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: CounterPage(title: "Storage Counter"),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Inside of our &lt;code&gt;CounterPage&lt;/code&gt; we use a &lt;code&gt;FutureBuilder&lt;/code&gt; to get the current count, and otherwise, display a loading indicator. To update our count we get a new count from our &lt;code&gt;CounterService&lt;/code&gt; which also saves this to our persistence layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:my_storage/local_storage_repository.dart';
import 'package:storage_counter/services/counter_service.dart';

class CounterPage extends StatefulWidget {
  CounterPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _CounterPageState createState() =&amp;gt; _CounterPageState();
}

class _CounterPageState extends State&amp;lt;CounterPage&amp;gt; {
  int _counter;
  CounterService _counterService;

  @override
  void initState() {
    super.initState();

    _counter = 0;
    _counterService = CounterService(
        localStorageRepository: LocalStorageRepository("counter.json"));
  }

  Future&amp;lt;void&amp;gt; _incrementCounter() async {
    final int _newCount = await _counterService.incrementAndSave(_counter);

    setState(() {
      _counter = _newCount;
    });
  }

  Future&amp;lt;void&amp;gt; _decrementCounter() async {
    final int _newCount = await _counterService.decrementAndSave(_counter);

    setState(() {
      _counter = _newCount;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              'You have pushed the button this many times:',
            ),
            FutureBuilder&amp;lt;int&amp;gt;(
              future: _counterService.getCount(),
              builder: (BuildContext context, AsyncSnapshot&amp;lt;int&amp;gt; snapshot) {
                if (snapshot.hasData) {
                  _counter = snapshot.data;

                  return Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        icon: Icon(Icons.remove),
                        onPressed: _decrementCounter,
                      ),
                      Text(
                        '$_counter',
                        style: Theme.of(context).textTheme.headline4,
                      ),
                      IconButton(
                        icon: Icon(Icons.add),
                        onPressed: _incrementCounter,
                      ),
                    ],
                  );
                }

                return CircularProgressIndicator();
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Swapping out LocalStorage for SharedPreferences
&lt;/h3&gt;

&lt;p&gt;What if we wanted to change our persistence layer? How about using &lt;code&gt;SharedPreferences&lt;/code&gt; instead? That's easy enough because of the work we've already done.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;shared_preferences&lt;/code&gt; to your &lt;code&gt;pubspec.yaml&lt;/code&gt; on the &lt;code&gt;my_storage&lt;/code&gt; package we created earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  flutter:
    sdk: flutter
  localstorage: ^3.0.1+4
  shared_preferences: ^0.5.6+3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Creating a &lt;code&gt;SharedPreferencesRepository&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;We can then create a &lt;code&gt;SharedPreferencesRepository&lt;/code&gt; which uses &lt;code&gt;SharedPreferences&lt;/code&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:my_storage/i_local_storage_repository.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SharedPreferencesRepository implements ILocalStorageRepository {
  @override
  Future getAll(String key) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getInt(key);
  }

  @override
  Future&amp;lt;void&amp;gt; save(String key, item) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.setInt(key, item);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice how we're implementing our &lt;code&gt;ILocalStorageRepository&lt;/code&gt; here, which contains our two key methods. Here are a couple of points to consider at this stage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name &lt;code&gt;ILocalStorageRepository&lt;/code&gt; &lt;em&gt;could&lt;/em&gt; lend itself to being &lt;em&gt;too tied&lt;/em&gt; to the &lt;code&gt;localstorage&lt;/code&gt; package if we looked at it from this lense. I feel it's still general enough at this stage, but it's certainly a consideration.&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;ILocalStorageRepository&lt;/code&gt; contract is &lt;em&gt;not&lt;/em&gt; representative of the &lt;code&gt;SharedPreferences&lt;/code&gt; implementation, as we're using &lt;code&gt;getInt&lt;/code&gt; and &lt;code&gt;setInt&lt;/code&gt; for this demonstration. Consider making this more general to account for other data type(s) if changing your persistence layer is likely required in the near future.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's swap out our &lt;code&gt;LocalStorageRepository&lt;/code&gt; for the &lt;code&gt;SharedPreferencesRepository&lt;/code&gt; in our &lt;code&gt;CounterPage&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class _CounterPageState extends State&amp;lt;CounterPage&amp;gt; {
  int _counter;
  CounterService _counterService;

  @override
  void initState() {
    super.initState();

    _counter = 0;
    _counterService = CounterService(
      localStorageRepository: SharedPreferencesRepository(),
    );
  }

  //
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we try and increment our counter, you'll notice that things work exactly how they did before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vU5RZLCK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-13-at-17.28.08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vU5RZLCK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-13-at-17.28.08.png" alt="How to Save Data to LocalStorage and SharedPreferences in Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can even display this in another way... using both persistence layers at once!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:my_storage/local_storage_repository.dart';
import 'package:my_storage/shared_preferences_repository.dart';
import 'package:storage_counter/services/counter_service.dart';

class CounterPage extends StatefulWidget {
  CounterPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _CounterPageState createState() =&amp;gt; _CounterPageState();
}

class _CounterPageState extends State&amp;lt;CounterPage&amp;gt; {
  int _sharedPreferencesCounter;
  int _localStorageCounter;

  CounterService _sharedPreferencesCounterService;
  CounterService _localStorageCounterService;

  @override
  void initState() {
    super.initState();

    _localStorageCounter = 0;
    _sharedPreferencesCounter = 0;

    _sharedPreferencesCounterService = CounterService(
      localStorageRepository: SharedPreferencesRepository(),
    );
    _localStorageCounterService = CounterService(
      localStorageRepository: LocalStorageRepository("counter.json"),
    );
  }

  Future&amp;lt;void&amp;gt; _incrementCounter(bool useSharedPrefs) async {
    final int _newCount = useSharedPrefs
        ? await _sharedPreferencesCounterService
            .incrementAndSave(_sharedPreferencesCounter)
        : await _localStorageCounterService
            .incrementAndSave(_localStorageCounter);

    _updateCount(useSharedPrefs, _newCount);
  }

  Future&amp;lt;void&amp;gt; _decrementCounter(bool useSharedPrefs) async {
    final int _newCount = useSharedPrefs
        ? await _sharedPreferencesCounterService
            .decrementAndSave(_sharedPreferencesCounter)
        : await _localStorageCounterService
            .decrementAndSave(_localStorageCounter);

    _updateCount(useSharedPrefs, _newCount);
  }

  void _updateCount(bool useSharedPrefs, int count) {
    setState(() {
      useSharedPrefs
          ? _sharedPreferencesCounter = count
          : _localStorageCounter = count;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              'You have pushed the button this many times:',
            ),
            _buildSharedPreferencesCounter(),
            _buildLocalStorageCounter()
          ],
        ),
      ),
    );
  }

  FutureBuilder&amp;lt;int&amp;gt; _buildSharedPreferencesCounter() {
    return FutureBuilder&amp;lt;int&amp;gt;(
      future: _sharedPreferencesCounterService.getCount(),
      builder: (BuildContext context, AsyncSnapshot&amp;lt;int&amp;gt; snapshot) {
        if (snapshot.hasData) {
          _sharedPreferencesCounter = snapshot.data;

          return Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: Icon(Icons.remove),
                onPressed: () =&amp;gt; _decrementCounter(true),
              ),
              Text(
                '$_sharedPreferencesCounter',
                style: Theme.of(context).textTheme.headline4,
              ),
              IconButton(
                icon: Icon(Icons.add),
                onPressed: () =&amp;gt; _incrementCounter(true),
              ),
            ],
          );
        }

        return CircularProgressIndicator();
      },
    );
  }

  FutureBuilder&amp;lt;int&amp;gt; _buildLocalStorageCounter() {
    return FutureBuilder&amp;lt;int&amp;gt;(
      future: _localStorageCounterService.getCount(),
      builder: (BuildContext context, AsyncSnapshot&amp;lt;int&amp;gt; snapshot) {
        if (snapshot.hasData) {
          _localStorageCounter = snapshot.data;

          return Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: Icon(Icons.remove),
                onPressed: () =&amp;gt; _decrementCounter(false),
              ),
              Text(
                '$_localStorageCounter',
                style: Theme.of(context).textTheme.headline4,
              ),
              IconButton(
                icon: Icon(Icons.add),
                onPressed: () =&amp;gt; _incrementCounter(false),
              ),
            ],
          );
        }

        return CircularProgressIndicator();
      },
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's our output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yFTpV0eM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-13-at-17.37.31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yFTpV0eM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/04/Screenshot-2020-04-13-at-17.37.31.png" alt="How to Save Data to LocalStorage and SharedPreferences in Flutter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yup. Totally not necessary, but a fun experiment nonetheless.&lt;/p&gt;

&lt;p&gt;I hope you've found this useful! I'd love to hear your thoughts.&lt;/p&gt;

&lt;p&gt;Code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PaulHalliday/my_storage"&gt;https://github.com/PaulHalliday/my_storage&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/PaulHalliday/storage_counter"&gt;https://github.com/PaulHalliday/storage_counter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>Ionic 5 and React: Geolocation</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Wed, 04 Mar 2020 03:04:42 +0000</pubDate>
      <link>https://dev.to/paulhalliday/ionic-5-and-react-geolocation-2bih</link>
      <guid>https://dev.to/paulhalliday/ionic-5-and-react-geolocation-2bih</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O-ThlzCV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/03/Location-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O-ThlzCV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/03/Location-1.png" alt="Ionic 5 and React: Geolocation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article we'll be looking at how to get the user's current location using the Geolocation plugin alongside our Ionic and React application(s). With this plugin we'll be able to get the user's &lt;code&gt;latitude&lt;/code&gt;, &lt;code&gt;longitude&lt;/code&gt; , &lt;code&gt;altitude&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Video
&lt;/h4&gt;

&lt;p&gt;Want to see the video for this article? Check it out below:&lt;/p&gt;

&lt;h3&gt;
  
  
  New Ionic React project
&lt;/h3&gt;

&lt;p&gt;In order to ensure we're all starting from the same point, run the following inside of your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ionic start ionic_geolocation

? Framework:
&amp;gt; React

? Starter template:
&amp;gt; blank

? Integrate Capacitor
&amp;gt; Y

$ cd ionic_geolocation

$ npm install @ionic-native/core
$ npm install cordova-plugin-geolocation
$ npm install @ionic-native/geolocation

$ ionic cap sync

$ code .

$ ionic serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This may look like a lot, but here we're essentially:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a new Ionic and React project&lt;/li&gt;
&lt;li&gt;Adding the Geolocation plugin&lt;/li&gt;
&lt;li&gt;Update web/native capacitor elements&lt;/li&gt;
&lt;li&gt;Running a development server at &lt;a href="http://localhost:8100"&gt;http://localhost:8100&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Location
&lt;/h3&gt;

&lt;p&gt;We'll then create ourselves a new &lt;code&gt;GeolocationButton&lt;/code&gt; component which can be used to interact with the &lt;code&gt;Geolocation&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;This will either display an &lt;code&gt;IonLoading&lt;/code&gt; component whilst the &lt;code&gt;Geolocation.getCurrentPosition&lt;/code&gt; is being called, show an &lt;code&gt;IonToast&lt;/code&gt; with an error message (usually if the user has blocked permissions), &lt;em&gt;or&lt;/em&gt; show the current &lt;code&gt;latitude&lt;/code&gt; and &lt;code&gt;longitude&lt;/code&gt; as the button text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import { Geolocation, Geoposition } from '@ionic-native/geolocation';
import { IonButton, IonLoading, IonToast } from '@ionic/react';
import React, { useState } from 'react';

interface LocationError {
    showError: boolean;
    message?: string;
}

const GeolocationButton: React.FC = () =&amp;gt; {
    const [loading, setLoading] = useState&amp;lt;boolean&amp;gt;(false);
    const [error, setError] = useState&amp;lt;LocationError&amp;gt;({ showError: false });
    const [position, setPosition] = useState&amp;lt;Geoposition&amp;gt;();

    const getLocation = async () =&amp;gt; {
        setLoading(true);

        try {
            const position = await Geolocation.getCurrentPosition();
            setPosition(position);
            setLoading(false);
            setError({ showError: false });
        } catch (e) {
            setError({ showError: true, message: e.message });
            setLoading(false);
        }
    }

    return (
        &amp;lt;&amp;gt;
            &amp;lt;IonLoading
                isOpen={loading}
                onDidDismiss={() =&amp;gt; setLoading(false)}
                message={'Getting Location...'}
            /&amp;gt;
            &amp;lt;IonToast
                isOpen={error.showError}
                onDidDismiss={() =&amp;gt; setError({ message: "", showError: false })}
                message={error.message}
                duration={3000}
            /&amp;gt;
            &amp;lt;IonButton color="primary" onClick={getLocation}&amp;gt;{position ? `${position.coords.latitude} ${position.coords.longitude}` : "Get Location"}&amp;lt;/IonButton&amp;gt;
        &amp;lt;/&amp;gt;
    );
};

export default GeolocationButton;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then add this into &lt;code&gt;Home.tsx&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  IonContent,
  IonHeader,
  IonPage,
  IonTitle,
  IonToolbar,
  IonGrid,
  IonCol,
  IonRow,
} from "@ionic/react";
import React from "react";
import ExploreContainer from "../components/ExploreContainer";
import "./Home.css";
import GeolocationButton from "../components/GeolocationButton";

const Home: React.FC = () =&amp;gt; {
  return (
    &amp;lt;IonPage&amp;gt;
      &amp;lt;IonHeader&amp;gt;
        &amp;lt;IonToolbar&amp;gt;
          &amp;lt;IonTitle&amp;gt;Geolocation&amp;lt;/IonTitle&amp;gt;
        &amp;lt;/IonToolbar&amp;gt;
      &amp;lt;/IonHeader&amp;gt;
      &amp;lt;IonContent&amp;gt;
        &amp;lt;IonHeader collapse="condense"&amp;gt;
          &amp;lt;IonToolbar&amp;gt;
            &amp;lt;IonTitle size="large"&amp;gt;Geolocation&amp;lt;/IonTitle&amp;gt;
          &amp;lt;/IonToolbar&amp;gt;
        &amp;lt;/IonHeader&amp;gt;

        &amp;lt;div className="container"&amp;gt;
          &amp;lt;GeolocationButton /&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/IonContent&amp;gt;
    &amp;lt;/IonPage&amp;gt;
  );
};

export default Home;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I've added the &lt;code&gt;container&lt;/code&gt; class to our button to center it in the middle of &lt;code&gt;IonContent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.container {
  text-align: center;
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If all goes well, clicking on the button will give us the current &lt;code&gt;latitude&lt;/code&gt; and &lt;code&gt;longitude&lt;/code&gt; as we expect:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tDslfTZX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/03/Location.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tDslfTZX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.school/content/images/2020/03/Location.png" alt="Ionic 5 and React: Geolocation"&gt;&lt;/a&gt;San Fransisco!&lt;/p&gt;

</description>
      <category>ionic</category>
      <category>react</category>
    </item>
    <item>
      <title>Ionic 4: Angular, Vue.js and React</title>
      <dc:creator>Paul Halliday</dc:creator>
      <pubDate>Wed, 27 Mar 2019 15:19:17 +0000</pubDate>
      <link>https://dev.to/paulhalliday/ionic-4-angular-vue-js-and-react-1o14</link>
      <guid>https://dev.to/paulhalliday/ionic-4-angular-vue-js-and-react-1o14</guid>
      <description>&lt;p&gt;&lt;a href="https://ionicworkshop.com/posts/introduction-to-ionic-framework-angular-vue-react/" rel="noopener noreferrer"&gt;This is also published on Ionic Workshop.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're looking to make cross platform web applications, you've undoubtedly heard of Ionic Framework. With the latest iteration of Ionic (version 4) we're no longer limited to just using Angular! &lt;/p&gt;

&lt;p&gt;Personally, I've been a fan of the Angular ecosystem since the early days of Angular.js, but Ionic have (correctly) recognised that the modern web developer uses a variety of different frontend frameworks. &lt;/p&gt;

&lt;p&gt;We'll be creating a basic Ionic application three times, allowing us to investigate the creation of an Ionic project in Angular, Vue and React.  After following this article you'll be able to take advantage of Ionic in any framework that you want!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prior to following this tutorial, ensure you have &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Installing the Ionic CLI
&lt;/h2&gt;

&lt;p&gt;We can install the Ionic CLI to assist us in making Ionic applications. The Ionic CLI allows us to interact with the various services that Ionic offers, as well as initialise new projects. &lt;/p&gt;

&lt;p&gt;Run the following inside of your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; ionic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in mind, let's create our first Ionic application using Angular!&lt;/p&gt;

&lt;h1&gt;
  
  
  Angular
&lt;/h1&gt;

&lt;p&gt;Project creation with the Ionic CLI is currently just limited to Angular, but in the future I can imagine other templates will be created for Vue, React and others.&lt;/p&gt;

&lt;p&gt;Run the following in your terminal to create a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start a new Ionic and Angular project based on the blank template&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ionic start ionic-angular blank

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; don&lt;span class="s1"&gt;'t install AppFlow at this stage

# Change directory
$ cd ionic-angular

# Run the project
$ ionic serve
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ionic CLI already scaffolds out a basic home page for us. If we look inside of &lt;code&gt;src/app/home.page.html&lt;/code&gt;, we'll see the following markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ion-header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ion-toolbar&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-title&amp;gt;&lt;/span&gt;
      Ionic Blank
    &lt;span class="nt"&gt;&amp;lt;/ion-title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ion-toolbar&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ion-header&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ion-content&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  The world is your oyster.
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you get lost, the &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://ionicframework.com/docs/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;docs&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; will be your guide.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ion-content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use any Ionic component that we want. Let's make some slight changes to this by adding an &lt;code&gt;ion-card&lt;/code&gt;, and adding a &lt;code&gt;color&lt;/code&gt; attribute to the &lt;code&gt;ion-toolbar&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ion-header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ion-toolbar&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-title&amp;gt;&lt;/span&gt;Hello, Ionic!&lt;span class="nt"&gt;&amp;lt;/ion-title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ion-toolbar&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ion-header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ion-content&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ion-card&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
      &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://images.unsplash.com/photo-1552394459-917cbbffbc84?ixlib=rb-1.2.1&amp;amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=500&amp;amp;q=80"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-card-header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ion-card-subtitle&amp;gt;&lt;/span&gt;Isn't it great?&lt;span class="nt"&gt;&amp;lt;/ion-card-subtitle&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ion-card-title&amp;gt;&lt;/span&gt;Look at this view!&lt;span class="nt"&gt;&amp;lt;/ion-card-title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ion-card-header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-card-content&amp;gt;&lt;/span&gt;
      Although, it does look fairly cold.
    &lt;span class="nt"&gt;&amp;lt;/ion-card-content&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ion-card&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ion-content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the results of doing this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F02-ionic-vue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F02-ionic-vue.png" alt="Check out this vue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty sweet! Let's move on to looking at how to do the same with Vue.js.&lt;/p&gt;

&lt;h1&gt;
  
  
  Vue.js
&lt;/h1&gt;

&lt;p&gt;Adding Ionic to a Vue.js project is simple. We firstly generate a new Vue project with the Vue CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ensure you have the Vue.js CLI installed&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @vue/cli

&lt;span class="c"&gt;# Create a new Vue project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;vue create ionic-vue 

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select &lt;/span&gt;default options

&lt;span class="c"&gt;# Change directory into ionic-vue&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;ionic-vue

&lt;span class="c"&gt;# Run the project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm run serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates us a new Vue.js application and opens it inside of our browser. We'll use this application as the basis for our Ionic app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F01-ionic-vue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F01-ionic-vue.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Ionic Vue
&lt;/h2&gt;

&lt;p&gt;Next up, we need to install Ionic into our project. Back to the command line (in a new terminal window):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ionic/core @ionic/vue vue-router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've now got Ionic installed into our project. As &lt;code&gt;@ionic/vue&lt;/code&gt; is exposed as a Vue.js plugin, we'll need to import this and tell Vue that we'd like to use it with &lt;code&gt;Vue.use&lt;/code&gt;. Head over to &lt;code&gt;main.js&lt;/code&gt; and do exactly that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;IonicVue&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/core/css/core.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/core/css/ionic.bundle.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productionTip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IonicVue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;$mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;What's happening here?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;We're firstly importing &lt;code&gt;IonicVue&lt;/code&gt; from &lt;code&gt;@ionic/vue&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Next, we're importing the core Ionic styles from &lt;code&gt;@ionic/core&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, we're registering the IonicVue plugin using &lt;code&gt;Vue.use(IonicVue)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;



&lt;h2&gt;
  
  
  Using Ionic
&lt;/h2&gt;

&lt;p&gt;Now that we've installed and initialised Ionic inside of our Vue.js application, we're able to use Ionic components. Let's head over to &lt;code&gt;App.vue&lt;/code&gt; and create the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ion-app&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ion-toolbar&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ion-title&amp;gt;&lt;/span&gt;Hello, Ionic!&lt;span class="nt"&gt;&amp;lt;/ion-title&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ion-toolbar&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ion-header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-content&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ion-card&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://images.unsplash.com/photo-1552394459-917cbbffbc84?ixlib=rb-1.2.1&amp;amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=500&amp;amp;q=80"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ion-card-header&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ion-card-subtitle&amp;gt;&lt;/span&gt;Isn't it great?&lt;span class="nt"&gt;&amp;lt;/ion-card-subtitle&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ion-card-title&amp;gt;&lt;/span&gt;Look at this view!&lt;span class="nt"&gt;&amp;lt;/ion-card-title&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ion-card-header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ion-card-content&amp;gt;&lt;/span&gt;
          Although, it does look fairly cold.
        &lt;span class="nt"&gt;&amp;lt;/ion-card-content&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ion-card&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ion-content&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ion-app&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the following Ionic application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F02-ionic-vue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F02-ionic-vue.png" alt="Check out this vue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out this &lt;em&gt;vue&lt;/em&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice how we were able to take the same Ionic content from our Angular application with &lt;em&gt;zero&lt;/em&gt; changes. This is the power of Ionic in action! &lt;/p&gt;

&lt;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;React offers us the same ability to create awesome Ionic projects, but with some minor differences regarding how the components are used.&lt;/p&gt;

&lt;p&gt;Similar to Vue, we'll start off with a blank React application and add Ionic onto this with &lt;code&gt;@ionic/core&lt;/code&gt; and &lt;code&gt;@ionic/react&lt;/code&gt;. We'll be using &lt;code&gt;create-react-app&lt;/code&gt; to start our React project. Run the following in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install create-react-app globally&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;create-react-app &lt;span class="nt"&gt;-g&lt;/span&gt;

&lt;span class="c"&gt;# Create a new React project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;create-react-app ionic-react &lt;span class="nt"&gt;--typescript&lt;/span&gt;

&lt;span class="c"&gt;# Change directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;ionic-react

&lt;span class="c"&gt;# Run the project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Ionic
&lt;/h2&gt;

&lt;p&gt;We'll now need to install &lt;code&gt;@ionic/react&lt;/code&gt;, &lt;code&gt;@ionic/core&lt;/code&gt; and &lt;code&gt;react-router&lt;/code&gt; into our React project. Ionic currently depends on &lt;code&gt;react-router&lt;/code&gt;, so we'll need to add it even though we're not using the router at this stage.&lt;/p&gt;

&lt;p&gt;Run the following in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ionic/react react-router react-router-dom @types/react-router @types/react-router-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Ionic
&lt;/h2&gt;

&lt;p&gt;In a similar fashion to our previous examples, we'll start off by importing the relevant CSS files. &lt;/p&gt;

&lt;p&gt;Head over to &lt;code&gt;index.tsx&lt;/code&gt; and import the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/core/css/core.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/core/css/ionic.bundle.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then import individual components such as &lt;code&gt;IonApp&lt;/code&gt;, &lt;code&gt;IonHeader&lt;/code&gt;, &lt;code&gt;IonContent&lt;/code&gt; and so on like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IonApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonContent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now recreate the previous example of our toolbar and card. Head over to &lt;code&gt;App.tsx&lt;/code&gt; and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IonApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonToolbar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonCardHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonCardTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonCardSubtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IonCardContent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonApp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonHeader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonToolbar&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonTitle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Ionic&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonTitle&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonToolbar&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonHeader&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonContent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonCard&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://images.unsplash.com/photo-1552394459-917cbbffbc84?ixlib=rb-1.2.1&amp;amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=500&amp;amp;q=80&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonCardHeader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonCardSubtitle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Isn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t it great?&amp;lt;/IonCardSubtitle&amp;gt;
              &amp;lt;IonCardTitle&amp;gt;Look at this view!&amp;lt;/IonCardTitle&amp;gt;
            &amp;lt;/IonCardHeader&amp;gt;
            &amp;lt;IonCardContent&amp;gt;Although, it does look fairly cold.&amp;lt;/IonCardContent&amp;gt;
          &amp;lt;/IonCard&amp;gt;
        &amp;lt;/IonContent&amp;gt;
      &amp;lt;/IonApp&amp;gt;
    );
  }
}

export default App;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tada! This gives us the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F02-ionic-vue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fionicworkshop.com%2Fmedia%2F01-intro-ionic%2F02-ionic-vue.png" alt="Check out this vue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The major difference in the React implementation is the need to import each component individually. Aside from that, the application looks and feels identical to our other implementations!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article investigated the implementation of an Ionic 4 application by using Angular, Vue and React. You should now feel comfortable using Ionic in a variety of different environments! 🙌&lt;/p&gt;

&lt;p&gt;Did you enjoy this article? Let me know on &lt;a href="https://twitter.com/IonicWorkshop" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or at &lt;a href="https://ionicworkshop.com" rel="noopener noreferrer"&gt;Ionic Workshop!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ionic</category>
      <category>vue</category>
      <category>angular</category>
      <category>react</category>
    </item>
  </channel>
</rss>
