<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[JoFlee]]></title><description><![CDATA[JoFlee]]></description><link>https://blog.joflee.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1714126761522/a625ed8b-a4d0-4df1-bb37-0da9a2bb6d26.png</url><title>JoFlee</title><link>https://blog.joflee.com</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 07:27:32 GMT</lastBuildDate><atom:link href="https://blog.joflee.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Create Animations in Flutter]]></title><description><![CDATA[Flutter animations are a way to make your app’s user interface more dynamic and engaging. They can help create smooth transitions, visual effects, and interactive elements that enhance the user experience. Here are some key points about Flutter anima...]]></description><link>https://blog.joflee.com/how-to-create-animations-in-flutter</link><guid isPermaLink="true">https://blog.joflee.com/how-to-create-animations-in-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[animations]]></category><category><![CDATA[Flutter Widgets]]></category><category><![CDATA[examples]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Tue, 24 Sep 2024 05:17:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725625893030/b325a4b7-9a02-4122-9fe1-2a4be9e715b7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Flutter animations are a way to make your app’s user interface more dynamic and engaging. They can help create smooth transitions, visual effects, and interactive elements that enhance the user experience. Here are some key points about Flutter animations:</p>
<ol>
<li><p><strong>Types of Animations</strong>:</p>
<ul>
<li><p><strong>Implicit Animations</strong>: These are simple to implement and automatically handle the animation for you. Examples include <code>AnimatedContainer</code>, <code>AnimatedOpacity</code>, and <code>AnimatedAlign</code>.</p>
</li>
<li><p><strong>Explicit Animations</strong>: These give you more control over the animation process. Examples include <code>AnimationController</code>, <code>Tween</code>, and <code>AnimatedBuilder</code>.</p>
</li>
</ul>
</li>
<li><p><strong>Common Animation Widgets</strong>:</p>
<ul>
<li><p><strong>AnimatedContainer</strong>: Animates changes in its properties like size, colour, and alignment.</p>
</li>
<li><p><strong>TweenAnimationBuilder</strong>: Allows you to define a tween (a way to interpolate between two values) and animate it.</p>
</li>
<li><p><strong>AnimationController</strong>: Manages the animation’s duration and direction.</p>
</li>
<li><p><strong>CurvedAnimation</strong>: Adds non-linear motion to animations, making them feel more natural.</p>
</li>
</ul>
</li>
<li><p><strong>Animation Techniques</strong>:</p>
<ul>
<li><p><strong>Tweening</strong>: Interpolates between a start and end value over a specified duration.</p>
</li>
<li><p><strong>Physics-based Animations</strong>: Simulate real-world physics, like spring or gravity animations.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-implicit-animations">Implicit Animations</h2>
<p>Implicit animations in Flutter are built on several key components that make them easy to use and effective for creating smooth transitions.</p>
<h3 id="heading-key-components-for-implicit-animations">Key Components for Implicit Animations:</h3>
<ol>
<li><strong>State Management:</strong></li>
</ol>
<ul>
<li><p>Implicit animations rely on state changes. When a property of a widget changes, the animation automatically begins.</p>
</li>
<li><p>This is typically managed using <code>setState</code> in a StatefulWidget.</p>
</li>
</ul>
<ol start="2">
<li><strong>Tweening:</strong></li>
</ol>
<ul>
<li><p>Tweening refers to calculating intermediate values between the start and end points of an animation.</p>
</li>
<li><p>Flutter handles this internally for implicit animations, ensuring smooth transitions.</p>
</li>
</ul>
<ol start="3">
<li><strong>Duration and Curve:</strong></li>
</ol>
<ul>
<li><p><strong>Duration</strong>: Specifies how long the animation should take to complete. This is set using the <code>duration</code> parameter.</p>
</li>
<li><p><strong>Curve</strong>: Defines the rate of change of the animation over time. Common curves include <code>Curves.easeIn</code>, <code>Curves.easeOut</code>, and <code>Curves.line</code></p>
</li>
</ul>
<ol start="4">
<li><strong>Implicitly Animated Widgets:</strong></li>
</ol>
<ul>
<li><p>Flutter provides several built-in widgets for implicit animations, such as:</p>
</li>
<li><p><code>AnimatedContainer</code>: Animates changes in size, color, and other properties.</p>
</li>
<li><p><code>AnimatedOpacity</code>: Animates changes in opacity.</p>
</li>
<li><p><code>AnimatedPadding</code>: Animates changes in padding.</p>
</li>
<li><p><code>AnimatedPositioned</code>: Animates changes in position within a <code>Stack</code>.</p>
</li>
<li><p><code>AnimatedDefaultTextStyle</code>: Animates changes in text style.</p>
</li>
</ul>
<h3 id="heading-example-of-an-implicit-animation">Example of an Implicit Animation :</h3>
<p>Here’s a simple example using <code>AnimatedContainer</code> to animate changes in size and colour:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AnimatedContainerDemo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _AnimatedContainerDemoState createState() =&gt; _AnimatedContainerDemoState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_AnimatedContainerDemoState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">AnimatedContainerDemo</span>&gt; </span>{
  <span class="hljs-built_in">bool</span> _isExpanded = <span class="hljs-keyword">false</span>;

  <span class="hljs-keyword">void</span> _toggleContainer() {
    setState(() {
      _isExpanded = !_isExpanded;
    });
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: Text(<span class="hljs-string">'AnimatedContainer Practice'</span>)),
      body: Center(
        child: AnimatedContainer(
          width: _isExpanded ? <span class="hljs-number">200</span> : <span class="hljs-number">100</span>,
           height: _isExpanded ? <span class="hljs-number">200</span> : <span class="hljs-number">100</span>,
          color: _isExpanded ? Colors.blue : Colors.red,
          alignment: Alignment.center,
          duration: <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>),
          curve: Curves.easeInOut,
          child: Text(<span class="hljs-string">'Press Me!'</span>, style: TextStyle(color: Colors.white)),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggleContainer,
        child: Icon(Icons.play_arrow),
      ),
    );
  }
}
</code></pre>
<p>In this example, tapping the button toggles the size and colour of the container, creating a smooth animation.</p>
<p>Implicit animations are a powerful tool for adding visual polish to your Flutter apps without the complexity of manually managing animation controllers.</p>
<hr />
<h2 id="heading-explicit-animations">Explicit Animations</h2>
<p>Explicit animations in Flutter give you fine-grained control over the animation process, allowing you to define the animation’s behaviour, properties, and lifecycle. Unlike implicit animations, which handle the animation for you, explicit animations require you to manage the animation’s state and transitions manually.</p>
<h3 id="heading-key-components-of-explicit-animations">Key Components of Explicit Animations:</h3>
<ol>
<li><p><strong>AnimationController</strong>:</p>
<ul>
<li><p>Manages the duration, direction, and state of the animation.</p>
</li>
<li><p>Controls the animation’s lifecycle, including starting, stopping, and reversing.</p>
</li>
</ul>
</li>
<li><p><strong>Tween</strong>:</p>
<ul>
<li><p>Defines the range of values the animation will interpolate between.</p>
</li>
<li><p>Common types include <code>Tween&lt;double&gt;</code>, <code>ColorTween</code>, and <code>SizeTween</code>.</p>
</li>
</ul>
</li>
<li><p><strong>CurvedAnimation</strong>:</p>
<ul>
<li><p>Adds non-linear motion to the animation, making it more natural.</p>
</li>
<li><p>Uses predefined curves like <code>Curves.easeIn</code>, <code>Curves.bounceOut</code>, etc.</p>
</li>
</ul>
</li>
<li><p><strong>AnimatedBuilder</strong>:</p>
<ul>
<li><p>Rebuilds the widget tree whenever the animation value changes.</p>
</li>
<li><p>Provides a way to separate the animation logic from the widget tree.</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-example-of-an-explicit-animation">Example of an Explicit Animation:</h3>
<p>Here’s a simple example of an explicit animation that animates the size and colour of a container:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text(<span class="hljs-string">'Explicit Animation Practice'</span>)),
        body: ExplicitAnimationDemo(),
      ),
    );
  }
}

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ExplicitAnimationDemoState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ExplicitAnimationDemo</span>&gt; <span class="hljs-title">with</span> <span class="hljs-title">SingleTickerProviderStateMixin</span> </span>{
  AnimationController _controller;
  Animation&lt;<span class="hljs-built_in">double</span>&gt; _sizeAnimation;
  Animation&lt;Color&gt; _colorAnimation;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _controller = AnimationController(
      duration: <span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">3</span>),
      vsync: <span class="hljs-keyword">this</span>,
    );

    _sizeAnimation = Tween&lt;<span class="hljs-built_in">double</span>&gt;(begin: <span class="hljs-number">50.0</span>, end: <span class="hljs-number">200.0</span>).animate(_controller);
    _colorAnimation = ColorTween(begin: Colors.blue, end: Colors.red).animate(_controller);

    _controller.forward();
  }
 <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _controller.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Center(
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, child) {
          <span class="hljs-keyword">return</span> Container(
            width: _sizeAnimation.value,
            height: _sizeAnimation.value,
            color: _colorAnimation.value,
          );
        },
      ),
    );
  }
}
</code></pre>
<p>In this example:</p>
<ul>
<li><p><strong>AnimationController</strong>: Manages the animation’s duration and state.</p>
</li>
<li><p><strong>Tween</strong>: Defines the start and end values for size and colour.</p>
</li>
<li><p><strong>AnimatedBuilder</strong>: Rebuilds the container with the updated animation values.</p>
</li>
</ul>
<hr />
<h3 id="heading-some-common-pitfalls-to-avoid-in-animation-code">Some common pitfalls to avoid in animation code:</h3>
<p>When working with animations in Flutter, there are several common pitfalls to be aware of to ensure your animations are smooth and efficient. Here are some key ones to avoid:</p>
<ol>
<li><strong>Overusing StatefulWidgets:</strong></li>
</ol>
<ul>
<li><p><strong>Pitfall</strong>: Using too many StatefulWidgets can lead to complex and hard-to-maintain code.</p>
</li>
<li><p><strong>Solution</strong>: Use StatelessWidgets where possible and manage state efficiently with state management solutions like Provider, Riverpod, or GetX.</p>
</li>
</ul>
<ol start="2">
<li><strong>Not Disposing Animation Controllers:</strong></li>
</ol>
<ul>
<li><p><strong>Solution</strong>: Always dispose of your animation controllers in the <code>dispose</code> method of your StatefulWidget.</p>
</li>
<li><p><strong>Pitfall</strong>: Failing to dispose of animation controllers can lead to memory leaks.</p>
</li>
</ul>
<ol start="3">
<li><strong>Unnecessary Rebuilds:</strong></li>
</ol>
<ul>
<li><p><strong>Pitfall</strong>: Rebuilding the entire widget tree unnecessarily can cause performance issues.</p>
</li>
<li><p><strong>Solution</strong>: Use <code>AnimatedBuilder</code> or <code>AnimatedWidget</code> to optimise performance by only rebuilding the parts of the widget tree that need to be animated.</p>
</li>
</ul>
<ol start="4">
<li><strong>Ignoring Perfomance Optimisation:</strong></li>
</ol>
<ul>
<li><p><strong>Pitfall</strong>: Not optimising animations can lead to janky and unresponsive UIs.</p>
</li>
<li><p><strong>Solution</strong>: Use tools like the Flutter Inspector to track widget rebuilds and optimise performance. Avoid offscreen rendering and excessive use of <code>saveLayer()</code></p>
</li>
</ul>
<ol start="5">
<li><strong>Improper Use of Tween and Curves</strong></li>
</ol>
<ul>
<li><p><strong>Pitfall</strong>: Using linear animations for all transitions can make animations feel unnatural.</p>
</li>
<li><p><strong>Solution</strong>: Use <code>Tween</code> and apply easing curves like <code>Curves.easeInOut</code> to create more natural animations**.**</p>
</li>
</ul>
<ol start="6">
<li><strong>Animating Large Lists Inefficiently</strong></li>
</ol>
<ul>
<li><p><strong>Solution</strong>: Use <code>ListView.builder</code> or <code>AnimatedList</code> to efficiently handle large lists and their animations</p>
</li>
<li><p><strong>Pitfall</strong>: Animating large lists directly can be resource-intensive.</p>
</li>
</ul>
<ol start="7">
<li><strong>Overcomplicating Simple Animations</strong></li>
</ol>
<ul>
<li><p><strong>Pitfall</strong>: Using complex animation controllers for simple animations can make the code harder to manage.</p>
</li>
<li><p><strong>Solution</strong>: Use implicit animation widgets like <code>AnimatedContainer</code>, <code>AnimatedOpacity</code>, and <code>AnimatedPositioned</code> for simpler animations.</p>
</li>
</ul>
<ol start="8">
<li><strong>Not Testing Animations</strong></li>
</ol>
<ul>
<li><p><strong>Pitfall</strong>: Failing to test animations can lead to unexpected behaviour in production.</p>
</li>
<li><p><strong>Solution</strong>: Write unit tests for your animation logic and use Flutter’s debugging tools to inspect and debug animations</p>
</li>
</ul>
<h3 id="heading-example">Example:</h3>
<p>Here’s an example of properly disposing of an animation controller:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyAnimation</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _MyAnimationState createState() =&gt; _MyAnimationState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyAnimationState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyAnimation</span>&gt; <span class="hljs-title">with</span> <span class="hljs-title">SingleTickerProviderStateMixin</span> </span>{
  AnimationController _controller;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _controller = AnimationController(
      duration: <span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">3</span>),
      vsync: <span class="hljs-keyword">this</span>,
    );
    _controller.forward();
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _controller.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  } 
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: Text(<span class="hljs-string">'Animation Practice'</span>)),
      body: Center(
        child: Container(
          width: _controller.value * <span class="hljs-number">300</span>,
          height: _controller.value * <span class="hljs-number">300</span>,
          color: Colors.blue,
        ),
      ),
    );
  }
}
</code></pre>
<p>By avoiding these common pitfalls, you can create more efficient and maintainable animations in your Flutter applications.</p>
<hr />
<blockquote>
<p>In conclusion, mastering Flutter animations can significantly enhance your app's user experience by making the interface more dynamic and engaging. Understanding implicit and explicit animations, along with best practices, ensures your animations are visually appealing, efficient, and maintainable. Whether using simple implicit animations or complex explicit ones, these techniques can elevate the quality of your mobile applications.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Beginner's Guide to Embedding AI in Flutter Apps]]></title><description><![CDATA[To integrate the Gemini AI API into a Flutter project, you can use the http package to make HTTP requests. Here’s how you can create a basic Flutter app to interact with the Gemini API.
Step 1: Add Dependencies
First, add the http package to your pub...]]></description><link>https://blog.joflee.com/beginners-guide-to-embedding-ai-in-flutter-apps</link><guid isPermaLink="true">https://blog.joflee.com/beginners-guide-to-embedding-ai-in-flutter-apps</guid><category><![CDATA[AI]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[integration]]></category><category><![CDATA[Machine Learning]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Tue, 20 Aug 2024 04:07:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723717713880/07bdb0a3-aae7-4bf5-a334-641ded030c00.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To integrate the Gemini AI API into a Flutter project, you can use the <code>http</code> package to make HTTP requests. Here’s how you can create a basic Flutter app to interact with the Gemini API.</p>
<h3 id="heading-step-1-add-dependencies">Step 1: Add Dependencies</h3>
<p>First, add the <code>http</code> package to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">http:</span> <span class="hljs-string">^1.2.2</span>
</code></pre>
<p>Run <code>flutter pub get</code> to install the package.</p>
<h3 id="heading-step-2-create-a-dart-service-to-handle-the-api-request">Step 2: Create a Dart Service to Handle the API Request</h3>
<p>Create a service in your Flutter project to interact with the Gemini API.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AIService</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> apiKey = <span class="hljs-string">'YOUR_API_KEY'</span>;

  Future&lt;<span class="hljs-built_in">String</span>&gt; getAIResponse(<span class="hljs-built_in">String</span> inputText) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> url = <span class="hljs-string">'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:getAIResponse?key=<span class="hljs-subst">$apiKey</span>'</span>;
    <span class="hljs-keyword">final</span> headers = {<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>};
    <span class="hljs-keyword">final</span> body = jsonEncode({
      <span class="hljs-string">'contents'</span>: [
        {
          <span class="hljs-string">'parts'</span>: [
            {<span class="hljs-string">'text'</span>: inputText}
          ]
        }
      ]
    });

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(<span class="hljs-built_in">Uri</span>.parse(url), headers: headers, body: body);
      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-keyword">final</span> data = jsonDecode(response.body);
        <span class="hljs-keyword">return</span> data[<span class="hljs-string">'results'</span>][<span class="hljs-number">0</span>][<span class="hljs-string">'output'</span>] ?? <span class="hljs-string">'No response by AI'</span>;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Error: <span class="hljs-subst">${response.statusCode}</span> - <span class="hljs-subst">${response.body}</span>'</span>;
      }
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>;
    }
  }
}
</code></pre>
<h3 id="heading-step-3-create-a-ui-to-input-text-and-display-ai-response">Step 3: Create a UI to Input Text and Display AI Response</h3>
<p>In your <code>main.dart</code> file, create a simple UI to input text and display the response from the Gemini API.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'gemini_service.dart'</span>;

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      home: HomeScreen(),
    );
  }
}

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_HomeScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">HomeScreen</span>&gt; </span>{
  <span class="hljs-keyword">final</span> TextEditingController _controller = TextEditingController();
  <span class="hljs-keyword">final</span> AIService _aiService = AIService();
  <span class="hljs-built_in">String</span> _response = <span class="hljs-string">''</span>;

  <span class="hljs-keyword">void</span> _sendRequest() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> inputText = _controller.text;
    <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _aiService.getAIResponse(inputText);
    setState(() {
      _response = result;
    });
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: Text(<span class="hljs-string">'AI Service'</span>)),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">20.0</span>),
        child: Column(
          children: [
            TextField(
              controller: _controller,
              decoration: InputDecoration(labelText: <span class="hljs-string">'Enter your input here'</span>),
            ),
            SizedBox(height: <span class="hljs-number">25</span>),
            ElevatedButton(
              onPressed: _sendRequest,
              child: Text(<span class="hljs-string">'Generate'</span>),
            ),
            SizedBox(height: <span class="hljs-number">25</span>),
            Text(
              _response,
              style: TextStyle(fontSize: <span class="hljs-number">18</span>),
            ),
          ],
        ),
      ),
    );
  }
}
</code></pre>
<h3 id="heading-step-4-run-the-flutter-app">Step 4: Run the Flutter App</h3>
<p>Replace <code>'YOUR_API_KEY'</code> with your actual API key from the Gemini AI platform. After setting this up, you can run the app on your emulator or device to see the AI response.</p>
<p>This basic app allows users to input text and then sends a request to the Gemini API. The AI-generated content is displayed on the screen.</p>
<hr />
<h2 id="heading-steps-to-find-an-api-key-from-the-google-ai-studio-website">Steps to find an API key from the Google AI Studio website:</h2>
<h3 id="heading-step-1-sign-in-to-google-ai-studio">Step 1: Sign In to Google AI Studio</h3>
<ol>
<li><p>Go to <a target="_blank" href="https://ai.google.dev/aistudio">Google AI Studio</a>.</p>
</li>
<li><p>Sign in using your Google account.</p>
</li>
</ol>
<h3 id="heading-step-2-access-the-api-section">Step 2: Access the API Section</h3>
<ol>
<li><p>Once signed in, navigate to the "API &amp; Services" section from the main dashboard or menu.</p>
</li>
<li><p>If this is your first time, you may need to create a new project. Click on "Select a Project" or "Create a Project" if prompted.</p>
</li>
</ol>
<h3 id="heading-step-3-enable-the-gemini-api-or-relevant-api">Step 3: Enable the Gemini API (or relevant API)</h3>
<ol>
<li><p>In the "API &amp; Services" section, click on "Library."</p>
</li>
<li><p>Search for the "Gemini" API or any specific AI API you want to use.</p>
</li>
<li><p>Click on the API and then click "Enable" to activate it for your project.</p>
</li>
</ol>
<h3 id="heading-step-4-create-credentials-api-key">Step 4: Create Credentials (API Key)</h3>
<ol>
<li><p>After enabling the API, navigate to the "Credentials" tab on the left sidebar.</p>
</li>
<li><p>Click on "Create Credentials" and select "API Key" from the dropdown menu.</p>
</li>
<li><p>Google will generate an API key for you. You can copy this key to your clipboard.</p>
</li>
</ol>
<h3 id="heading-step-5-restrict-your-api-key-optional-but-recommended">Step 5: Restrict Your API Key (Optional but Recommended)</h3>
<ol>
<li><p>Click on "Edit" next to your API key to add restrictions.</p>
</li>
<li><p>You can restrict the key by IP addresses, referrer URLs, or by the specific API it can access.</p>
</li>
<li><p>Click "Save" to apply the restrictions.</p>
</li>
</ol>
<h3 id="heading-step-6-add-the-api-key-to-your-flutter-project">Step 6: Add the API Key to Your Flutter Project</h3>
<ol>
<li>Replace <code>'YOUR_API_KEY'</code> in your Flutter project's code with the API key you copied.</li>
</ol>
<h3 id="heading-step-7-use-the-api-key-in-your-application">Step 7: Use the API Key in Your Application</h3>
<ol>
<li>Now that you have the API key, you can use it in your Flutter app to make requests to the Gemini AI API or any other enabled API.</li>
</ol>
<h3 id="heading-important-notes">Important Notes:</h3>
<ul>
<li><p><strong>Keep Your API Key Secure:</strong> Never share your API key publicly or include it directly in client-side code without precautions. Use environment variables or other secure methods to store it.</p>
</li>
<li><p><strong>Monitor Usage:</strong> You can monitor your API usage and manage your API keys from the Google Cloud Console.</p>
</li>
</ul>
<hr />
<blockquote>
<p>Integrating AI into Flutter apps can significantly enhance user experience by providing intelligent features and responses. By following the steps outlined in this guide, you can successfully incorporate the Gemini AI API into your Flutter project. From setting up dependencies and creating a service to handle API requests, to designing a user-friendly interface for input and output.</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[How to Use Provider for State Management in Flutter || Part - 2]]></title><description><![CDATA[In Flutter, managing state efficiently is crucial for building responsive and dynamic applications. In the first part of this series, we explored the basics of using the Provider package for state management. In this second part, we will delve deeper...]]></description><link>https://blog.joflee.com/how-to-use-provider-for-state-management-in-flutter-part-2</link><guid isPermaLink="true">https://blog.joflee.com/how-to-use-provider-for-state-management-in-flutter-part-2</guid><category><![CDATA[#SkillDevelopment ]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[FutureBuilder]]></category><category><![CDATA[Provider]]></category><category><![CDATA[APIs]]></category><category><![CDATA[#valuenotifier]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Tue, 06 Aug 2024 03:50:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1720004328507/17bd1c8a-719a-42fc-b260-2d972503e285.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Flutter, managing state efficiently is crucial for building responsive and dynamic applications. In the first part of this series, we explored the basics of using the <strong>Provider</strong> package for state management. In this second part, we will delve deeper into <code>FutureProvider</code> and <code>StreamProvider</code>, two powerful tools that help handle asynchronous data in Flutter apps. By understanding how to use these providers, you can ensure your app remains responsive and provides a seamless user experience. If you missed the first part, you can check it out <a target="_blank" href="https://blog.joflee.com/how-to-use-provider-for-state-management-in-flutter-part-1"><strong>here</strong>.</a></p>
<h2 id="heading-what-is-futureprovider">What is FutureProvider?</h2>
<p>In Flutter, <code>FutureProvider</code> is a part of the <strong>Provider</strong> package, which helps manage state in your app. Let me explain what it does:</p>
<ol>
<li><p><strong>Purpose of</strong><code>FutureProvider</code>:</p>
<ul>
<li><p><code>FutureProvider</code> is used to provide a value that might not be ready when the widget tree is built. It ensures that a null value isn’t passed to any widgets.</p>
</li>
<li><p>The main use-case is to handle asynchronous operations (like fetching data from an API) and provide the result to widgets once the future is completed.</p>
</li>
</ul>
</li>
<li><p><strong>How it works:</strong></p>
<ul>
<li><p>You wrap your widget tree with a <code>FutureProvider</code>.</p>
</li>
<li><p>The <code>FutureProvider</code> takes a <code>Future</code> class (e.g., an API call) and updates the widgets depending on it when the future completes.</p>
</li>
<li><p>Until the future resolves, you can provide an initial value (e.g., a loading spinner) to avoid null errors.</p>
</li>
</ul>
</li>
<li><p><strong>Example:</strong></p>
<pre><code class="lang-dart"> FutureProvider&lt;<span class="hljs-built_in">String</span>&gt;(
   create: (_) =&gt; fetchDataFromApi(), <span class="hljs-comment">// Your async operation</span>
   initialData: <span class="hljs-string">'Loading...'</span>, <span class="hljs-comment">// Initial value</span>
   child: MyWidget(),
 )
</code></pre>
</li>
</ol>
<p>Remember, <code>FutureProvider</code> is particularly useful when dealing with asynchronous data in your Flutter app!</p>
<hr />
<h2 id="heading-how-to-use-futureprovider-in-a-flutter-app">How to use FutureProvider in a Flutter app?</h2>
<p>In Flutter, <code>FutureProvider</code> from the <strong>Provider</strong> package is used to handle asynchronous operations and provide the result to widgets once the future completes. Here’s how you can use it:</p>
<ol>
<li><p><strong>Exposing an Immutable Value:</strong></p>
<ul>
<li><p>If you have an “immutable value” that needs to be asynchronously loaded (e.g., a config file), use <code>FutureProvider</code> like this:</p>
<pre><code class="lang-dart">  FutureProvider&lt;MyConfig&gt;(
    builder: (_) <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> json = <span class="hljs-keyword">await</span> <span class="hljs-comment">// Load JSON from somewhere (e.g., an API);</span>
      <span class="hljs-keyword">return</span> MyConfig.fromJson(json);
    },
  )
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Combining with Mutations:</strong></p>
<ul>
<li><p>To convert a <code>Future</code> into something easier to manipulate, combine <code>FutureProvider</code> with a mutable object (like <code>ChangeNotifier</code>):</p>
<pre><code class="lang-dart">  ChangeNotifierProvider(
    builder: (_) =&gt; Foo(),
    child: Consumer&lt;Foo&gt;(
      builder: (_, foo, __) {
        <span class="hljs-keyword">return</span> FutureProvider.value(
          value: foo.someFuture,
          child: ... <span class="hljs-comment">// Your widget tree</span>
        );
      },
    ),
  )
</code></pre>
</li>
<li><p>This allows you to handle updates when the future completes.</p>
</li>
</ul>
</li>
</ol>
<p>Remember, <code>FutureProvider</code> is particularly useful for managing asynchronous data in your Flutter app!</p>
<hr />
<h2 id="heading-what-is-streamprovider"><strong>What is StreamProvider?</strong></h2>
<p>In Flutter, <strong>StreamProvider</strong> is a state management solution that leverages the power of the Dart Stream API and the simplicity of Flutter’s Provider package. Here’s what you need to know:</p>
<ol>
<li><p><strong>Purpose of StreamProvider:</strong></p>
<ul>
<li><p><strong>Exposing a Stream</strong>: StreamProvider is used to expose a stream and allows its descendants to access the latest value emitted by that stream.</p>
</li>
<li><p><strong>Listening to Changes</strong>: It listens to the stream and automatically updates the widgets when new data arrives.</p>
</li>
</ul>
</li>
<li><p><strong>How to Use StreamProvider:</strong></p>
<ul>
<li><p>Wrap your widget tree with StreamProvider:</p>
<pre><code class="lang-dart">  StreamProvider&lt;<span class="hljs-built_in">int</span>&gt;(
    create: (_) =&gt; myStream, <span class="hljs-comment">// Your stream (e.g., from Firestore, WebSocket, etc.)</span>
    initialData: <span class="hljs-number">0</span>, <span class="hljs-comment">// Initial value</span>
    child: MyWidget(),
  )
</code></pre>
</li>
<li><p>Replace <code>int</code> with the type of data your stream emits (e.g., <code>String</code>, <code>List&lt;User&gt;</code>, etc.).</p>
</li>
</ul>
</li>
<li><p><strong>Difference Between StreamProvider and StreamBuilder:</strong></p>
<ul>
<li><p><strong>StreamBuilder</strong>: Rebuilds itself every time the stream updates. It’s a built-in Flutter widget.</p>
</li>
<li><p><strong>StreamProvider</strong>: Combines <code>StreamBuilder</code> with <code>InheritedWidget</code>, allowing efficient data passing through the widget tree.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-how-to-use-streamprovider-in-a-flutter-app">How to use StreamProvider in a Flutter app?</h2>
<p>In your Flutter app, <strong>StreamProvider</strong> is a powerful way to handle asynchronous data using streams. Let’s dive into how you can use it:</p>
<ol>
<li><p><strong>Import the Necessary Packages:</strong> First, make sure you have the <code>provider</code> package added to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">dependencies:</span>
   <span class="hljs-attr">flutter:</span>
     <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
   <span class="hljs-attr">provider:</span> <span class="hljs-string">^6.1.2</span>
</code></pre>
</li>
<li><p><strong>Create a StreamProvider:</strong> Wrap your widget tree with <code>StreamProvider</code>. Specify the type of data your stream emits (e.g., <code>Stream&lt;int&gt;</code> or <code>Stream&lt;List&lt;User&gt;&gt;</code>):</p>
<pre><code class="lang-dart"> StreamProvider&lt;<span class="hljs-built_in">int</span>&gt;(
   create: (_) =&gt; myStream, <span class="hljs-comment">// Your stream (e.g., from Firestore, WebSocket, etc.)</span>
   initialData: <span class="hljs-number">0</span>, <span class="hljs-comment">// Initial value</span>
   child: MyWidget(),
 )
</code></pre>
</li>
<li><p><strong>Access the Stream in Your Widgets:</strong> Inside <code>MyWidget</code> or any descendant widget, use <code>context.watch</code> or <code>context.read</code> to access the stream:</p>
<pre><code class="lang-dart"> <span class="hljs-keyword">final</span> myValue = context.watch&lt;<span class="hljs-built_in">int</span>&gt;(); <span class="hljs-comment">// Access the stream value</span>
</code></pre>
</li>
<li><p><strong>Update UI When Stream Emits Data:</strong> Whenever your stream emits new data, the widgets wrapped by <code>StreamProvider</code> will automatically rebuild with the updated value.</p>
</li>
</ol>
<p>Remember, <code>StreamProvider</code> simplifies handling asynchronous data in your app!</p>
<hr />
<h2 id="heading-what-is-valuelistenablebuilder-in-flutter">What is ValueListenableBuilder in flutter?</h2>
<p>Certainly! The <code>ValueListenableBuilder</code> in Flutter is a handy widget that keeps its content in sync with a <code>ValueListenable</code>. Here’s how it works:</p>
<ol>
<li><p><strong>What is</strong><code>ValueListenableBuilder</code>?</p>
<ul>
<li><p><code>ValueListenableBuilder</code> is a widget that listens to changes in a <code>ValueListenable</code> and automatically rebuilds its content when the value changes.</p>
</li>
<li><p>You provide a <code>ValueListenable&lt;T&gt;</code> and a builder function that creates widgets based on the current value of the <code>ValueListenable</code>.</p>
</li>
<li><p>When the value changes, the builder function is called with the updated value, allowing you to update your UI accordingly.</p>
</li>
</ul>
</li>
<li><p><strong>Usage Example:</strong></p>
<pre><code class="lang-dart"> ValueListenableBuilder&lt;<span class="hljs-built_in">int</span>&gt;(
   valueListenable: myValueListenable, <span class="hljs-comment">// Your ValueListenable instance</span>
   builder: (BuildContext context, <span class="hljs-built_in">int</span> value, Widget? child) {
     <span class="hljs-comment">// Build your UI based on the current value</span>
     <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Count: <span class="hljs-subst">$value</span>'</span>);
   },
 )
</code></pre>
</li>
<li><p><strong>Performance Optimisation:</strong></p>
<ul>
<li><p>If your builder function contains a subtree that doesn’t depend on the <code>ValueListenable</code> value, you can pass a pre-built subtree as the <code>child</code> parameter.</p>
</li>
<li><p>This pre-built child is optional but can significantly improve performance in some cases.</p>
</li>
</ul>
</li>
<li><p><strong>Other Similar Widgets:</strong></p>
<ul>
<li><p><code>AnimatedBuilder</code>: Rebuilds based on a <code>Listenable</code> without passing back a specific value.</p>
</li>
<li><p><code>NotificationListener</code>: Rebuilds based on notifications from descendant widgets.</p>
</li>
<li><p><code>StreamBuilder</code>: For more advanced use cases involving streams.</p>
</li>
<li><p><code>TweenAnimationBuilder</code>: Animates values based on a <code>Tween</code>.</p>
</li>
</ul>
</li>
</ol>
<p>Remember to replace <code>myValueListenable</code> with your actual <code>ValueListenable</code> instance.</p>
<hr />
<h2 id="heading-how-to-create-your-own-valuenotifier-in-flutter">How to create your own ValueNotifier in Flutter?</h2>
<p>Certainly! Creating your own <code>ValueNotifier</code> in Flutter is straightforward. It’s a simple class that extends <code>ChangeNotifier</code> and provides a way to store a single value that can be listened to and updated reactively. Let’s walk through an example:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ValueNotifier</span>&lt;<span class="hljs-title">int</span>&gt; </span>{
  CounterModel(<span class="hljs-built_in">int</span> value) : <span class="hljs-keyword">super</span>(value);

  <span class="hljs-keyword">void</span> increment() =&gt; value++;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">final</span> counter = CounterModel(<span class="hljs-number">0</span>);

    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'ValueNotifier Demo'</span>,
      home: Scaffold(
        appBar: AppBar(
          title: Text(<span class="hljs-string">'ValueNotifier Demo'</span>),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: &lt;Widget&gt;[
              Text(<span class="hljs-string">'You have pushed the button this many times:'</span>),
              ValueListenableBuilder(
                valueListenable: counter,
                builder: (BuildContext context, <span class="hljs-built_in">int</span> value, Widget? child) {
                  <span class="hljs-keyword">return</span> Text(
                    <span class="hljs-string">'<span class="hljs-subst">$value</span>'</span>,
                    style: Theme.of(context).textTheme.headline4,
                  );
                },
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            counter.increment();
          },
          tooltip: <span class="hljs-string">'Increment'</span>,
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}
</code></pre>
<p>In this example:</p>
<ol>
<li><p>We define a <code>CounterModel</code> class that extends <code>ValueNotifier&lt;int&gt;</code>.</p>
</li>
<li><p>The <code>increment()</code> method updates the counter value.</p>
</li>
<li><p>We create an instance of <code>CounterModel</code> and pass it to <code>ValueListenableBuilder</code>, which listens to changes in the counter value and rebuilds the widget tree when the value changes.</p>
</li>
</ol>
<p>Using <code>ValueNotifier</code> is that simple! It's a great choice for small to medium-sized projects where you don't need the complexity of other state management solution. keep in mind that for larger or more complex projects, other state management solutions like Riverpod, Provider, Bloc, or Redux might be more suitable.</p>
<hr />
<blockquote>
<p>In this article, we explored <code>FutureProvider</code> and <code>StreamProvider</code> in Flutter. These tools help manage asynchronous data, ensuring your app remains responsive and dynamic. By using these providers, you can efficiently handle state management in your Flutter applications.</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[How to Use Provider for State Management in Flutter || Part - 1]]></title><description><![CDATA[Provider is one of the recommended state management options when using Flutter. It simplifies data flow within your app, making it more manageable and scalable. Here’s a brief overview:

What is Provider?

Provider is a package in Flutter that allows...]]></description><link>https://blog.joflee.com/how-to-use-provider-for-state-management-in-flutter-part-1</link><guid isPermaLink="true">https://blog.joflee.com/how-to-use-provider-for-state-management-in-flutter-part-1</guid><category><![CDATA[Provider]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[Flutter Examples]]></category><category><![CDATA[Flutter State Management]]></category><category><![CDATA[package]]></category><category><![CDATA[work]]></category><category><![CDATA[theme development]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Tue, 23 Jul 2024 04:33:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719923935684/72619cd6-44e6-4c49-b722-52a9b33e3ec2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Provider</strong> is one of the recommended state management options when using Flutter. It simplifies data flow within your app, making it more manageable and scalable. Here’s a brief overview:</p>
<ol>
<li><p><strong>What is Provider?</strong></p>
<ul>
<li><p>Provider is a package in Flutter that allows you to manage application state efficiently.</p>
</li>
<li><p>It follows the provider design pattern, enabling components to consume data without needing to know the source of that data.</p>
</li>
<li><p>This decoupling makes it easier to refactor, test, and maintain your code.</p>
</li>
</ul>
</li>
<li><p><strong>How does it work?</strong></p>
<ul>
<li><p>When you place a <code>Provider</code> widget in your widget tree, all its children have access to the values exposed by it.</p>
</li>
<li><p>There are different types of providers, but for this explanation, let’s focus on <code>ChangeNotifierProvider</code>.</p>
</li>
<li><p><code>ChangeNotifierProvider</code> allows you to listen to changes in the provider and automatically rebuilds widgets when needed.</p>
</li>
<li><p>You can create your own custom providers by extending <code>ChangeNotifier</code> you can create your own custom providers by extending <code>ChangeNotifier</code> or other provider classes.</p>
</li>
</ul>
</li>
<li><p><strong>Example: Creating a ThemeProvider</strong></p>
<ul>
<li><p>Suppose you want to change the main colour of your app dynamically.</p>
</li>
<li><p>You can create a <code>ThemeProvider</code> class that extends <code>ChangeNotifier</code>.</p>
</li>
<li><p>It exposes a <code>mainColor</code> value and a <code>changeThemeColor</code> function to update it.</p>
</li>
<li><p>When the <code>mainColor</code> changes, the class notifies its listeners using <code>notifyListeners()</code> .</p>
</li>
</ul>
</li>
<li><p><strong>Usage: Wrapping the Widget Tree</strong></p>
<ul>
<li><p>In your app, wrap the root widget (usually <code>MaterialApp</code>) with a <code>ChangeNotifierProvider&lt;ThemeProvider&gt;</code>.</p>
</li>
<li><p>Use a <code>Consumer&lt;ThemeProvider&gt;</code> widget to access the value of <code>ThemeProvider</code> within your widgets.</p>
</li>
<li><p>For example:</p>
<pre><code class="lang-dart">  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
    <span class="hljs-meta">@override</span>
    Widget build(BuildContext context) {
      <span class="hljs-keyword">return</span> ChangeNotifierProvider&lt;ThemeProvider&gt;(
        create: (context) =&gt; ThemeProvider(),
        child: Consumer&lt;ThemeProvider&gt;(
          builder: (context, themeProvider, child) =&gt; MaterialApp(
            <span class="hljs-comment">// Your app content here</span>
          ),
        ),
      );
    }
  }
</code></pre>
</li>
<li><p>Now any child widget can access and update the main color using <code>ThemeProvider.mainColor</code> .</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-how-to-use-provider-in-a-flutter-project">How to Use provider in a Flutter Project</h2>
<p><strong>Provider</strong> is a powerful state management solution for Flutter. Let’s walk through the steps to use it in your project:</p>
<ol>
<li><p><strong>Add the Provider Package</strong>:</p>
<ul>
<li><p>Open your <code>pubspec.yaml</code> file and add the <code>provider</code> dependency:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">dependencies:</span>
    <span class="hljs-attr">flutter:</span>
      <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
    <span class="hljs-attr">provider:</span> <span class="hljs-string">^6.1.2</span>
</code></pre>
</li>
<li><p>Run <code>flutter pub get</code> to fetch the package.</p>
</li>
</ul>
</li>
<li><p><strong>Create a Provider Class</strong>:</p>
<ul>
<li><p>Create a new Dart file (e.g., <code>data.dart</code>) to store your data.</p>
</li>
<li><p>Define a class (e.g., <code>Counter</code>) that extends <code>ChangeNotifier</code>.</p>
</li>
<li><p>Inside this class, declare your state variables (e.g., <code>_count</code>).</p>
</li>
</ul>
</li>
<li><p><strong>Expose State with the Provider</strong>:</p>
<ul>
<li><p>In your <code>Counter</code> class, provide methods to modify the state (e.g., incrementing the count).</p>
</li>
<li><p>Use <code>notifyListeners()</code> to notify listeners when the state changes.</p>
</li>
</ul>
</li>
<li><p><strong>Wrap Your App with the Provider</strong>:</p>
<ul>
<li><p>In your <code>main.dart</code>, wrap your app with a <code>ChangeNotifierProvider</code>:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">void</span> main() {
    runApp(
      ChangeNotifierProvider(
        create: (context) =&gt; Counter(),
        child: MyApp(),
      ),
    );
  }
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Access State in Widgets</strong>:</p>
<ul>
<li><p>Use <code>Consumer&lt;Counter&gt;</code> to access the state within your widgets:</p>
<pre><code class="lang-dart">  Consumer&lt;Counter&gt;(
    builder: (context, counter, child) {
      <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Count: <span class="hljs-subst">${counter.count}</span>'</span>);
    },
  )
</code></pre>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-changenotifierprovider-in-flutter"><code>ChangeNotifierProvider</code> in Flutter:</h2>
<p><strong>ChangeNotifierProvider</strong> is a class from the <strong>provider</strong> package in Flutter that links the lifecycle of a <code>ChangeNotifier</code> to widgets in the widget tree. Let’s break it down:</p>
<ol>
<li><p><strong>What is a</strong><code>ChangeNotifier</code>?</p>
<ul>
<li><p>A <code>ChangeNotifier</code> is a simple class in the Flutter SDK that provides change notification to its listeners.</p>
</li>
<li><p>When something is a <code>ChangeNotifier</code>, you can subscribe to its changes. It acts as an observable, notifying listeners when its internal state changes.</p>
</li>
</ul>
</li>
<li><p><strong>How does</strong><code>ChangeNotifierProvider</code> work?</p>
<ul>
<li><p><code>ChangeNotifierProvider</code> is responsible for creating and supplying an instance of a <code>ChangeNotifier</code> directly to the UI.</p>
</li>
<li><p>It ensures that when the <code>ChangeNotifier</code> emits a signal(due to changes).,dependent widgets are notified and can rebuild accordingly.</p>
</li>
</ul>
</li>
<li><p><strong>Difference between</strong><code>ChangeNotifierProvider</code> and <code>Provider</code>:</p>
<ul>
<li><p>The <code>Provider</code> class exposes a value to all widgets but doesn’t listen to changes from that value.</p>
</li>
<li><p>In contrast, <code>ChangeNotifierProvider</code> listens to changes from the <code>ChangeNotifier</code> and provides the updated values to dependent widgets.</p>
</li>
</ul>
</li>
</ol>
<p>So, if you want to manage state using a <code>ChangeNotifier</code>, use <code>ChangeNotifierProvider</code> to integrate it seamlessly into your Flutter app!</p>
<hr />
<h2 id="heading-multiprovider-in-flutter"><code>MultiProvider</code> in Flutter</h2>
<p>In Flutter, <code>MultiProvider</code> is a powerful widget provided by the <code>provider</code> package. It allows you to combine multiple providers into a single widget tree. Here’s how it works:</p>
<ol>
<li><p><strong>Combining Multiple Providers:</strong></p>
<ul>
<li><p>Suppose you have different data models (e.g., <code>ChatProvider</code>, <code>MessageProvider</code>) that you want to use across your app.</p>
</li>
<li><p>You can wrap your widget tree with a <code>MultiProvider</code> and provide a list of providers. Each provider corresponds to a specific data model.</p>
</li>
<li><p>For example:</p>
<pre><code class="lang-dart">  MultiProvider(
    providers: [
      ChangeNotifierProvider&lt;ChatProvider&gt;(
        create: (context) =&gt; ChatProvider(),
      ),
      ChangeNotifierProvider&lt;MessageProvider&gt;(
        create: (context) =&gt; MessageProvider(),
      ),
      <span class="hljs-comment">// Add more providers as needed</span>
    ],
    child: YourApp(),
  )
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Accessing Providers:</strong></p>
<ul>
<li><p>Once you’ve set up the <code>MultiProvider</code>, you can access the provided data models using <code>Provider.of&lt;T&gt;(context)</code>.</p>
</li>
<li><p>For instance, in your widget, you can use:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">final</span> chatProvider = Provider.of&lt;ChatProvider&gt;(context);
  <span class="hljs-keyword">final</span> messageProvider = Provider.of&lt;MessageProvider&gt;(context);
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Context Hierarchy:</strong></p>
<ul>
<li><p>The context hierarchy matters. Providers should be above the widgets that need access to them.</p>
</li>
<li><p>If you want to use providers across different routes, place the <code>MultiProvider</code> above your <code>MaterialApp</code>.</p>
</li>
<li><p>However, having multiple <code>MaterialApp</code> widgets is not recommended. Instead, consider passing the providers as values to new routes without nesting additional <code>MaterialApp</code> widgets.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-best-practices-for-organising-provider-setup">Best Practices for organising provider setup:</h2>
<p>Certainly! Organising your <strong>Provider</strong> setup effectively is crucial for maintaining a clean and scalable Flutter app. here are some best practices:</p>
<ol>
<li><p><strong>Separate Concerns:</strong></p>
<ul>
<li><p>Divide your code into logical layers: UI, business logic, and data.</p>
</li>
<li><p>Place UI-related widget in separate files(e.g.,<code>screens</code>, <code>widgets</code> folders).</p>
</li>
<li><p>Keep business logic (providers)in their own files(e.g.,<code>providers</code> folder)</p>
</li>
</ul>
</li>
<li><p><strong>Provider Classes:</strong></p>
<ul>
<li><p>Create separate provider classes for different data models or features.</p>
</li>
<li><p>For example, if you have tasks and user authentication, create <code>TaskProvider</code> and <code>AuthProvider</code>.</p>
</li>
</ul>
</li>
<li><p><strong>Nested Providers</strong>:</p>
<ul>
<li><p>Use nested providers when one provider depends on another.</p>
</li>
<li><p>For instance, if a <code>TaskList</code> widget needs access to both tasks and user data, nest <code>TaskProvider</code> and <code>AuthProvider</code> .</p>
</li>
</ul>
</li>
<li><p><strong>Provider Tree:</strong></p>
<ul>
<li><p>Visualise your provider tree. Understand which widgets depend on which providers.</p>
</li>
<li><p>Avoid unnecessary providers at the root level. Only expose what's needed.</p>
</li>
</ul>
</li>
<li><p><strong>Scoped Providers:</strong></p>
<ul>
<li><p>Use <code>Provider.of</code> or <code>Consumer</code> for specific widgets that need access to a provider.</p>
</li>
<li><p>Avoid using <code>Provider.of</code> at the root level unless it's truly global state.</p>
</li>
</ul>
</li>
<li><p><strong>Provider Architecture:</strong></p>
<ul>
<li><p>Consider using a layered architecture(e.g., Clean Architecture)with providers.</p>
</li>
<li><p>Separate data sources (API, local storage) from business logic providers.</p>
</li>
</ul>
</li>
<li><p><strong>Value vs. ChangeNotifier:</strong></p>
<ul>
<li><p>Use <code>ChangeNotifierProvider</code> for mutable state(e.g.,tasks, user preferences).</p>
</li>
<li><p>Use <code>Provider.value</code> for immutable data (e.g., constants, configuration).</p>
</li>
</ul>
</li>
<li><p><strong>Dispose Resources:</strong></p>
<ul>
<li><p>Clean up resources(e.g.,close streams, cancel subscriptions) in your provider's <code>dispose</code> method.</p>
</li>
<li><p>Avoid memory leaks.</p>
</li>
</ul>
</li>
<li><p><strong>Testing</strong>:</p>
<ul>
<li><p>Write unit tests for your providers.</p>
</li>
<li><p>Use <code>ProviderScope</code> in your tests to isolate providers.</p>
</li>
</ul>
</li>
<li><p><strong>Localisation and Themes</strong>:</p>
<ul>
<li>Consider using providers for localisation (language) and themes (dark mode, light mode).</li>
</ul>
</li>
</ol>
<hr />
<blockquote>
<p>Using the <strong>Provider</strong> package for state management in Flutter offers a scalable solution for managing application state. By leveraging <code>ChangeNotifierProvider</code> and other provider classes, developers can efficiently handle state changes and ensure their apps remain maintainable and testable. Organising your provider setup with best practices, such as separating concerns and using nested providers, enhances the structure and readability of your code. Whether managing simple state or complex data models, <strong>Provider</strong> simplifies the process, making it an essential tool for any Flutter developer.</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[Guide to State Management in Flutter with GetX || Part - 2]]></title><description><![CDATA[In our previous article, we explored the basics of GetX and its core features. We discussed the introduction to GetX, demonstrated how to work with reactive state variables, explored dependency injection using GetX, and highlighted the advantages of ...]]></description><link>https://blog.joflee.com/guide-to-state-management-in-flutter-with-getx-part-2</link><guid isPermaLink="true">https://blog.joflee.com/guide-to-state-management-in-flutter-with-getx-part-2</guid><category><![CDATA[JoFlee]]></category><category><![CDATA[GetX]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[explore]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Wed, 03 Jul 2024 04:11:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719394429785/7fdde3b3-0181-42e4-9afb-f2eeb31607ff.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In our previous article, we explored the basics of GetX and its core features. We discussed the introduction to GetX, demonstrated how to work with reactive state variables, explored dependency injection using GetX, and highlighted the advantages of using GetX over other state management libraries. If you missed any of these topics, please revisit the first part to build a solid foundation. Now, let’s take our understanding of GetX to the next level. In this article, we’ll delve deeper into advanced techniques, uncover hidden gems, and empower you to create robust and efficient Flutter applications using GetX.</p>
<h2 id="heading-syntax-for-defining-named-routes-using-getx">Syntax for Defining Named Routes Using GetX</h2>
<ol>
<li><p>First, make sure you’ve set up GetX in your app:</p>
<ul>
<li><p>Install the <code>get</code> package by running:</p>
<pre><code class="lang-plaintext">  flutter pub add get
</code></pre>
</li>
<li><p>Import the library into your Dart code:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get/get.dart'</span>;
</code></pre>
</li>
</ul>
</li>
<li><p>Replace <code>MaterialApp</code> with <code>GetMaterialApp</code> in your app’s main widget tree:</p>
<pre><code class="lang-dart"> GetMaterialApp(
   <span class="hljs-comment">// Other properties...</span>
   getPages: [
     GetPage(name: <span class="hljs-string">'/page-one'</span>, page: () =&gt; PageOne()),
     GetPage(name: <span class="hljs-string">'/page-two'</span>, page: () =&gt; PageTwo()),
     <span class="hljs-comment">// Add more named routes as you needed</span>
   ],
 )
</code></pre>
</li>
<li><p>Define your named routes using <code>GetPage</code>:</p>
<ul>
<li><p>The <code>name</code> parameter specifies the route name (e.g., ‘/page-one’).</p>
</li>
<li><p>The <code>page</code> parameter is a callback that returns the widget for that route.</p>
</li>
</ul>
</li>
</ol>
<p>That’s it! You can now navigate to these named routes using <code>Get.toNamed('/page-one')</code> , <code>Get.toNamed('/page-two')</code> and so on.</p>
<hr />
<h2 id="heading-what-is-internationalisation-in-flutter-using-getx">What is internationalisation in Flutter using GetX?</h2>
<p>Internationalisation (often abbreviated as i18n) in Flutter refers to the process of adapting your app to support multiple languages and locales. It allows you to create a single codebase that can display content in different languages based on the user’s preferences. In the Context of GetX, a popular state management library for Flutter, you can achieve internationalisation efficiently using, the <code>Getx_Translator</code> package.</p>
<p>Here are the steps to set up internationalisation using <code>GetX_Translator</code>:</p>
<ol>
<li><p><strong>Setting Up the Google Sheet for GetX_Translator</strong>:</p>
<ul>
<li><p>Create a Google Sheet to serve as a centralised hub for all your application’s translations.</p>
</li>
<li><p>Define the structure of the sheet with headers for keys (used in your Flutter app) and columns for different languages.</p>
</li>
<li><p>Populate the sheet with localisation data, linking keys to their respective translations.</p>
</li>
</ul>
</li>
<li><p><strong>Leveraging Google App Script for Communication</strong>:</p>
<ul>
<li><p>Use Google App Script to establish communication between the Google Sheet and your Flutter app.</p>
</li>
<li><p>Create a script for your sheet and paste the provided code in the script editor.</p>
</li>
<li><p>This script handles HTTP requests and manages the sheet’s data.</p>
</li>
</ul>
</li>
</ol>
<p>With this setup, managing translations becomes efficient and collaborative. You'll be able to dynamically switch between languages in your Flutter app based on user preferences.</p>
<hr />
<h2 id="heading-some-best-practices-for-internationalisation-in-flutter">Some best practices for internationalisation in Flutter:</h2>
<ol>
<li><p><strong>Use</strong> <code>intl</code> Package:</p>
<ul>
<li><p>The <code>intl</code> package provides essential tools for formatting dates, numbers, and messages in different languages.</p>
</li>
<li><p>Leverage <code>intl</code> for date and number formatting, pluralisation, and message interpolation.</p>
</li>
<li><p>It supports various locales, so you can adapt your app to different regions seamlessly.</p>
</li>
</ul>
</li>
<li><p><strong>Separate Texts from UI Widgets</strong>:</p>
<ul>
<li><p>Avoid hardcoding strings directly into UI widgets (e.g., <code>Text('Hello')</code>).</p>
</li>
<li><p>Instead, define your strings in a separate file (e.g., <code>strings.dart</code>) and reference them using keys.</p>
</li>
<li><p>This makes it easier to replace translations and maintain consistency.</p>
</li>
</ul>
</li>
<li><p><strong>Choose a Localisation Strategy</strong>:</p>
<ul>
<li><p>Decide whether to use resource files (<code>.arb</code> or <code>.json</code>) or a centralised solution (like Google Sheets with <code>GetX_Translator</code>).</p>
</li>
<li><p>Resource files are straightforward but can become unwieldy for large apps.</p>
</li>
<li><p>Centralised solutions allow collaboration and dynamic updates but require additional setup.</p>
</li>
</ul>
</li>
<li><p><strong>Test with Different Locales</strong>:</p>
<ul>
<li><p>Regularly test your app with different locales to ensure text fits within UI elements.</p>
</li>
<li><p>Pay attention to text expansion (some languages require more space) and right-to-left (RTL) layouts.</p>
</li>
</ul>
</li>
<li><p><strong>Provide Context for Translators</strong>:</p>
<ul>
<li><p>When adding keys to your localisation files, include context information.</p>
</li>
<li><p>For example, instead of just <code>"Save"</code>, use <code>"buttonSave"</code> or <code>"actionSave"</code> to give translators context.</p>
</li>
</ul>
</li>
<li><p><strong>Handle Plurals and Gender Variations</strong>:</p>
<ul>
<li><p>Different languages have varying rules for plurals and gender.</p>
</li>
<li><p>Use the <code>intl</code> package’s <code>Intl.plural</code> and <code>Intl.gender</code> methods to handle these variations.</p>
</li>
</ul>
</li>
<li><p><strong>Localise Dates and Times</strong>:</p>
<ul>
<li><p>Use <code>DateFormat</code> from the <code>intl</code> package to format dates and times according to the user’s locale.</p>
</li>
<li><p>Be aware of different date formats (e.g., day-month-year vs. month-day-year).</p>
</li>
</ul>
</li>
</ol>
<p>Remember that internationalisation is an ongoing process. Regularly update your translations, test thoroughly, and consider user feedback to improve the localisation experience.</p>
<hr />
<h2 id="heading-how-to-change-theme-using-getx-in-flutter">How to Change theme using GetX in Flutter</h2>
<ol>
<li><p><strong>Create a Theme Controller</strong>:</p>
<ul>
<li><p>First, create a controller class (let’s call it <code>ThemeController</code>) that manages the theme state.</p>
</li>
<li><p>In this controller, define a boolean property (e.g., <code>isDarkMode</code>) to track whether the app is in dark mode or light mode.</p>
</li>
</ul>
</li>
<li><p><strong>Toggle the Theme</strong>:</p>
<ul>
<li><p>Implement a method in <code>ThemeController</code> (e.g., <code>toggleDarkMode()</code>) that toggles the <code>isDarkMode</code> property.</p>
</li>
<li><p>Depending on the value of <code>isDarkMode</code>, switch between your light and dark theme data.</p>
</li>
</ul>
</li>
<li><p><strong>Use GetX to Change the Theme</strong>:</p>
<ul>
<li><p>In your UI (e.g., <code>HomeScreen</code>), use <code>GetBuilder&lt;ThemeController&gt;</code> to listen for changes in the theme state.</p>
</li>
<li><p>Inside the <code>IconButton</code> (for toggling the theme), update the icon based on <code>isDarkMode</code>.</p>
</li>
<li><p>When the user taps the icon, call <code>controller.toggleDarkMode()</code> to switch the theme.</p>
</li>
</ul>
</li>
<li><p><strong>Persist the Theme State</strong>:</p>
<ul>
<li><p>To make the theme persist across app restarts, use the <code>get_storage</code> package.</p>
</li>
<li><p>Create a helper class (e.g., <code>ThemeHelper</code>) that reads and writes the theme mode to local storage.</p>
</li>
<li><p>Save the theme mode when the user toggles it, and load it when the app starts.</p>
</li>
</ul>
</li>
</ol>
<p>Here’s a simplified example of how you can achieve this:</p>
<pre><code class="lang-dart"><span class="hljs-comment">// ThemeController.dart</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThemeController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">GetxController</span> </span>{
  <span class="hljs-built_in">bool</span> isDarkMode = <span class="hljs-keyword">false</span>;

  <span class="hljs-keyword">void</span> toggleDarkMode() {
    isDarkMode = !isDarkMode;
    <span class="hljs-keyword">if</span> (isDarkMode) {
      Get.changeTheme(themeDataDark);
    } <span class="hljs-keyword">else</span> {
      Get.changeTheme(themeDataLight);
    }
    update();
  }
}
<span class="hljs-comment">// main.dart</span>
<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">await</span> GetStorage.init();
  runApp(MyApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> GetMaterialApp(
      theme: themeDataLight,
      darkTheme: themeDataDark,
      themeMode: ThemeHelper().theme,
      home: HomeScreen(),
    );
  }
}

<span class="hljs-comment">// ThemeHelper.dart</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThemeHelper</span> </span>{
  <span class="hljs-keyword">final</span> _box = GetStorage();
  <span class="hljs-keyword">final</span> _key = <span class="hljs-string">'isDarkMode'</span>;

  ThemeMode <span class="hljs-keyword">get</span> theme =&gt; _loadThemeFromBox() ? ThemeMode.dark : ThemeMode.light;

  <span class="hljs-built_in">bool</span> _loadThemeFromBox() =&gt; _box.read(_key) ?? <span class="hljs-keyword">false</span>;

  <span class="hljs-keyword">void</span> switchTheme() {
    Get.changeThemeMode(_loadThemeFromBox() ? ThemeMode.light : ThemeMode.dark);
    _saveThemeToBox(!_loadThemeFromBox());
  }

  <span class="hljs-keyword">void</span> _saveThemeToBox(<span class="hljs-built_in">bool</span> isDarkMode) =&gt; _box.write(_key, isDarkMode);
}
</code></pre>
<p>Remember to adjust the theme data (<code>themeDataLight</code> and <code>themeDataDark</code>) according to your need for app’s design.</p>
<hr />
<h2 id="heading-validation-in-flutter-using-getx">Validation in Flutter using GetX</h2>
<p><strong>Validation in Flutter using GetX</strong> involves handling form input validation and displaying appropriate error messages. Let’s break it down:</p>
<ol>
<li><p><strong>Create a Form Controller</strong>:</p>
<ul>
<li><p>Start by creating a controller (e.g., <code>FormController</code>) that manages form-related state.</p>
</li>
<li><p>Define observables for form fields (e.g., <code>RxString</code> for username, email, etc.).</p>
</li>
<li><p>Use <code>debounce</code> to validate input after a delay (e.g., 500 milliseconds) to avoid excessive validation calls.</p>
</li>
</ul>
</li>
<li><p><strong>Implement Validation Logic</strong>:</p>
<ul>
<li><p>In the <code>validations</code> method, perform your validation checks (e.g., length, availability, etc.).</p>
</li>
<li><p>Update the <code>errorText</code> observable with validation errors or set it to <code>null</code> if validation passes.</p>
</li>
<li><p>Disable the submit button (<code>submitFunc</code>) during validation.</p>
</li>
</ul>
</li>
<li><p><strong>UI Integration</strong>:</p>
<ul>
<li><p>In your UI (e.g., <code>FormObxPage</code>), use <code>Obx</code> to listen for changes in observables.</p>
</li>
<li><p>Bind the <code>onChanged</code> callback of <code>TextFormField</code> to the corresponding controller function (e.g., <code>usernameChanged</code>).</p>
</li>
<li><p>Enable/disable the submit button based on the <code>submitFunc</code> observable.</p>
</li>
</ul>
</li>
</ol>
<p>Here’s a simplified example:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FormController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">GetxController</span> </span>{
  RxString username = RxString(<span class="hljs-string">''</span>);
  RxnString errorText = RxnString(<span class="hljs-keyword">null</span>);
  Rxn&lt;<span class="hljs-built_in">Function</span>()&gt; submitFunc = Rxn&lt;<span class="hljs-built_in">Function</span>()&gt;(<span class="hljs-keyword">null</span>);

  <span class="hljs-keyword">void</span> usernameChanged(<span class="hljs-built_in">String</span> val) {
    username.value = val;
  }

  <span class="hljs-keyword">void</span> validations(<span class="hljs-built_in">String</span> val) <span class="hljs-keyword">async</span> {
    errorText.value = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// Reset validation errors</span>
    submitFunc.value = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// Disable submit while validating</span>

    <span class="hljs-keyword">if</span> (val.isNotEmpty) {
      <span class="hljs-keyword">if</span> (lengthOK(val) &amp;&amp; <span class="hljs-keyword">await</span> available(val)) {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'All validations passed, now enable submit button...'</span>);
        submitFunc.value = submitFunction();
        errorText.value = <span class="hljs-keyword">null</span>;
      } <span class="hljs-keyword">else</span> {
        errorText.value = <span class="hljs-string">'please enter valid input'</span>; <span class="hljs-comment">// Set appropriate error message</span>
      }
    }
  }
  <span class="hljs-comment">// Other validation methods (lengthOK, available, etc.) go here</span>
}

<span class="hljs-comment">// UI Example</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FormObxPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    FormController fx = Get.put(FormController());

    <span class="hljs-keyword">return</span> Scaffold(
      body: Column(
        children: [
          TextFormField(
            onChanged: fx.usernameChanged,
            decoration: InputDecoration(
              labelText: <span class="hljs-string">'Username'</span>,
              errorText: fx.errorText.value,
            ),
          ),
          ElevatedButton(
            child: Text(<span class="hljs-string">'Submit'</span>),
            onPressed: fx.submitFunc.value,
          ),
        ],
      ),
    );
  }
}
</code></pre>
<blockquote>
<p>In conclusion, mastering state management in Flutter using GetX can significantly enhance the efficiency and robustness of your applications. By understanding advanced techniques such as defining named routes, implementing internationalisation, changing themes, and handling validation, you can create a seamless and dynamic user experience. Remember to follow best practices for internationalisation and persist theme states to ensure a consistent and user-friendly interface.</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[Guide to State Management in Flutter with GetX || Part - 1]]></title><description><![CDATA[Let's dive into GetX, a lightweight and powerful state management library for Flutter. Designed for simplicity and efficiency, GetX helps manage app state and provides real-time updates to the user interface. This guide will walk you through using Ge...]]></description><link>https://blog.joflee.com/guide-to-state-management-in-flutter-with-getx-part-1</link><guid isPermaLink="true">https://blog.joflee.com/guide-to-state-management-in-flutter-with-getx-part-1</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[GetX]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[Libraries]]></category><category><![CDATA[routing]]></category><category><![CDATA[navigation]]></category><category><![CDATA[Validation]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Thu, 27 Jun 2024 03:30:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719394470628/53263283-a5be-4179-abfc-7aefab231777.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's dive into <strong>GetX</strong>, a lightweight and powerful state management library for Flutter. Designed for simplicity and efficiency, GetX helps manage app state and provides real-time updates to the user interface. This guide will walk you through using GetX for state management, making your Flutter development experience smoother and more productive.</p>
<ol>
<li><p><strong>What is GetX?</strong></p>
<ul>
<li><p>GetX is not just a state management library; it’s a micro-framework that combines route management and dependency injection.</p>
</li>
<li><p>It aims to deliver an excellent development experience with minimal overhead, making it an ideal choice for Flutter apps.</p>
</li>
<li><p>Three pillars of GetX:</p>
<ul>
<li><p><strong>Performance</strong>: Minimal memory and resource consumption.</p>
</li>
<li><p><strong>Productivity</strong>: Intuitive syntax and straightforward tools save development time.</p>
</li>
<li><p><strong>Organisation</strong>: Decouples business logic from view and presentation logic without needing context for navigation or stateful widgets.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>State Management with GetX</strong>:</p>
<ul>
<li><p>GetX allows you to manage states efficiently without context.</p>
</li>
<li><p>You can create reactive state variables using <code>Rx</code> classes (e.g., <code>RxInt</code>, <code>RxString</code>).</p>
</li>
<li><p>Example:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">final</span> count = <span class="hljs-number">0.</span>obs;
</code></pre>
</li>
<li><p>Update the state:</p>
<pre><code class="lang-dart">  count.value++;
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Routing with GetX</strong>:</p>
<ul>
<li><p>Define routes without context:</p>
<pre><code class="lang-dart">  Get.toNamed(<span class="hljs-string">'/details'</span>);
</code></pre>
</li>
<li><p>Use named routes in your app:</p>
<pre><code class="lang-dart">  GetMaterialApp(
    initialRoute: <span class="hljs-string">'/'</span>,
    getPages: [
      GetPage(name: <span class="hljs-string">'/'</span>, page: () =&gt; HomeScreen()),
      GetPage(name: <span class="hljs-string">'/details'</span>, page: () =&gt; DetailsScreen()),
    ],
  )
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Dependency Injection</strong>:</p>
<ul>
<li><p>GetX provides a simple way to inject dependencies using <code>Get.put</code> or <code>Get.lazyPut</code>.</p>
</li>
<li><p>Example:</p>
<pre><code class="lang-dart">  Get.put(ApiService());
</code></pre>
</li>
<li><p>Access the service anywhere in your app:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">final</span> apiService = Get.find&lt;ApiService&gt;();
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Other Features</strong>:</p>
<ul>
<li><p><strong>Snack bar and Dialogs</strong>: Easily show snack bar messages or dialogs.</p>
</li>
<li><p><strong>Internationalisation (i18n)</strong>: Localise your app with GetX.</p>
</li>
<li><p><strong>Bindings</strong>: Manage controller lifecycles.</p>
</li>
<li><p><strong>Reactive Workers</strong>: Execute background tasks reactively.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-example-of-using-reactive-state-variables-with-getx">Example of using Reactive state Variables with GetX:</h2>
<ol>
<li><p><strong>Setting Up GetX</strong>: First, make sure you’ve added the <code>get</code> package to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">dependencies:</span>
   <span class="hljs-attr">flutter:</span>
     <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
   <span class="hljs-attr">get:</span> <span class="hljs-string">^4.6.6</span>
</code></pre>
</li>
<li><p><strong>Create a Controller</strong>: Create a controller class that extends <code>GetxController</code>. This controller will manage our state:</p>
<pre><code class="lang-dart"> <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get/get.dart'</span>;

 <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">GetxController</span> </span>{
   <span class="hljs-keyword">final</span> count = <span class="hljs-number">0.</span>obs; <span class="hljs-comment">// Observable state variable</span>

   <span class="hljs-keyword">void</span> increment() {
     count.value++; <span class="hljs-comment">// Automatically triggers UI updates</span>
   }

   <span class="hljs-keyword">void</span> decrement() {
     count.value--;
   }
 }
</code></pre>
</li>
<li><p><strong>Use the Controller in Your Widget</strong>: In your widget, use the <code>GetBuilder</code> widget to listen to changes in the <code>count</code> variable:</p>
<pre><code class="lang-dart"> <span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
 <span class="hljs-keyword">import</span> <span class="hljs-string">'package:get/get.dart'</span>;

 <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
   <span class="hljs-keyword">final</span> CounterController _controller = Get.put(CounterController());

   <span class="hljs-meta">@override</span>
   Widget build(BuildContext context) {
     <span class="hljs-keyword">return</span> Scaffold(
       appBar: AppBar(title: Text(<span class="hljs-string">'Counter App'</span>)),
       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
             Text(<span class="hljs-string">'Count:'</span>, style: TextStyle(fontSize: <span class="hljs-number">24</span>)),
             GetBuilder&lt;CounterController&gt;(
               builder: (controller) =&gt; Text(
                 <span class="hljs-string">'<span class="hljs-subst">${controller.count}</span>'</span>, <span class="hljs-comment">// Display the Number</span>
                 style: TextStyle(fontSize: <span class="hljs-number">36</span>),
               ),
             ),
             SizedBox(height: <span class="hljs-number">16</span>),
             Row(
               mainAxisAlignment: MainAxisAlignment.center,
               children: [
                 ElevatedButton(
                   onPressed: _controller.increment,
                   child: Text(<span class="hljs-string">'Increment'</span>),
                 ),
                 SizedBox(width: <span class="hljs-number">16</span>),
                 ElevatedButton(
                   onPressed: _controller.decrement,
                   child: Text(<span class="hljs-string">'Decrement'</span>),
                 ),
               ],
             ),
           ],
         ),
       ),
     );
   }
 }
</code></pre>
</li>
<li><p><strong>Run Your App</strong>: Run your app, and you’ll see the counter value updating as you press the “Increment” and “Decrement” buttons.</p>
</li>
</ol>
<hr />
<h2 id="heading-learn-more-about-dependency-injection-with-getx">Learn more about dependency injection with GetX:</h2>
<p>Certainly! Let’s dive into <strong>dependency injection (DI)</strong> with GetX in Flutter. Dependency injection is a technique that allows you to manage and provide dependencies (such as services, repositories, or other objects) to different parts of your app without tightly coupling them. GetX makes DI straightforward and efficient.</p>
<ol>
<li><p><strong>Why Use Dependency Injection?</strong></p>
<ul>
<li><p><strong>Decoupling</strong>: DI helps decouple your app’s components, making them more modular and easier to maintain.</p>
</li>
<li><p><strong>Testability</strong>: By injecting dependencies, you can easily mock or replace them during testing.</p>
</li>
<li><p><strong>Reusability</strong>: Reusable services can be injected into multiple parts of your app.</p>
</li>
</ul>
</li>
<li><p><strong>How Does GetX Handle Dependency Injection?</strong></p>
<ul>
<li><p>GetX uses a simple and intuitive approach:</p>
<ul>
<li><p><code>Get.put&lt;T&gt;(T instance)</code>: Registers an instance of type <code>T</code> globally. You can access it anywhere using <code>Get.find&lt;T&gt;()</code>.</p>
</li>
<li><p><code>Get.lazyPut&lt;T&gt;(() =&gt; T())</code>: Lazily creates an instance of <code>T</code> when it is first accessed.</p>
</li>
<li><p><code>Get.delete&lt;T&gt;()</code>: Removes the instance of type <code>T</code>.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Example: Injecting an API Service</strong></p>
<p> Let’s say you have an <code>ApiService</code> class that handles network requests. Here’s how you’d use DI with GetX:</p>
<pre><code class="lang-dart"> <span class="hljs-comment">//1). Create your ApiService class</span>
 <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiService</span> </span>{
   Future&lt;<span class="hljs-built_in">String</span>&gt; fetchData() <span class="hljs-keyword">async</span> {
     <span class="hljs-comment">// Fetch data from an API</span>
     <span class="hljs-keyword">return</span> <span class="hljs-string">'Data from API'</span>;
   }
 }

 <span class="hljs-comment">// 2). Inject ApiService globally</span>
 <span class="hljs-keyword">final</span> apiService = Get.put(ApiService());

 <span class="hljs-comment">// 3). Use it in your widget</span>
 <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
   <span class="hljs-meta">@override</span>
   Widget build(BuildContext context) {
     <span class="hljs-keyword">return</span> Scaffold(
       appBar: AppBar(title: Text(<span class="hljs-string">'Dependency Injection'</span>)),
       body: Center(
         child: ElevatedButton(
           onPressed: () <span class="hljs-keyword">async</span> {
             <span class="hljs-keyword">final</span> data = <span class="hljs-keyword">await</span> apiService.fetchData();
             <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
           },
           child: Text(<span class="hljs-string">'Fetch Data'</span>),
         ),
       ),
     );
   }
 }
</code></pre>
</li>
<li><p><strong>Lazy Initialisation</strong> If you want to create the <code>ApiService</code> only when needed (lazy initialisation), use <code>Get.lazyPut</code>:</p>
<pre><code class="lang-dart"> <span class="hljs-keyword">final</span> apiService = Get.lazyPut(() =&gt; ApiService());
</code></pre>
</li>
<li><p><strong>Scoped Dependencies</strong> You can also create scoped dependencies for specific parts of your app (e.g., per screen or feature). Use <code>Get.create</code>:</p>
<pre><code class="lang-dart"> <span class="hljs-keyword">final</span> myScopedService = Get.create(() =&gt; MyScopedService());
</code></pre>
</li>
<li><p><strong>Clean Up</strong> Don’t forget to clean up when your widget or screen is disposed:</p>
<pre><code class="lang-dart"> <span class="hljs-meta">@override</span>
 <span class="hljs-keyword">void</span> dispose() {
   Get.delete&lt;ApiService&gt;();
   <span class="hljs-keyword">super</span>.dispose();
 }
</code></pre>
</li>
</ol>
<p>That’s the essence of dependency injection with GetX! It keeps your code organised, testable, and maintainable. Feel free to explore more features like reactive state management and routing with GetX.</p>
<hr />
<h2 id="heading-advantages-of-using-getx-over-other-state-management-libraries">Advantages of using GetX over other state management libraries:</h2>
<p>Certainly! Let’s explore the advantages of using <strong>GetX</strong> for state management in Flutter compared to other libraries:</p>
<ol>
<li><p><strong>Lightweight and Minimal Boilerplate</strong>:</p>
<ul>
<li><p>GetX is incredibly lightweight, which means less overhead in your app.</p>
</li>
<li><p>It requires minimal boilerplate code, making development faster and more efficient.</p>
</li>
</ul>
</li>
<li><p><strong>Reactive State Management</strong>:</p>
<ul>
<li><p>GetX provides reactive state management using <code>Rx</code> classes (e.g., <code>RxInt</code>, <code>RxString</code>).</p>
</li>
<li><p>Changes to state variables automatically trigger UI updates without using <code>setState</code>.</p>
</li>
</ul>
</li>
<li><p><strong>Dependency Injection (DI)</strong>:</p>
<ul>
<li><p>GetX integrates DI seamlessly. You can inject dependencies globally or lazily.</p>
</li>
<li><p>DI improves code organisation, testability, and reusability.</p>
</li>
</ul>
</li>
<li><p><strong>Routing and Navigation</strong>:</p>
<ul>
<li><p>GetX combines state management and routing.</p>
</li>
<li><p>Define named routes easily and navigate without context.</p>
</li>
</ul>
</li>
<li><p><strong>Performance Optimisation</strong>:</p>
<ul>
<li><p>GetX optimises memory usage and performance.</p>
</li>
<li><p>It’s designed to be efficient even in large apps.</p>
</li>
</ul>
</li>
<li><p><strong>Additional Features</strong>:</p>
<ul>
<li><p><strong>Snackbar and Dialogs</strong>: Show snackbar messages or dialogs effortlessly.</p>
</li>
<li><p><strong>Internationalisation (i18n)</strong>: Localise your app with GetX.</p>
</li>
<li><p><strong>Bindings</strong>: Manage controller lifecycles.</p>
</li>
<li><p><strong>Reactive Workers</strong>: Execute background tasks reactively.</p>
</li>
</ul>
</li>
<li><p><strong>Community and Documentation</strong>:</p>
<ul>
<li><p>GetX has an active community and excellent documentation.</p>
</li>
<li><p>You’ll find tutorials, examples, and support readily available.</p>
</li>
</ul>
</li>
</ol>
<blockquote>
<p>Now, as we conclude this segment, stay tuned for the second part! We’ll dive deeper into advanced techniques, explore hidden gems, and empower you to create robust and efficient Flutter applications using GetX. Get ready for an exciting continuation! 🚀📝</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[WebSockets in Flutter: A Beginner's Guide to Real-Time Connectivity]]></title><description><![CDATA[In the world of mobile app development, providing real-time data updates and seamless communication between the client and server is crucial. Flutter, a popular framework for building cross-platform applications, offers a powerful solution for this t...]]></description><link>https://blog.joflee.com/websockets-in-flutter-a-beginners-guide-to-real-time-connectivity</link><guid isPermaLink="true">https://blog.joflee.com/websockets-in-flutter-a-beginners-guide-to-real-time-connectivity</guid><category><![CDATA[websockets]]></category><category><![CDATA[communication]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[RealTimeCommunication]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Mon, 17 Jun 2024 03:54:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718445801111/78f86789-b0d9-46ed-b34b-967d5c213628.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of mobile app development, providing real-time data updates and seamless communication between the client and server is crucial. Flutter, a popular framework for building cross-platform applications, offers a powerful solution for this through WebSockets. This beginner's guide will walk you through the essentials of using WebSockets in Flutter, enabling you to create applications with efficient, bidirectional communication. Whether it's for real-time messaging, live notifications, or instant updates, mastering WebSockets in Flutter will elevate your app development skills.Let's Get Started!!</p>
<h3 id="heading-what-is-websockets">What is WebSockets?</h3>
<p>In Flutter, <strong>WebSockets</strong> allow for two-way communication with a server without polling.They Provide a full-duplex communication channel over a single, long-lived connection, enabling bidirectional data flow between the client and server.</p>
<hr />
<h3 id="heading-how-to-communicate-with-websockets-in-flutter">How to Communicate with WebSockets in Flutter?</h3>
<p>Certainly! In Flutter, You can use WebSockets to enable real-time communication between a client(your Application) and a server. WebSockets allow for bidirectional, low-latency communication and are commonly used for real-time messaging, notification and live updates.</p>
<p>Here’s how you can work with WebSockets in Flutter:</p>
<ol>
<li><p><strong>Connect to a WebSocket Server</strong>:</p>
<ul>
<li><p>To establish a WebSocket connection, use the <code>web_socket_channel</code> package. Add it to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">dependencies:</span>
    <span class="hljs-attr">web_socket_channel:</span> <span class="hljs-string">^3.0.0</span>
</code></pre>
</li>
<li><p>Connect to a WebSocket server using the following code:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">final</span> channel = WebSocketChannel.connect(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'wss://demo.websocket.server'</span>));
</code></pre>
</li>
<li><p>Replace the URL (<code>wss://echo.websocket.events</code>) with the actual WebSocket server you want to connect to.</p>
</li>
</ul>
</li>
<li><p><strong>Listen for Messages from the Server</strong>:</p>
<ul>
<li><p>Once connected, listen for messages from the server using a <code>StreamBuilder</code> widget:</p>
<pre><code class="lang-dart">  StreamBuilder(
    stream: channel.stream,
    builder: (context, snapshot) {
      <span class="hljs-keyword">if</span> (snapshot.hasData) {
        <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Data arrived: <span class="hljs-subst">${snapshot.data}</span>'</span>);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Awaiting data...'</span>);
      }
    },
  );
</code></pre>
</li>
<li><p>The <code>channel.stream</code> provides a stream of messages from the server.</p>
</li>
</ul>
</li>
<li><p><strong>Send Data to the Server</strong>:</p>
<ul>
<li><p>To send data to the server, use the <code>sink</code> provided by the <code>WebSocketChannel</code>:</p>
<pre><code class="lang-dart">  channel.sink.add(<span class="hljs-string">'Greetings from JoFlee'</span>);
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Close the WebSocket Connection</strong>:</p>
<ul>
<li><p>When you’re done using the WebSocket, close the connection properly:</p>
<pre><code class="lang-dart">  channel.sink.close();
</code></pre>
</li>
</ul>
</li>
</ol>
<p><strong>Here’s a complete example of a Flutter app that connects to the test WebSocket server and displays received messages:</strong></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:web_socket_channel/web_socket_channel.dart'</span>;

<span class="hljs-keyword">void</span> main() =&gt; runApp(<span class="hljs-keyword">const</span> MyApp());

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">const</span> title = <span class="hljs-string">'WebSocket Demo'</span>;

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> MaterialApp(
      title: title,
      home: MyHomePage(title: title),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyHomePage({Key? key, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;

  <span class="hljs-meta">@override</span>
  State&lt;MyHomePage&gt; createState() =&gt; _MyHomePageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyHomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyHomePage</span>&gt; </span>{
  <span class="hljs-keyword">final</span> TextEditingController _controller = TextEditingController();
  <span class="hljs-keyword">final</span> _channel = WebSocketChannel.connect(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'wss://demo.websocket.server'</span>));

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">20</span>),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Form(
              child: TextFormField(
                controller: _controller,
                decoration: <span class="hljs-keyword">const</span> InputDecoration(labelText: <span class="hljs-string">'Send Your message'</span>),
              ),
            ),
            <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">24</span>),
            StreamBuilder(
              stream: _channel.stream,
              builder: (context, snapshot) {
                <span class="hljs-keyword">return</span> Text(snapshot.hasData ? <span class="hljs-string">'<span class="hljs-subst">${snapshot.data}</span>'</span> : <span class="hljs-string">''</span>);
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _sendMessage,
        tooltip: <span class="hljs-string">'Send message'</span>,
        child: <span class="hljs-keyword">const</span> Icon(Icons.send),
      ),
    );
  }

  <span class="hljs-keyword">void</span> _sendMessage() {
    <span class="hljs-keyword">if</span> (_controller.text.isNotEmpty) {
      _channel.sink.add(_controller.text);
    }
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _channel.sink.close();
    _controller.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  }
}
</code></pre>
<hr />
<h3 id="heading-most-common-5-websocket-error-scenarios">Most Common 5 WebSocket Error Scenarios :</h3>
<ol>
<li><p><strong>Connection Failures</strong>:</p>
<ul>
<li><p><strong>Timeouts</strong>: If the WebSocket connection takes too long to establish, it might time out.</p>
</li>
<li><p><strong>Network Errors</strong>: Network disruptions (e.g., loss of internet connectivity) can cause connection failures.</p>
</li>
<li><p><strong>Server Unavailability</strong>: If the server is down or unreachable, the connection won’t succeed.</p>
</li>
</ul>
</li>
<li><p><strong>Protocol Errors</strong>:</p>
<ul>
<li><p><strong>Invalid Handshake</strong>: The initial handshake between client and server fails due to incorrect headers or protocols.</p>
</li>
<li><p><strong>Unsupported Subprotocols</strong>: If the server doesn’t support the requested subprotocol, it can lead to errors.</p>
</li>
</ul>
</li>
<li><p><strong>Message Errors</strong>:</p>
<ul>
<li><p><strong>Invalid Data</strong>: Sending data in an unexpected format (e.g., binary data to a text-only WebSocket) can cause errors.</p>
</li>
<li><p><strong>Message Too Large</strong>: Exceeding the maximum message size can result in errors.</p>
</li>
</ul>
</li>
<li><p><strong>Server-Side Errors</strong>:</p>
<ul>
<li><p><strong>Internal Server Errors</strong>: The server might encounter issues while processing messages.</p>
</li>
<li><p><strong>Authentication Failures</strong>: If authentication fails, the server may close the connection.</p>
</li>
</ul>
</li>
<li><p><strong>Client-Side Errors</strong>:</p>
<ul>
<li><p><strong>Improper Handling</strong>: Incorrectly handling messages or not properly closing connections can lead to errors.</p>
</li>
<li><p><strong>Resource Exhaustion</strong>: Opening too many WebSocket connections can strain system resources.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h3 id="heading-how-to-handle-websocket-errors">How to handle WebSocket errors?</h3>
<p>When working with WebSockets in Flutter, it’s essential to handle errors gracefully. Here are some strategies:</p>
<ol>
<li><p><strong>Connection Errors</strong>:</p>
<ul>
<li><p>Listen for connection errors using the <code>WebSocketChannel</code>’s <code>stream.handleError</code> :</p>
<pre><code class="lang-dart">  channel.stream.handleError((error) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'WebSocket error: <span class="hljs-subst">$error</span>'</span>);
    <span class="hljs-comment">// Handle the error (e.g., you can reconnect or show an error message).</span>
  });
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Server-Side Errors</strong>:</p>
<ul>
<li><p>The server might send error messages. Handle them in your <code>StreamBuilder</code> :</p>
<pre><code class="lang-dart">  StreamBuilder(
    stream: channel.stream,
    builder: (context, snapshot) {
      <span class="hljs-keyword">if</span> (snapshot.hasError) {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Server error: <span class="hljs-subst">${snapshot.error}</span>'</span>);
        <span class="hljs-comment">// Handle the server error.</span>
      }
      <span class="hljs-keyword">return</span> Text(snapshot.hasData ? <span class="hljs-string">'<span class="hljs-subst">${snapshot.data}</span>'</span> : <span class="hljs-string">''</span>);
    },
  )
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Close Errors</strong>:</p>
<ul>
<li><p>When closing the connection, handle any errors:</p>
<pre><code class="lang-dart">  channel.sink.close().then((_) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'WebSocket closed successfully.'</span>);
  }).catchError((error) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error closing WebSocket: <span class="hljs-subst">$error</span>'</span>);
  });
</code></pre>
</li>
</ul>
</li>
</ol>
<hr />
<blockquote>
<p>Implement WebSockets in Flutter unlocks the potential for creating responsive, real-time applications. By understanding how to establish connections, handle messages, and manage errors, you can enhance your app’s interactivity and user experience. Whether you’re building chat applications, live notifications, or real-time updates, integrating WebSockets into your Flutter projects will significantly elevate your development skills and the functionality of your applications. Start experimenting with WebSockets today to bring your Flutter apps to life with real-time connectivity.</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[How to Use Dio Package for Networking in Flutter]]></title><description><![CDATA[What is Dio?
Certainly! The Dio package is a powerful HTTP networking library for Dart and Flutter. It simplifies making network requests and handling responses. Here are some key features and examples of how to use Dio:

Global Configuration and Int...]]></description><link>https://blog.joflee.com/how-to-use-dio-package-for-networking-in-flutter</link><guid isPermaLink="true">https://blog.joflee.com/how-to-use-dio-package-for-networking-in-flutter</guid><category><![CDATA[APIs]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Applications]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Wed, 29 May 2024 06:22:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715688701108/f27b1de3-5065-4aec-9615-ccaa537b39dc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-dio">What is Dio?</h2>
<p>Certainly! The <strong>Dio</strong> package is a powerful HTTP networking library for <strong>Dart</strong> and <strong>Flutter</strong>. It simplifies making network requests and handling responses. Here are some key features and examples of how to use Dio:</p>
<ol>
<li><p><strong>Global Configuration and Interceptors</strong>:</p>
<ul>
<li><p>Dio allows you to set global configurations and interceptors for all requests. You can customise headers, timeouts, and other settings.</p>
</li>
<li><p>Example:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
  <span class="hljs-keyword">final</span> dio = Dio();
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Performing GET and POST Requests</strong>:</p>
<ul>
<li><p>You can easily make GET and POST requests:</p>
<pre><code class="lang-dart">  <span class="hljs-comment">// GET request</span>
  Response response = <span class="hljs-keyword">await</span> dio.<span class="hljs-keyword">get</span>(<span class="hljs-string">'/demo?id=02&amp;name=dio'</span>);
  <span class="hljs-built_in">print</span>(response.data.toString());

  <span class="hljs-comment">// POST request</span>
  response = <span class="hljs-keyword">await</span> dio.post(<span class="hljs-string">'/demo'</span>, data: {<span class="hljs-string">'id'</span>: <span class="hljs-number">02</span>, <span class="hljs-string">'name'</span>: <span class="hljs-string">'dio'</span>});
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Concurrent Requests</strong>:</p>
<ul>
<li><p>Dio supports making multiple concurrent requests:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">await</span> Future.wait([dio.post(<span class="hljs-string">'/info'</span>), dio.<span class="hljs-keyword">get</span>(<span class="hljs-string">'/token'</span>)]);
</code></pre>
</li>
</ul>
</li>
<li><p><strong>File Uploading and Downloading</strong>:</p>
<ul>
<li><p>You can upload files to the server using FormData:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>; <span class="hljs-comment">// Needed for accessing files</span>
  <span class="hljs-comment">// Assuming you have a method in your API service (or elsewhere) named `uploadFile`</span>
  <span class="hljs-comment">// and a variable named `post` of type `Post` (or your model class name) containing the data</span>
  <span class="hljs-comment">// Assuming the 'filePath' property exists in your Post model class</span>
  <span class="hljs-keyword">final</span> formData = FormData.fromMap({
    <span class="hljs-string">'name'</span>: <span class="hljs-string">'dio'</span>,
    <span class="hljs-string">'date'</span>: <span class="hljs-built_in">DateTime</span>.now().toIso8601String(),
    <span class="hljs-string">'file'</span>: <span class="hljs-keyword">await</span> MultipartFile.fromFile(post.filePath, filename: <span class="hljs-string">'upload.txt'</span>),
  });
  <span class="hljs-keyword">await</span> ApiService.uploadFile(<span class="hljs-string">'/info'</span>, formData); <span class="hljs-comment">// Replace '/info' with your actual endpoint</span>
  <span class="hljs-comment">// Assuming you have a method in your service (or elsewhere) named `downloadFile`</span>
  <span class="hljs-comment">// and a variable named `downloadUrl` containing the download URL</span>
  <span class="hljs-keyword">final</span> directory = <span class="hljs-keyword">await</span> getTemporaryDirectory();
  <span class="hljs-keyword">final</span> savePath = <span class="hljs-string">'<span class="hljs-subst">${directory.path}</span>/downloaded_file.txt'</span>; <span class="hljs-comment">// Replace with desired filename</span>
  <span class="hljs-keyword">await</span> ApiService.downloadFile(downloadUrl, savePath);
</code></pre>
<p>  <strong>Explanation:</strong></p>
<ul>
<li><p>We've assumed you have a <code>Post</code> model class or a similar class that represents the data you want to upload along with the file.</p>
</li>
<li><p>We've further assumed that your <code>Post</code> model class has a property named <code>filePath</code> that stores the path to the file you want to upload. Adjust this placeholder if the property name is different.</p>
</li>
<li><p>We've assumed you have methods in your API service (or a similar class) named <code>uploadFile</code> and <code>downloadFile</code> for handling uploads and downloads, respectively. Replace these with your actual method names.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>        <strong>Additional Notes:</strong></p>
<ul>
<li><p>Remember to replace placeholders like <code>/info</code> and <code>downloadUrl</code> with your actual server-side API endpoint URLs.</p>
</li>
<li><p>You'll need to modify the code to call these methods appropriately from your UI or other parts of your code.</p>
</li>
<li><p>If the structure of your code is significantly different, please provide the relevant code sections for further customisation.</p>
</li>
</ul>
<ol start="5">
<li><p><strong>Listening to Upload Progress</strong>:</p>
<ul>
<li><p>You can listen to the progress of file uploads:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">await</span> dio.post(
    <span class="hljs-string">'https://www.fluttercommunity.com/joflee/2/8.2.1/demo'</span>,
    data: {<span class="hljs-string">'jj'</span>: <span class="hljs-string">'aa'</span> * <span class="hljs-number">11</span>},
    onSendProgress: (<span class="hljs-built_in">int</span> sent, <span class="hljs-built_in">int</span> total) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'<span class="hljs-subst">$sent</span> <span class="hljs-subst">$total</span>'</span>);
    },
  );
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Custom Adapters and Transformers</strong>:</p>
<ul>
<li>Dio allows you to create custom adapters and transformers for handling specific data formats.</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-how-to-do-networking-using-the-dio-package-in-flutter">How to Do Networking Using the Dio Package in Flutter</h2>
<p>Certainly! Let’s break down the process of making API calls, creating model classes, and parsing data using the <strong>Dio</strong> package in Flutter. I’ll provide a step-by-step guide along with code examples.</p>
<h3 id="heading-step-1-set-up-your-flutter-project">Step 1: Set Up Your Flutter Project</h3>
<p>Before we dive into working with APIs, make sure you have Flutter and Dart installed. If you haven’t already, follow the official Flutter installation guide. Create a new Flutter project using the following command:</p>
<pre><code class="lang-bash">flutter create api_example
</code></pre>
<h3 id="heading-step-2-add-the-dio-package">Step 2: Add the Dio Package</h3>
<p>To make HTTP requests, add the <strong>dio</strong> package to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">dio:</span> <span class="hljs-string">^5.4.3</span>
</code></pre>
<p>Run <code>flutter pub get</code> to fetch the dependencies.</p>
<h3 id="heading-step-3-create-model-classes">Step 3: Create Model Classes</h3>
<p>To parse the JSON data received from the API, create a model class. For example:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> userId;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> id;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> body;
  Post({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.userId, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.body});
  <span class="hljs-keyword">factory</span> Post.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">return</span> Post(
      userId: json[<span class="hljs-string">'userId'</span>],
      id: json[<span class="hljs-string">'id'</span>],
      title: json[<span class="hljs-string">'title'</span>],
      body: json[<span class="hljs-string">'body'</span>],
    );
  }
  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() =&gt; {
    <span class="hljs-string">'userId'</span>: userId,
    <span class="hljs-string">'id'</span>: id,
    <span class="hljs-string">'title'</span>: title,
    <span class="hljs-string">'body'</span>: body,
  };
}
</code></pre>
<h3 id="heading-step-4-api-calling">Step 4: API Calling</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_api_example/post.dart'</span>;  <span class="hljs-comment">// Import Post model</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiService</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> baseUrl = <span class="hljs-string">'https://jsonplaceholder.typicode.com'</span>; <span class="hljs-comment">// Replace with your API endpoint</span>
  <span class="hljs-keyword">static</span> Future&lt;Post&gt; fetchPost(<span class="hljs-built_in">int</span> postId) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> dio = Dio();
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> dio.<span class="hljs-keyword">get</span>(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span>/posts/<span class="hljs-subst">$postId</span>'</span>);
    <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
      <span class="hljs-keyword">return</span> Post.fromJson(response.data);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to fetch post: <span class="hljs-subst">${response.statusCode}</span>'</span>);
    }
  }
  <span class="hljs-keyword">static</span> Future&lt;<span class="hljs-keyword">void</span>&gt; createPost(Post post) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> dio = Dio();
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> dio.post(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span>/posts'</span>, data: post.toJson());
    <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">201</span>) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Post created successfully!'</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to create post: <span class="hljs-subst">${response.statusCode}</span>'</span>);
    }
  }
}
</code></pre>
<h3 id="heading-step-5-fetch-data-from-an-api-and-display-on-screen">Step 5: Fetch Data from an API and Display on Screen</h3>
<p>Let’s create a simple Flutter app that fetches and displays data from a public API. Here’s an example:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_api_example/api_service.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_api_example/post.dart'</span>;
<span class="hljs-keyword">void</span> main() =&gt; runApp(MyApp());
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'API Example'</span>,
      home: MyHomePage(),
    );
  }
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyHomePage({<span class="hljs-keyword">super</span>.key});
  <span class="hljs-meta">@override</span>
  _MyHomePageState createState() =&gt; _MyHomePageState();
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyHomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyHomePage</span>&gt; </span>{
  Future&lt;Post?&gt; _post;
  <span class="hljs-built_in">String</span> _errorMessage = <span class="hljs-string">''</span>;
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _fetchData(); <span class="hljs-comment">// Fetch data on app launch</span>
  }
  Future&lt;<span class="hljs-keyword">void</span>&gt; _fetchData() <span class="hljs-keyword">async</span> {
    setState(() {
      _errorMessage = <span class="hljs-string">''</span>; <span class="hljs-comment">// Clear any previous error messages</span>
      _post = <span class="hljs-keyword">null</span>;         <span class="hljs-comment">// Reset the post state</span>
    });
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> post = <span class="hljs-keyword">await</span> ApiService.fetchPost(<span class="hljs-number">1</span>); <span class="hljs-comment">// Assuming post ID is 1</span>
      setState(() {
        _post = post;
      });
    } <span class="hljs-keyword">on</span> Exception <span class="hljs-keyword">catch</span> (e) {
      setState(() {
        _errorMessage = e.toString();
      });
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>);
    }
  }
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'API Example'</span>),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            <span class="hljs-keyword">if</span> (_post != <span class="hljs-keyword">null</span>)
            Text(<span class="hljs-string">'Title: <span class="hljs-subst">${_post.title}</span>'</span>),
            <span class="hljs-keyword">if</span> (_post != <span class="hljs-keyword">null</span>)
            Text(<span class="hljs-string">'Body: <span class="hljs-subst">${_post.body}</span>'</span>),
            <span class="hljs-keyword">if</span> (_errorMessage.isNotEmpty)
              Text(
                <span class="hljs-string">'Error: <span class="hljs-subst">$_errorMessage</span>'</span>,
                style: <span class="hljs-keyword">const</span> TextStyle(color: Colors.red),
              ),
            ElevatedButton(
              onPressed: _fetchData,
              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Fetch Post'</span>),
            ),
                    ElevatedButton(
                      onPressed: () <span class="hljs-keyword">async</span> {
                        <span class="hljs-comment">// Example of posting data (replace with your data)</span>
                        <span class="hljs-keyword">final</span> post = Post(userId: <span class="hljs-number">1</span>, id: <span class="hljs-number">2</span>, title: <span class="hljs-string">'New Post'</span>, body: <span class="hljs-string">'Here is a new post'</span>);
                        <span class="hljs-keyword">await</span> ApiService.createPost(post);
                      }
             )
          ],
        ),
      ),
    );
  }
}
</code></pre>
<p>In this example:</p>
<ul>
<li><p>We create a simple app with a button that triggers the <code>fetchData</code> function.</p>
</li>
<li><p>The <code>fetchData</code> function makes a GET request to the JSONPlaceholder API.</p>
</li>
<li><p>If the response status code is 200, we extract the title from the response and update the UI.</p>
</li>
<li><p>If there’s an error, we handle it gracefully.</p>
</li>
</ul>
<hr />
<h2 id="heading-difference-between-http-and-dio-packages-in-flutter">Difference Between http and dio packages in Flutter</h2>
<p>Certainly! Let’s compare the <strong>http</strong> package and the <strong>Dio</strong> package in Flutter for making API calls. Both packages serve the purpose of handling HTTP requests, but they have different features and use cases:</p>
<ol>
<li><p><strong>http Package</strong>:</p>
<ul>
<li><p><strong>Advantages</strong>:</p>
<ul>
<li><p><strong>Simplicity</strong>: The <strong>http</strong> package is straightforward and easy to use. It provides basic functionality for making HTTP requests.</p>
</li>
<li><p><strong>Lightweight</strong>: It’s a lightweight library, making it suitable for small projects and simple use cases.</p>
</li>
</ul>
</li>
<li><p><strong>Disadvantages</strong>:</p>
<ul>
<li><p><strong>Basic Functionality</strong>: The <strong>http</strong> package only covers the essentials for making network requests. Additional features must be implemented independently.</p>
</li>
<li><p><strong>Error Handling</strong>: You need to write custom error-handling functions.</p>
</li>
</ul>
</li>
<li><p><strong>Use Case</strong>:</p>
<ul>
<li>Ideal for small projects where development speed is crucial.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Dio Package</strong>:</p>
<ul>
<li><p><strong>Advantages</strong>:</p>
<ul>
<li><p><strong>Feature-Rich</strong>: <strong>Dio</strong> is a powerful HTTP client for Dart. It offers a wide range of features beyond basic networking.</p>
</li>
<li><p><strong>Interceptors</strong>: You can use interceptors for tasks like logging, caching, and modifying requests and responses.</p>
</li>
<li><p><strong>Global Configuration</strong>: Customise global settings for all requests.</p>
</li>
<li><p><strong>FormData and File Downloading</strong>: Supports file uploading and downloading.</p>
</li>
<li><p><strong>Timeouts</strong>: Set timeouts for requests.</p>
</li>
<li><p><strong>Fast and Efficient</strong>: Utilises asynchronous and parallel requests.</p>
</li>
</ul>
</li>
<li><p><strong>Disadvantages</strong>:</p>
<ul>
<li><strong>Complexity</strong>: Due to its extensive functionality, there’s a higher chance of encountering bugs.</li>
</ul>
</li>
<li><p><strong>Use Case</strong>:</p>
<ul>
<li><p>Suitable for larger projects with long-term support.</p>
</li>
<li><p>When development speed is less critical, and you prioritise additional features and reliability.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<ul>
<li><p><strong>http</strong> is simple and lightweight, while <strong>Dio</strong> provides more advanced features.</p>
</li>
<li><p>Choose <strong>http</strong> for small projects and quick development.</p>
</li>
<li><p>Opt for <strong>Dio</strong> when you need additional functionality and long-term support.</p>
</li>
</ul>
<p>Remember that the choice depends on your project’s size, complexity, and specific requirements. Happy coding! 🚀</p>
<blockquote>
<p>In conclusion, the Dio package is a robust and feature-rich HTTP client for Dart and Flutter, offering advanced capabilities like global configuration, interceptors, concurrent requests, and file handling. While the http package is suitable for smaller projects due to its simplicity and lightweight nature, Dio excels in larger, more complex applications that require extensive networking features and long-term support. By understanding the strengths and use cases of each package, developers can make informed decisions to best meet their project's needs. Happy coding! 🚀</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[Beginner's Guide to Flutter Networking with HTTP Package]]></title><description><![CDATA[What is Networking in flutter?
Networking in Flutter involves establishing and managing communication between a Flutter app and a remote server or API over the network. It encompasses tasks such as making HTTP requests, handling responses, sending an...]]></description><link>https://blog.joflee.com/beginners-guide-to-flutter-networking-with-http-package</link><guid isPermaLink="true">https://blog.joflee.com/beginners-guide-to-flutter-networking-with-http-package</guid><category><![CDATA[Flutter]]></category><category><![CDATA[networking for beginners]]></category><category><![CDATA[http]]></category><category><![CDATA[api]]></category><category><![CDATA[API basics ]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Tue, 14 May 2024 05:04:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715331211121/03d13b8f-3756-4c59-9951-7eb21f440b26.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-networking-in-flutter">What is Networking in flutter?</h2>
<p>Networking in Flutter involves establishing and managing communication between a Flutter app and a remote server or API over the network. It encompasses tasks such as making HTTP requests, handling responses, sending and receiving data, and managing errors. Essentially, networking enables your Flutter app to interact with external services, retrieve data, and update information over the internet.</p>
<p>Here are some common networking tasks in Flutter:</p>
<ol>
<li><p><strong>Making Authenticated Requests</strong>: When your app needs to access protected resources on a server, it can send authenticated requests by including authentication tokens or credentials.</p>
</li>
<li><p><strong>Parsing JSON in the Background</strong>: Many APIs return data in JSON format. Flutter allows you to parse this data efficiently, transforming it into usable objects within your app.</p>
</li>
<li><p><strong>Fetching Data from the Internet</strong>: Your app can retrieve data from external APIs, databases, or other web services. This data can be used to display information to users or perform other actions.</p>
</li>
<li><p><strong>Sending Data to the Internet</strong>: If your app collects user input or generates data, it can send that data to a server for processing or storage.</p>
</li>
<li><p><strong>Updating Data Over the Internet</strong>: Apps often need to update information on the server, such as saving user preferences, marking items as read, or modifying user profiles.</p>
</li>
<li><p><strong>Communicating with WebSockets</strong>: WebSockets allow real-time communication between the app and a server. They are useful for features like chat applications, live updates, and notifications.</p>
</li>
</ol>
<p>Flutter provides a powerful and flexible framework for networking, enabling developers to implement various networking techniques and interact with web services efficiently.If you’re building a Flutter app that requires data exchange with external servers or APIs, understanding networking concepts is essential for a smooth user experience.</p>
<h2 id="heading-popular-networking-library-of-flutter-http">Popular networking library of Flutter - HTTP</h2>
<p>Certainly! When it comes to networking in Flutter, there are several popular libraries that can simplify making HTTP requests and handling network-related tasks. Here are some of the most widely used ones:</p>
<h3 id="heading-http">http :</h3>
<ul>
<li><p><strong>Description</strong>: The <code>http</code> package is maintained by the Dart team and is one of the most-liked HTTP packages on pub.dev.</p>
</li>
<li><p><strong>Features</strong>:</p>
<ul>
<li><p>Provides a high-level API for making HTTP requests.</p>
</li>
<li><p>Supports retrying requests.</p>
</li>
</ul>
</li>
<li><p><strong>Sample Usage</strong>:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;
  <span class="hljs-keyword">void</span> sendPostRequest() <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">void</span> sendPostRequest() <span class="hljs-keyword">async</span> {
          <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'put your API here'</span>);
          <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(url, body: {
            <span class="hljs-string">'title'</span>: <span class="hljs-string">'[Your title]'</span>,
            <span class="hljs-string">'body'</span>: <span class="hljs-string">'Your body'</span>,
          });
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response status: <span class="hljs-subst">${response.statusCode}</span>'</span>);
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response body: <span class="hljs-subst">${response.body}</span>'</span>);
        }
      } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">print</span>(e.toString());
      }
    }
</code></pre>
<p>  Libraries offer different features and capabilities, so choose the one that best fits your project’s requirements.</p>
</li>
</ul>
<hr />
<h2 id="heading-http-package-in-flutter-for-networking-and-data-parsing"><strong>HTTP Package in Flutter for Networking and Data Parsing</strong></h2>
<p><strong>Step 1: Setting Up a Flutter Project</strong></p>
<ul>
<li><p>Ensure that you have Flutter and Dart installed. If you haven’t already, follow the official Flutter installation guide.</p>
</li>
<li><p>Create a new Flutter project using the following command:</p>
</li>
</ul>
<pre><code class="lang-dart">flutter create api_example
</code></pre>
<p><strong>Step 2: Add the HTTP Package</strong></p>
<ul>
<li>To add the http package as a dependency, run the following command:</li>
</ul>
<pre><code class="lang-dart">flutter pub add http
</code></pre>
<p>or</p>
<ul>
<li>To make HTTP requests, add the http package to your <code>pubspec.yaml</code> file:</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">http:</span> <span class="hljs-string">^1.2.1</span>
</code></pre>
<p><strong>Step 3: Import the HTTP Package</strong></p>
<ul>
<li>Import the package in your Dart file:</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;
</code></pre>
<p><strong>Step 4: Make a Network Request</strong></p>
<ul>
<li>Use the <code>http.get()</code> method to fetch data from an API:</li>
</ul>
<pre><code class="lang-dart">Future&lt;http.Response&gt; fetchPosts() {
  <span class="hljs-keyword">return</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'Put your API here'</span>));
}
</code></pre>
<p><strong>Step 5: Handle Authentication</strong></p>
<ul>
<li>For basic authentication, include the credentials in the request headers:</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'Put your API here'</span>);
<span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(url, headers: {
  <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">'Basic your_base64_encoded_credentials'</span>,
});
</code></pre>
<ul>
<li>For token-based authentication, set the token in the request headers:</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> token = <span class="hljs-string">'your_access_token'</span>;
<span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(url, headers: {
  <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">'Bearer <span class="hljs-subst">$token</span>'</span>,
});
</code></pre>
<p><strong>Step 6: Parse the Response</strong></p>
<ul>
<li>Convert the response into a Dart object. Create a class that represents the data from the network request:</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostModel</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> userId;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> body;
  PostModel({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.userId,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.body,
  });
  <span class="hljs-keyword">factory</span> PostModel.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">return</span> PostModel(
      userId: json[<span class="hljs-string">'userId'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">int</span>,
      title: json[<span class="hljs-string">'title'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>,
      body: json[<span class="hljs-string">'body'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>,
    );
  }
  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'userId'</span>: userId,
      <span class="hljs-string">'title'</span>: title,
      <span class="hljs-string">'body'</span>: body,
    };
  }
}
</code></pre>
<p><strong>Step 7: Display the Data</strong></p>
<ul>
<li><p>In your widget tree, wrap the part where you want to display the data with a <code>FutureBuilder</code>.</p>
</li>
<li><p>Set the <code>future</code> parameter of <code>FutureBuilder</code> to the function that fetches the data (in this case, <code>fetchPosts()</code>).</p>
</li>
<li><p>The <code>builder</code> callback will be called with the data once it’s available. You can use this data to build your UI.</p>
</li>
</ul>
<pre><code class="lang-dart">FutureBuilder&lt;<span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;&gt;&gt;(
  future: fetchPosts(),
  builder: (context, snapshot) {
    <span class="hljs-keyword">if</span> (snapshot.connectionState == ConnectionState.waiting) {
      <span class="hljs-comment">// While waiting for data, show a loading indicator</span>
      <span class="hljs-keyword">return</span> CircularProgressIndicator();
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.hasError) {
      <span class="hljs-comment">// If there's an error, display an error message</span>
      <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Error loading data'</span>);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.hasData) {
      <span class="hljs-comment">// If data is available, display the posts</span>
      <span class="hljs-keyword">final</span> posts = snapshot.data;
      <span class="hljs-keyword">return</span> ListView.builder(
        itemCount: posts.length,
        itemBuilder: (context, index) {
          <span class="hljs-keyword">final</span> post = posts[index];
          <span class="hljs-keyword">return</span> ListTile(
            title: Text(post[<span class="hljs-string">'title'</span>]),
            subtitle: Text(post[<span class="hljs-string">'body'</span>]),
          );
        },
      );
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// If none of the above conditions apply, show a fallback</span>
      <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'No data available'</span>);
    }
  },
)
</code></pre>
<p><strong>Step 8: Error Handling</strong></p>
<ul>
<li>Test your network requests thoroughly, handle errors, and ensure graceful handling of exceptions. If the status code is not 200 (OK), throw an exception:</li>
</ul>
<pre><code class="lang-dart">
<span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
  <span class="hljs-comment">// If the server returns a 200 OK response, parse the JSON.</span>
  <span class="hljs-keyword">return</span> PostModel.toJson(jsonDecode(response.body));
} <span class="hljs-keyword">else</span> {
  <span class="hljs-comment">// If the server did not return a 200 OK response, throw an exception.</span>
  <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to load album'</span>);
}
</code></pre>
<hr />
<h2 id="heading-some-common-http-status-code">Some common http status code</h2>
<p>Certainly! HTTP status codes are three-digit numbers returned by a server in response to a client’s request. They provide information about the outcome of the request. Here are some common HTTP status codes:</p>
<ol>
<li><p><strong>1xx (Informational)</strong>:</p>
<ul>
<li><p>These codes indicate that the request has been received and the server is continuing to process it.</p>
</li>
<li><p>Example: <code>100 Continue</code> means the client can continue with the request.</p>
</li>
</ul>
</li>
<li><p><strong>2xx (Successful)</strong>:</p>
<ul>
<li><p>These codes indicate that the request was successfully received, understood, and processed.</p>
</li>
<li><p>Examples:</p>
<ul>
<li><p><code>200 OK</code>: The request was successful.</p>
</li>
<li><p><code>201 Created</code>: A new resource was successfully created.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>3xx (Redirection)</strong>:</p>
<ul>
<li><p>These codes indicate that further action is needed to complete the request.</p>
</li>
<li><p>Examples:</p>
<ul>
<li><p><code>301 Moved Permanently</code>: The requested resource has been permanently moved to a different location.</p>
</li>
<li><p><code>302 Found</code>: The requested resource has been temporarily moved to a different location.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>4xx (Client Errors)</strong>:</p>
<ul>
<li><p>These codes indicate that the client (usually the browser or user agent) made an error.</p>
</li>
<li><p>Examples:</p>
<ul>
<li><p><code>400 Bad Request</code>: The request was malformed or invalid.</p>
</li>
<li><p><code>404 Not Found</code>: The requested resource was not found on the server.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>5xx (Server Errors)</strong>:</p>
<ul>
<li><p>These codes indicate that the server encountered an error while processing the request.</p>
</li>
<li><p>Examples:</p>
<ul>
<li><p><code>500 Internal Server Error</code>: A generic server error occurred.</p>
</li>
<li><p><code>503 Service Unavailable</code>: The server is temporarily unable to handle requests.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>Remember that these are just a few examples, and there are many other status codes with specific meanings. When working with APIs or web services, understanding these status codes is crucial for handling responses effectively.</p>
<blockquote>
<p>Mastering networking in Flutter with the HTTP package is crucial for efficient communication between apps and servers. Understanding networking concepts, utilising the HTTP package for requests, handling authentication, parsing data, and managing errors are key for creating robust Flutter applications. Happy coding and networking in Flutter!</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[Everything You Need to Know About Navigation and Routing in Flutter]]></title><description><![CDATA[What is Navigation and Routing in Flutter?
In the context of Flutter, navigation refers to the process of switching between different screens or pages within an application, while routing involves managing the flow and organisation of these screens. ...]]></description><link>https://blog.joflee.com/everything-you-need-to-know-about-navigation-and-routing-in-flutter</link><guid isPermaLink="true">https://blog.joflee.com/everything-you-need-to-know-about-navigation-and-routing-in-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[navigation]]></category><category><![CDATA[Dynamic Programming]]></category><category><![CDATA[user experience]]></category><category><![CDATA[Flutter Examples]]></category><dc:creator><![CDATA[Fenisha Koladiya]]></dc:creator><pubDate>Thu, 02 May 2024 09:08:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714471777456/c373b518-4bd7-40e6-89e1-cc0db6f105ae.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-what-is-navigation-and-routing-in-flutter">What is Navigation and Routing in Flutter?</h3>
<p>In the context of <strong>Flutter</strong>, <strong>navigation</strong> refers to the process of switching between different screens or pages within an application, while <strong>routing</strong> involves managing the flow and organisation of these screens. Let’s delve into the details:</p>
<h3 id="heading-benefit-of-do-routing-and-navigation-in-flutter">Benefit of do routing and navigation in flutter:</h3>
<ol>
<li><p><strong>Screen Organisation and Flow</strong>:</p>
<ul>
<li><p><strong>Routing</strong> allows you to structure your app into different screens or pages. Each screen represents a distinct part of your app’s functionality.</p>
</li>
<li><p>By navigating between screens, users can follow a logical flow within the app. For example, moving from a login screen to a dashboard or from a product list to a product details page.</p>
</li>
</ul>
</li>
<li><p><strong>Modularity and Separation of Concerns</strong>:</p>
<ul>
<li><p>Routing encourages a modular approach to building apps. Each screen can be encapsulated as a separate widget or class.</p>
</li>
<li><p>Separating screens into smaller components improves code organisation and maintainability. It also enables better collaboration among team members.</p>
</li>
</ul>
</li>
<li><p><strong>User Experience (UX)</strong>:</p>
<ul>
<li><p><strong>Navigation</strong> provides a seamless experience for users. They can easily switch between different parts of the app without losing context.</p>
</li>
<li><p>Well-designed navigation enhances user satisfaction and engagement.</p>
</li>
</ul>
</li>
<li><p><strong>Deep Linking and URL Handling</strong>:</p>
<ul>
<li><p><strong>Routing</strong> enables deep linking, allowing users to directly access specific screens within the app via URLs.</p>
</li>
<li><p>Deep links are useful for sharing content, handling notifications, and improving user acquisition.</p>
</li>
</ul>
</li>
<li><p><strong>State Preservation</strong>:</p>
<ul>
<li><p>When navigating between screens, Flutter preserves the state of widgets on the stack.</p>
</li>
<li><p>This means that user input, scroll positions, and other widget states are retained when returning to a previous screen.</p>
</li>
</ul>
</li>
<li><p><strong>Custom Transitions and Animations</strong>:</p>
<ul>
<li><p>You can customise the transition animations between screens using Navigator.</p>
</li>
<li><p>For example, sliding, fading, or scaling effects can enhance the visual appeal of your app.</p>
</li>
</ul>
</li>
<li><p><strong>Named Routes for Clarity</strong>:</p>
<ul>
<li><p>Named routes provide a clear and descriptive way to define navigation paths.</p>
</li>
<li><p>Developers and designers can easily understand the app’s structure by looking at the route names.</p>
</li>
</ul>
</li>
<li><p><strong>Handling Back Navigation</strong>:</p>
<ul>
<li><p><strong>Navigator</strong> automatically handles the back button or swipe gestures for going back to the previous screen.</p>
</li>
<li><p>This behaviour ensures a consistent user experience across different platforms.</p>
</li>
</ul>
</li>
<li><p><strong>Advanced Routing Scenarios</strong>:</p>
<ul>
<li>For complex apps, you can use third-party routing packages like <code>go_router</code> to handle dynamic routes, nested navigation, and more.</li>
</ul>
</li>
</ol>
<hr />
<h3 id="heading-ways-to-do-navigation-and-routing-in-flutter">Ways to do Navigation and routing in Flutter</h3>
<p>Certainly! In <strong>Flutter</strong>, there are <strong>three main ways</strong> to handle routing and navigation:</p>
<ol>
<li><p><strong>Direct Navigation with</strong><code>MaterialPageRoute</code></p>
</li>
<li><p><strong>Static Navigation with Named Routes</strong></p>
</li>
<li><p><strong>Dynamic Navigation with Generated Routes</strong></p>
</li>
</ol>
<h2 id="heading-1direct-navigation-with-materialpageroute"><strong>1.Direct Navigation with</strong> <code>MaterialPageRoute</code>:</h2>
<ul>
<li><p>The most straightforward method involves using the <code>Navigator</code><strong>widget</strong>.</p>
</li>
<li><p>You can navigate between screens by calling imperative methods like <code>push()</code> or <code>pop()</code>.</p>
</li>
<li><p>For example</p>
<pre><code class="lang-dart">  onPressed: () {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) =&gt; <span class="hljs-keyword">const</span> UserScreen(user: user),
      ),
    );
  }
</code></pre>
</li>
</ul>
<p>The <code>MaterialPageRoute</code> specifies transition animations for Material Design.</p>
<h3 id="heading-pros-of-materialpageroute">Pros of <code>MaterialPageRoute</code>:</h3>
<ol>
<li><p><strong>Simplicity and Built-in Transitions</strong>:</p>
<ul>
<li><p><code>MaterialPageRoute</code> is straightforward to use. You define named routes, and it handles navigation between screens.</p>
</li>
<li><p>It provides built-in Material Design transitions (e.g., slide, fade) when navigating between pages.</p>
</li>
</ul>
</li>
<li><p><strong>Centralised Instantiation</strong>:</p>
<ul>
<li><p>When using named routes with <code>MaterialPageRoute</code>, you keep page instantiation centralised.</p>
</li>
<li><p>This avoids code duplication and makes your app cleaner and more maintainable.</p>
</li>
</ul>
</li>
<li><p><strong>Integration with Material Design</strong>:</p>
<ul>
<li><p><code>MaterialPageRoute</code> aligns seamlessly with Material Design guidelines.</p>
</li>
<li><p>It ensures consistency in navigation animat<a target="_blank" href="https://stackoverflow.com/questions/67444808/difference-between-materialpageroute-and-pageroutebuilder">i</a>ons and visual cues for Android apps.</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-cons-of-materialpageroute">Cons of <code>MaterialPageRoute</code>:</h3>
<ol>
<li><p><strong>Limited Customisation</strong>:</p>
<ul>
<li><p>While <code>MaterialPageRoute</code> provides basic transitions, it might not cover all custom animation needs.</p>
</li>
<li><p>If you require more complex or unique transitions, you’ll need to extend or create your own route.</p>
</li>
</ul>
</li>
<li><p><strong>iOS-Specific Behaviour</strong>:</p>
<ul>
<li><p>On iOS devices, <code>MaterialPageRoute</code> introduces some iOS-like sliding animations in the background.</p>
</li>
<li><p>If you want platform-specific behaviour, this might not be ideal.</p>
</li>
</ul>
</li>
<li><p><strong>Not Ideal for All Scenarios</strong>:</p>
<ul>
<li><p>For more advanced routing requirements (e.g., deep linking, dynamic routes<a target="_blank" href="https://stackoverflow.com/questions/67444808/difference-between-materialpageroute-and-pageroutebuilder">)</a>, <code>MaterialPageRoute</code> alone may not suffice.</p>
</li>
<li><p>Consider other options like <code>PageRouteBuilder</code> or third-party routing packages.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-2static-navigation-with-named-routes"><strong>2.Static Navigation with Named Routes</strong>:</h2>
<ul>
<li><p>Named routes provide a <strong>declarative way</strong> to define navi<a target="_blank" href="https://stackoverflow.com/questions/67444808/difference-between-materialpageroute-and-pageroutebuilder">g</a>ation paths.</p>
</li>
<li><p>Define named routes in the <code>routes</code> parameter of <code>MaterialApp</code>:</p>
</li>
<li><p>For example</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      routes: {
        <span class="hljs-string">'/'</span>: (context) =&gt; HomeScreen(),
        <span class="hljs-string">'/dashboard'</span>: (context) =&gt; DashboardScreen(),
      },
    );
  }
</code></pre>
</li>
<li><p>However, named routes have limitations and may not suit all applications.</p>
</li>
</ul>
<h3 id="heading-pros-of-named-routes">Pros of Named Routes:</h3>
<ol>
<li><p><strong>Faster Navigation for Frequently Accessed Routes</strong>:</p>
<ul>
<li><p>Named routes allow you to navigate directly to a specific screen without pushing it onto the navigation stack.</p>
</li>
<li><p>This results in fa<a target="_blank" href="https://stackoverflow.com/questions/67444808/difference-between-materialpageroute-and-pageroutebuilder">s</a>ter navigation for frequently accessed routes, as there’s no need to rebuild the entire stack.</p>
</li>
</ul>
</li>
<li><p><strong>Simpler Code Structure for Easier Maintenance</strong>:</p>
<ul>
<li><p>By defining named rou<a target="_blank" href="https://stackoverflow.com/questions/67444808/difference-between-materialpageroute-and-pageroutebuilder">t</a>es, you create a clear mapping between route names and their corresponding screens.</p>
</li>
<li><p>This simplifies your codebase and makes it easier to maintain and understand.</p>
</li>
</ul>
</li>
<li><p><strong>Quicker App Startup When Routes Are Limited</strong>:</p>
<ul>
<li>When using named routes, the app initialises only the necessary routes during startup.</li>
</ul>
</li>
</ol>
<p>This can lead to quicker app launch times, especially when you have a limited Number of routes.</p>
<h3 id="heading-cons-of-named-routes">Cons of Named Routes:</h3>
<ol>
<li><p><strong>Impact on Startup Time with Many Routes</strong>:</p>
<ul>
<li><p>If your app has an extensive set of named routes, it can impact the app’s startup time.</p>
</li>
<li><p>The more routes you define, the longer it takes to initialise the navigation structure.</p>
</li>
</ul>
</li>
<li><p><strong>Less Flexible for Dynamic Route Generation</strong>:</p>
</li>
</ol>
<ul>
<li><p>Named routes are static and defined upfront in your app.</p>
</li>
<li><p>If you need dynamic route generation (e.g., based on user data or external factors), named routes may not be the best fit.</p>
</li>
</ul>
<hr />
<h2 id="heading-3-dynamic-navigation-with-generated-routes"><strong>3. Dynamic Navigation with Generated Routes</strong>:</h2>
<ol>
<li><p><strong>Route Generation Based on Data</strong>:</p>
<ul>
<li><p>Instead of hardcoding routes, you can generate them dynamically based on data from your app.</p>
</li>
<li><p>For example, if you have a list of reports, each with a unique ID, you can create routes for individual report dynamically.</p>
</li>
<li><p>Use a function to generate the route based on the report ID:</p>
<pre><code class="lang-dart">  <span class="hljs-built_in">String</span> generateReportRoute(<span class="hljs-built_in">String</span> reportId) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'/report/<span class="hljs-subst">$reportId</span>'</span>;
  }
</code></pre>
</li>
</ul>
</li>
</ol>
<p><strong>Using</strong><code>onGenerateRoute</code> Callback:</p>
<ul>
<li><p>In your <code>MaterialApp</code>, set the <code>onGenerateRoute</code> callback:</p>
<pre><code class="lang-dart">  MaterialApp(
    onGenerateRoute: (settings) {
      <span class="hljs-keyword">if</span> (settings.name.startsWith(<span class="hljs-string">'/report/'</span>)) {
        <span class="hljs-keyword">final</span> reportId = settings.name.split(<span class="hljs-string">'/'</span>).last;
        <span class="hljs-keyword">return</span> MaterialPageRoute(
          builder: (context) =&gt; ReportScreen(reportId: reportId),
        );
      }
      <span class="hljs-comment">// Handle other routes here...</span>
    },
  )
</code></pre>
<ul>
<li>When a route matches the pattern (e.g., <code>/report/123</code>), create the corresponding screen dynamically.</li>
</ul>
</li>
<li><p><strong>Passing Parameters to Screens</strong>:</p>
<ul>
<li><p>When navigating to dynamically generated routes, pass any necessary parameters (e.g., report ID) to the screen.</p>
</li>
<li><p>Extract the parameters from the route settings and use them to populate the screen content.</p>
</li>
</ul>
</li>
<li><p><strong>Deep Linking and Query Parameters</strong>:</p>
<ul>
<li><p>You can also handle deep linking by parsing query parameters from the route.</p>
</li>
<li><p>For example, if your route is <code>/search?query=flutter</code>, extract the query parameter to perform a search.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-pros-of-dynamic-navigation-and-generated-routes">Pros of Dynamic Navigation and Generated Routes:</h3>
<ol>
<li><p><strong>Adapts to Network Changes</strong>:</p>
<ul>
<li>Dynamic routing automatically adjusts routes when the network changes, such as when new devices connect or existing ones move. This ensures data flows smoothly even in a changing environment.</li>
</ul>
</li>
<li><p><strong>Balances Traffic Loads</strong>:</p>
<ul>
<li>By spreading data across multiple pathways, dynamic routing prevents any single link from becoming congested. This load balancing improves overall network performance.</li>
</ul>
</li>
<li><p><strong>Chooses the Best Path</strong>:</p>
<ul>
<li>Dynamic routing evaluates all possible routes and selects the most efficient one for data to travel. This optimisation results in a quicker and more reliable network.</li>
</ul>
</li>
<li><p><strong>Reduces Manual Configuration</strong>:</p>
<ul>
<li>Dynamic routing reduces the need for network administrators to manually set up routes. It saves time, minimises human error, and simplifies maintenance.</li>
</ul>
</li>
<li><p><strong>Improves Fault Tolerance</strong>:</p>
<ul>
<li>When issues occur (e.g., link failure), dynamic routing quickly finds alternative paths. This fault tolerance helps keep the network operational.</li>
</ul>
</li>
</ol>
<h3 id="heading-cons-of-dynamic-navigation-and-generated-routes">Cons of Dynamic Navigation and Generated Routes:</h3>
<ol>
<li><p><strong>Increased Network Complexity</strong>:</p>
<ul>
<li>Dynamic routing can make the network harder to understand and manage because it constantly changes the paths data takes. Administrators must adapt to these changes.</li>
</ul>
</li>
<li><p><strong>Potential Security Vulnerabilities</strong>:</p>
<ul>
<li>If not secured properly, dynamic routing might expose the network to risks. Routes are shared and updated automatically, which could lead to security vulnerabilities.</li>
</ul>
</li>
<li><p><strong>Higher Resource Consumption</strong>:</p>
<ul>
<li>Routers using dynamic routing require more memory and processing power. This increased resource consumption can make the network more expensive to operate.</li>
</ul>
</li>
<li><p><strong>More Difficult Troubleshooting</strong>:</p>
<ul>
<li>Diagnosing issues becomes challenging because dynamic routes can change. Identifying problems and pinpointing their causes may take more effort.</li>
</ul>
</li>
<li><p><strong>Slower Convergence Times</strong>:</p>
<ul>
<li>After network changes, routers need time to agree on the best paths. During this convergence process, the network might experience slowdowns.</li>
</ul>
</li>
</ol>
<h2 id="heading-deferent-navigator-methods-in-flutter">Deferent Navigator methods in Flutter</h2>
<ol>
<li><p><code>push</code> <strong>and</strong> <code>pop</code>:</p>
<ul>
<li><p><code>push</code>: Adds a new route to the top of the stack, navigating to a new screen.</p>
<ul>
<li><p>Use when moving from one screen to another.</p>
</li>
<li><p>Example: Navigating from a login screen to a home screen.</p>
</li>
</ul>
</li>
<li><p><code>pop</code>: Removes the top route from the stack, returning to the previous screen.</p>
<ul>
<li><p>Use when going back to the previous screen.</p>
</li>
<li><p><strong>Example:</strong> Going back from a details screen to a list screen.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<pre><code class="lang-dart">        <span class="hljs-comment">// Push to a new screen</span>
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) =&gt; <span class="hljs-keyword">const</span> DetailsScreen(),
          ),
        );

        <span class="hljs-comment">// Pop back to the previous screen</span>
        Navigator.of(context).pop();
</code></pre>
<ol start="2">
<li><p><code>pushNamed</code> <strong>and</strong> <code>pop</code>:</p>
<ul>
<li><p><code>pushNamed</code>: Navigates to a named route (previously defined in your app).</p>
<ul>
<li><p>Use when you have named routes (e.g., ‘/home’, ‘/profile’).</p>
</li>
<li><p>Example: Navigating to the profile screen using its route name.</p>
</li>
</ul>
</li>
<li><p><code>pop</code>: Removes the top route from the stack (similar to the previous case).</p>
<pre><code class="lang-dart">  <span class="hljs-comment">// Navigate to a named route</span>
  Navigator.pushNamed(context, <span class="hljs-string">'/profile'</span>);

  <span class="hljs-comment">// Go back to the previous screen</span>
  Navigator.pop(context);
</code></pre>
</li>
</ul>
</li>
<li><p><code>pushReplacement</code>:</p>
<ul>
<li><p>Replaces the current route with a new one.</p>
</li>
<li><p>Useful for scenarios like login/logout flows.</p>
</li>
<li><p><strong>Example:</strong> After successful login, replace the login screen with the home screen.</p>
<pre><code class="lang-dart">  <span class="hljs-comment">// Replace the current screen with a new screen</span>
  Navigator.pushReplacement(
    context,
    MaterialPageRoute(
      builder: (context) =&gt; <span class="hljs-keyword">const</span> NewHomeScreen(),
    ),
  );
</code></pre>
</li>
</ul>
</li>
<li><p><code>popUntil</code>:</p>
<ul>
<li><p>Pops routes until a specified condition is met.</p>
</li>
<li><p>Useful for navigating back to a specific screen.</p>
</li>
<li><p><strong>Example:</strong> Popping all screens until reaching the home screen.</p>
<pre><code class="lang-dart">  <span class="hljs-comment">// Pop until reaching the home screen</span>
  Navigator.popUntil(context, ModalRoute.withName(<span class="hljs-string">'/home'</span>));
</code></pre>
</li>
</ul>
</li>
<li><p><code>pushAndRemoveUntil</code>:</p>
<ul>
<li><p>Pushes a new route and removes all previous routes until a condition is met.</p>
</li>
<li><p>Useful for resetting the navigation stack.</p>
</li>
<li><p><strong>Example:</strong> After successful registration, push the home screen and remove all previous screens.</p>
<pre><code class="lang-dart">  <span class="hljs-comment">// Push home screen and remove all previous screens</span>
  Navigator.pushAndRemoveUntil(
    context,
    MaterialPageRoute(builder: (context) =&gt; <span class="hljs-keyword">const</span> HomeScreen()),
    (route) =&gt; <span class="hljs-keyword">false</span>, <span class="hljs-comment">// Remove all previous routes</span>
  );
</code></pre>
</li>
</ul>
</li>
<li><p><code>pushReplacementNamed</code>:</p>
<ul>
<li><p>Similar to <code>pushReplacement</code>, but navigates to a named route.</p>
</li>
<li><p>Replaces the current route with the specified named route.</p>
</li>
<li><p><strong>Example:</strong> After successful authentication, replace the login screen with the dashboard screen.</p>
<pre><code class="lang-dart">  <span class="hljs-comment">// Replace the current screen with a named route</span>
  Navigator.pushReplacementNamed(context, <span class="hljs-string">'/dashboard'</span>);
</code></pre>
</li>
</ul>
</li>
</ol>
<blockquote>
<p>Remember to choose the appropriate method based on your app’s flow and requirements. Each method serves a specific purpose, allowing you to create seamless navigation experiences for your users!</p>
</blockquote>
<div class="hn-embed-widget" id="linkedinwidget"></div>]]></content:encoded></item><item><title><![CDATA[Creating a VS Code Extension Pack: A Step-by-Step Guide]]></title><description><![CDATA[Visual Studio Code (VS Code) is a powerful and extensible code editor that supports a wide range of programming languages. One of the key features that makes VS Code so popular is its extensibility, allowing developers to enhance their coding experie...]]></description><link>https://blog.joflee.com/creating-a-vs-code-extension-pack-a-step-by-step-guide</link><guid isPermaLink="true">https://blog.joflee.com/creating-a-vs-code-extension-pack-a-step-by-step-guide</guid><category><![CDATA[extension]]></category><category><![CDATA[vscode extensions]]></category><category><![CDATA[VS Code Live Server Extension]]></category><category><![CDATA[vscode]]></category><category><![CDATA[vscode extension pack]]></category><dc:creator><![CDATA[Chintan S]]></dc:creator><pubDate>Wed, 22 Nov 2023 04:23:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750356544296/32eecbe3-b194-4403-8a0f-5ab1a7bddef0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Visual Studio Code (VS Code) is a powerful and extensible code editor that supports a wide range of programming languages. One of the key features that makes VS Code so popular is its extensibility, allowing developers to enhance their coding experience by installing extensions. If you find yourself frequently using a specific set of extensions for your projects, creating a custom extension pack can be a convenient way to share your curated collection with others. In this guide, we'll walk through the process of creating a VS Code extension pack.</p>
<h2 id="heading-step-1-set-up-your-development-environment"><strong>Step 1: Set Up Your Development Environment</strong></h2>
<p>Before you start creating your extension pack, ensure that you have the necessary tools installed:</p>
<ol>
<li><p><strong>Visual Studio Code:</strong> Make sure you have the latest version of VS Code installed on your machine.</p>
</li>
<li><p><strong>Node.js and npm:</strong> Extension development for VS Code relies on Node.js and npm (Node Package Manager). Install them from <a target="_blank" href="https://nodejs.org/">nodejs.org</a>.</p>
</li>
<li><p><strong>Yeoman and the VS Code Extension Generator:</strong> Yeoman is a scaffolding tool, and the VS Code Extension Generator is a Yeoman generator that provides a template for creating VS Code extensions. Install them using the following command:</p>
<pre><code class="lang-bash"> npm install -g yo generator-code
</code></pre>
</li>
</ol>
<h2 id="heading-step-2-create-a-new-extension-pack"><strong>Step 2: Create a New Extension Pack</strong></h2>
<ol>
<li><p>Open a terminal and navigate to the directory where you want to create your extension pack.</p>
</li>
<li><p>Run the following command to generate a new extension pack using Yeoman:</p>
<pre><code class="lang-bash"> yo code
</code></pre>
</li>
<li><p>Choose the "New Extension Pack" option from the menu.</p>
</li>
<li><p>Follow the prompts to provide information about your extension pack, such as the name, description, and publisher.</p>
</li>
</ol>
<h2 id="heading-step-3-customize-your-extension-pack"><strong>Step 3: Customize Your Extension Pack</strong></h2>
<p>Now that you have the basic structure of your extension pack, you can customize it by adding the extensions you want. Open the generated <code>package.json</code> file and locate the <code>"extensionPack"</code> field. This is where you'll list the extensions that should be included in your pack.</p>
<p>For example:</p>
<pre><code class="lang-json"><span class="hljs-string">"extensionPack"</span>: [
    <span class="hljs-string">"author.extension1"</span>,
    <span class="hljs-string">"author.extension2"</span>,
    <span class="hljs-string">"author.extension3"</span>
],
</code></pre>
<p>Replace <code>"author.extension1"</code>, <code>"author.extension2"</code>, etc., with the actual identifiers of the extensions you want to include.</p>
<h2 id="heading-step-4-test-your-extension-pack-locally"><strong>Step 4: Test Your Extension Pack Locally</strong></h2>
<p>Before publishing your extension pack, it's a good idea to test it locally to ensure everything works as expected. Open the command line, navigate to the extension pack directory, and run the following commands:</p>
<pre><code class="lang-bash">npm install
code .
</code></pre>
<p>This will open VS Code with your extension pack loaded. Make sure that all included extensions are functioning correctly together.</p>
<h2 id="heading-step-5-publish-your-extension-pack-optional"><strong>Step 5: Publish Your Extension Pack (Optional)</strong></h2>
<p>If you're satisfied with your extension pack and want to share it with the community, you can publish it to the Visual Studio Code Marketplace. Follow these steps:</p>
<ol>
<li><p><strong>Create a Publisher Account:</strong> Visit the <a target="_blank" href="https://marketplace.visualstudio.com/">Visual Studio Code Marketplace</a> and sign in with your Microsoft account. If you don't have one, you'll need to create a publisher account.</p>
</li>
<li><p><strong>Publish the Extension Pack:</strong></p>
</li>
</ol>
<ul>
<li><p>Open the terminal in the extension pack directory.</p>
</li>
<li><p>Run the following command to publish your extension pack:</p>
</li>
</ul>
<pre><code class="lang-bash">vsce publish
</code></pre>
<p>or you can do this for publish your extension pack</p>
<h2 id="heading-create-a-publisher">Create a publisher</h2>
<p>A <strong>publisher</strong> is an identity that can publish extensions to the Visual Studio Code Marketplace. Every extension needs to include a <code>publisher</code> name in its <code>package.json</code> file.</p>
<p>To create a publisher:</p>
<ol>
<li><p>Go to the <a target="_blank" href="https://marketplace.visualstudio.com/manage">Visual Studio Marketplace publisher management page</a> and Log in with Microsoft account.</p>
</li>
<li><p>Click <strong>Create publisher</strong> in the pane on the left.</p>
</li>
<li><p>In the new page, specify the mandatory parameters for a new publisher - identifier and name (<strong>ID</strong> and <strong>Name</strong> fields respectively):</p>
</li>
</ol>
<ul>
<li><p><strong>ID</strong>: the <strong>unique</strong> identifier for your publisher in Marketplace that will be used in your extension URLs. ID cannot be changed once created.</p>
</li>
<li><p><strong>Name</strong>: the <strong>unique</strong> name of your publisher that will be displayed in Marketplace with your extensions. This can be your company or brand name.</p>
</li>
</ul>
<p>Manually, upload .vsix file to the <a target="_blank" href="https://marketplace.visualstudio.com/manage">Visual Studio Marketplace publisher management page</a>:</p>
<p><img src="https://code.visualstudio.com/assets/api/working-with-extensions/publishing-extension/add-extension.png" alt class="image--center mx-auto" /></p>
<p>Congratulations! You've successfully created and published your VS Code extension pack. Users can now install your extension pack from the Visual Studio Code Marketplace or directly from within the VS Code editor. Keep in mind that you can update your extension pack over time by adding or removing extensions in the <code>package.json</code> file and publishing the updated version.</p>
]]></content:encoded></item><item><title><![CDATA[OneSignal vs. Firebase For Push Notification or Engagement: A Comprehensive Comparison]]></title><description><![CDATA[Push notifications are a vital tool for mobile app engagement and user retention. With push notifications, you can engage your users by sending them timely and relevant messages. However, choosing the right push notification service can be a challeng...]]></description><link>https://blog.joflee.com/onesignal-vs-firebase-for-push-notification-or-engagement-a-comprehensive-comparison</link><guid isPermaLink="true">https://blog.joflee.com/onesignal-vs-firebase-for-push-notification-or-engagement-a-comprehensive-comparison</guid><category><![CDATA[push notifications]]></category><category><![CDATA[Firebase]]></category><category><![CDATA[onesignal]]></category><dc:creator><![CDATA[Sharad P]]></dc:creator><pubDate>Thu, 16 Feb 2023 09:31:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/33oxtOMk6Ac/upload/fae143b5996b4f1e181714bca9ae1f4f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Push notifications are a vital tool for mobile app engagement and user retention. With push notifications, you can engage your users by sending them timely and relevant messages. However, choosing the right push notification service can be a challenging task, especially when you have to compare between two leading platforms like OneSignal and Firebase.</p>
<p>In this article, we will compare OneSignal and Firebase to help you understand which platform is best for your mobile app's push notification needs.</p>
<hr />
<h2 id="heading-onesignal">OneSignal</h2>
<p>OneSignal is a popular push notification service that offers a robust feature set, reliable delivery, and excellent customer support. OneSignal has a straightforward user interface that makes it easy to set up and use.</p>
<p><strong><em>OneSignal's key features include:</em></strong></p>
<ol>
<li><p><strong>Segmentation</strong> - OneSignal allows you to segment your users based on various factors, such as location, device type, and behavior, to send targeted notifications.</p>
</li>
<li><p><strong>Automation</strong> - You can automate your push notification campaigns based on user behavior, such as sending a notification to users who haven't opened the app in a while.</p>
</li>
<li><p><strong>A/B testing</strong> - OneSignal enables you to conduct A/B tests to optimize your push notification campaigns.</p>
</li>
<li><p><strong>Personalization</strong> - You can personalize your notifications by including the user's name or other relevant information.</p>
</li>
<li><p><strong>Delivery and reporting</strong> - OneSignal offers robust delivery and reporting capabilities, including real-time analytics, delivery tracking, and conversion tracking.</p>
</li>
</ol>
<hr />
<h2 id="heading-firebase">Firebase</h2>
<p>Firebase is a comprehensive mobile app development platform that offers a range of services, including push notifications. Firebase's push notification service offers a range of features, including reliable delivery, high-level security, and excellent performance.</p>
<p><strong><em>Firebase's key features include:</em></strong></p>
<ol>
<li><p><strong>FCM</strong> - Firebase Cloud Messaging (FCM) is the core of Firebase's push notification service, providing reliable and scalable delivery across multiple platforms.</p>
</li>
<li><p><strong>Targeting</strong> - Firebase allows you to target your notifications to specific user segments, based on a range of factors, such as location, device type, and user behavior.</p>
</li>
<li><p><strong>Customization</strong> - You can customize your notifications with rich media, such as images and videos, to make them more engaging.</p>
</li>
<li><p><strong>Reporting</strong> - Firebase offers detailed reporting and analytics, including delivery tracking, engagement rates, and user behavior.</p>
</li>
</ol>
<hr />
<h2 id="heading-onesignal-vs-firebase-which-one-to-choose">OneSignal vs. Firebase - Which One to Choose?</h2>
<p>Both OneSignal and Firebase offer robust push notification services with a range of features to engage your users. However, the choice between the two platforms ultimately depends on your specific needs.</p>
<p>If you are looking for a standalone push notification service that is easy to set up and use, OneSignal is an excellent choice. OneSignal's segmentation and automation features make it easy to send targeted notifications and optimize your campaigns.</p>
<p>On the other hand, if you are looking for a comprehensive mobile app development platform, Firebase is the better option. Firebase's push notification service integrates seamlessly with other Firebase services, such as authentication and cloud storage, to provide a complete app development solution.</p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<blockquote>
<p>In conclusion, both OneSignal and Firebase offer excellent push notification services with a range of features to engage your users. The choice between the two platforms ultimately depends on your specific needs, whether you want a standalone push notification service or a comprehensive mobile app development platform. Regardless of which platform you choose, push notifications are a powerful tool for increasing user engagement and retention.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Here is how we manage Flutter project and file architecture at JoFlee]]></title><description><![CDATA[Flutter is most useful in cross-platform technology. Each and every company or individual developer has either created their own way to manage code or just using someone else's patten. We found that file structure is most important for scalability of...]]></description><link>https://blog.joflee.com/flutter-file-architecture</link><guid isPermaLink="true">https://blog.joflee.com/flutter-file-architecture</guid><category><![CDATA[Flutter]]></category><category><![CDATA[BLoC]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[mobile app development]]></category><category><![CDATA[coding]]></category><dc:creator><![CDATA[Neel V.]]></dc:creator><pubDate>Sun, 02 Oct 2022 09:43:10 GMT</pubDate><content:encoded><![CDATA[<h4 id="heading-flutter-is-most-useful-in-cross-platform-technology-each-and-every-company-or-individual-developer-has-either-created-their-own-way-to-manage-code-or-just-using-someone-elses-patten-we-found-that-file-structure-is-most-important-for-scalability-of-the-project">Flutter is most useful in cross-platform technology. Each and every company or individual developer has either created their own way to manage code or just using someone else's patten. We found that file structure is most important for scalability of the project.</h4>
<p>Here are the commonly raised concerns for not using good architecture.</p>
<ul>
<li>Unable to find the specific file project</li>
<li>Code Maintaining Problem</li>
<li>Big Problem in Teamwork</li>
<li>Developers start blaming each other or older developer for even minor change in requirement</li>
</ul>
<h4 id="heading-everyone-has-their-own-architecture-no-one-is-wrong-at-all">Everyone has their own architecture. No one is wrong at all.</h4>
<p>  This is what I suggest and use in our existing projects having 10 people working on the same.</p>
<h4 id="heading-best-naming-pattern-for-folder-and-file">Best naming pattern for folder and file</h4>
<ol>
<li>homeScreen (Camel Case) - suitable for folder name </li>
<li>home_screen.dart (Lower case for all word) - suitable for file name </li>
</ol>
<h2 id="heading-asset">Asset</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664687489233/AhoPAv7cG.png" alt="image.png" />
We can store static asset e.g. app logo, app icon, pdf, video, font and svg file in Assets folder</p>
<h2 id="heading-lib">Lib</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664687989346/dQpM6s5Xl.png" alt="image.png" />
Store all dart file in Library 
    In Lib folder we can create 2 main folders core and module</p>
<h3 id="heading-core">Core</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664694232955/nVl55l0M1.png" alt="image.png" />
    In core folder we can create common, constant, enum,  serverCommunicator, storage, theme, validator folder etc.</p>
<h4 id="heading-common">Common</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664690824037/bXGRUQqeN.png" alt="image.png" />
      In Common folder we can store common widget, screen, function, model and more functionality used more then once </p>
<h4 id="heading-constant">Constant</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664691177488/64IEUevhV.png" alt="image.png" />
    In Constant folder we can store all strings used and our app, api string and asset and network image link</p>
<h4 id="heading-enum">Enum</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664691457208/yh8FzUIxS.png" alt="image.png" />
  We can store all enums in this folder </p>
<h4 id="heading-server-communicator">Server Communicator</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664691590698/i29l2GKhT.png" alt="image.png" />
 In Server Communicator file we can store any network services functionality e.g. HTTP service, Dio Service, Firebase Service and any type of network service</p>
<h4 id="heading-storage">Storage</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664691804068/8BhA8t5s9.png" alt="image.png" /></p>
<p>In Storage folder we can create Local Data Base files and Shared Preferences</p>
<h4 id="heading-theme">Theme</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664692096988/sSJqyVJr3.png" alt="image.png" /></p>
<p>In Theme folder we can Store theme related functionality e.g. separate color file, light and dark theme functionality </p>
<h4 id="heading-validator">Validator</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664692232694/fqlpcV-tM.png" alt="image.png" />
We can store all form validators in this folder </p>
<h3 id="heading-modules">Modules</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664692784753/T0NN8WPcQ.png" alt="image.png" />
We can create our main modules folder in Modules e.g. auth, dashboard and splash...</p>
<h4 id="heading-auth">Auth</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664693965318/IZrijaflZ.png" alt="image.png" />
We can store authentication related data e.g. login, sign-up, forgot password and onboarded screen </p>
<h4 id="heading-dashboard">Dashboard</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664693584824/ZS88wACvS.png" alt="image.png" /></p>
<p>We can store all screen and functionality in dashboard module</p>
<h4 id="heading-splash">Splash</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664693808203/AUOs5wEtI.png" alt="image.png" />
In this folder we can store splash time data loading functionality and splash screen related data </p>
<h5 id="heading-login">Login</h5>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664694349228/rsQEMRj6v.png" alt="image.png" /></p>
<p>In Login Module and same as all basic module we can participant in section first controller second Model and third view</p>
<ol>
<li>Controller 
 In Controller section we can store all login functionality</li>
<li>Model
 In Model section we can store login module</li>
<li>View 
 In View section we can divide in 2 part first one is screen and second one is widget
 a. In screen section we can create screen UI
 b. In Widget section we can store all widget using in screen </li>
</ol>
<h2 id="heading-our-full-folder-architecture">Our Full Folder Architecture</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664694893690/SLGYyy6v-.png" alt="image.png" /></p>
]]></content:encoded></item><item><title><![CDATA[Another list of VSCode Extensions for DailyCode, Git, Flutter and Other]]></title><description><![CDATA[Many of you had already seen few extensions from below list. If you found any extension new & useful, don't forget to like this blog.
I'll categories below extension in ? different sections.
A. VSCode must have extensions
B. Git specific
C. Flutter s...]]></description><link>https://blog.joflee.com/useful-extension-vscode</link><guid isPermaLink="true">https://blog.joflee.com/useful-extension-vscode</guid><category><![CDATA[Flutter]]></category><category><![CDATA[vscode extensions]]></category><category><![CDATA[Git]]></category><category><![CDATA[vscode]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Abhishek P]]></dc:creator><pubDate>Fri, 30 Sep 2022 11:18:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664540264193/NyLahGlU8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Many of you had already seen few extensions from below list. If you found any extension new &amp; useful, don't forget to like this blog.</p>
<p>I'll categories below extension in ? different sections.</p>
<h3 id="heading-a-vscode-must-have-extensions">A. VSCode must have extensions</h3>
<h3 id="heading-b-git-specific">B. Git specific</h3>
<h3 id="heading-c-flutter-specific">C. Flutter specific</h3>
<h3 id="heading-d-other-extras-but-worth-noticing">D. Other extras but worth noticing!</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663957039192/eoqNnyhLP.gif" alt="lets_get_started.gif" /></p>
<h1 id="heading-a-vscode-must-have-extensions">a. VSCode must have extensions</h1>
<h2 id="heading-1-better-commentshttpsmarketplacevisualstudiocomitemsitemnameaaron-bondbetter-comments"><strong>1. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments">Better Comments</a></strong></h2>
<p>The Better Comments extension will help you create more human-friendly comments in your code.
With this extension, you will be able to categorise your annotations into:</p>
<ul>
<li>Alerts</li>
<li>Queries</li>
<li>TODOs</li>
<li>Highlights</li>
<li>Commented out code can also be styled to make it clear the code shouldn't be there</li>
<li>Any other comment styles you'd like can be specified in the settings
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663957500762/hBiyNXO5I.png" alt="better-comments.png" /></li>
</ul>
<h2 id="heading-2-change-casehttpsmarketplacevisualstudiocomitemsitemnamewmaurerchange-case"><strong>2. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=wmaurer.change-case">change-case</a></strong></h2>
<p>Quickly change the case of the current selection or current word.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663957774566/4X2RhSh1V.gif" alt="change_case.gif" /></p>
<h2 id="heading-3-code-spell-checkerhttpsmarketplacevisualstudiocomitemsitemnamestreetsidesoftwarecode-spell-checker"><strong>3. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker">Code Spell Checker</a></strong></h2>
<p>A basic spell checker that works well with code and documents.</p>
<p>The goal of this spell checker is to help catch common spelling errors while keeping the number of false positives low.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663957890048/PDOeUKSky.gif" alt="code_spell_checker.gif" /></p>
<h2 id="heading-4-error-lenshttpsmarketplacevisualstudiocomitemsitemnameusernamehwerrorlens"><strong>4. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens">Error Lens</a></strong></h2>
<p>Improve highlighting of errors, warnings and other language diagnostics.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663958118768/qGgaq4YpB.png" alt="error_lens.png" /></p>
<h2 id="heading-5-settings-synchttpsmarketplacevisualstudiocomitemsitemnameshancode-settings-sync"><strong>5. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync">Settings Sync</a></strong></h2>
<p>Synchronize Settings, Snippets, Themes, File Icons, Launch, Keybindings, Workspaces and Extensions Across Multiple Machines Using GitHub Gist.</p>
<p>It Syncs</p>
<ol>
<li>Settings File</li>
<li>Keybinding File</li>
<li>Launch File</li>
<li>Snippets Folder</li>
<li>VSCode Extensions &amp; Extensions Configurations</li>
<li>Workspaces Folder</li>
</ol>
<h2 id="heading-6-vscode-fakerhttpsmarketplacevisualstudiocomitemsitemnamedeerawanvscode-faker"><strong>6. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=deerawan.vscode-faker">vscode-faker</a></strong></h2>
<p>Generate fake data for name, address, lorem ipsum, commerce and much more</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663958311934/8W6qBebIr.gif" alt="vscode-faker.gif" /></p>
<h2 id="heading-7-wakatimehttpsmarketplacevisualstudiocomitemsitemnamewakatimevscode-wakatime"><strong>7. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime">WakaTime</a></strong></h2>
<p>WakaTime is an open source VS Code plugin for metrics, insights, and time tracking automatically generated from your programming activity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663958399074/1EJB4nYc1.png" alt="wakatime.png" /></p>
<h1 id="heading-b-git-specific">b. git specific</h1>
<h2 id="heading-1-git-graphhttpsmarketplacevisualstudiocomitemsitemnamemhutchiegit-graph"><strong>1. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph">Git Graph</a></strong></h2>
<p>View a Git Graph of your repository, and easily perform Git actions from the graph. Configurable to look the way you want!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663958749581/Yv8KaF_pJ.gif" alt="git_graph.gif" /></p>
<h2 id="heading-2-git-historyhttpsmarketplacevisualstudiocomitemsitemnamedonjayamannegithistory"><strong>2. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory">Git History</a></strong></h2>
<ul>
<li>View and search git log along with the graph and details.</li>
<li>View a previous copy of the file.</li>
<li>View and search the history</li>
<li>Compare branches</li>
<li>Compare commits</li>
<li>Compare files across commits</li>
</ul>
<h2 id="heading-3-gitlenshttpsmarketplacevisualstudiocomitemsitemnameeamodiogitlens"><strong>3. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens</a></strong></h2>
<p>GitLens simply helps you better understand code. Quickly glimpse into whom, why, and when a line or code block was changed. Jump back through history to gain further insights as to how and why the code evolved. Effortlessly explore the history and evolution of a codebase.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663958986853/DQHd9CMh9.png" alt="gitLens.png" /></p>
<h2 id="heading-4-markdown-all-in-onehttpsmarketplacevisualstudiocomitemsitemnameyzhangmarkdown-all-in-one"><strong>4. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one">Markdown All in One</a></strong></h2>
<p>To create the markdown file, this extension will boost your speed by 1000%.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663959251972/Xsuz_hlqN.gif" alt="markdownallinone.gif" /></p>
<h2 id="heading-5-markdownlinthttpsmarketplacevisualstudiocomitemsitemnamedavidansonvscode-markdownlint"><strong>5. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint">markdownlint</a></strong></h2>
<p>The Markdown markup language is designed to be easy to read, write, and understand. It succeeds - and its flexibility is both a benefit and a drawback. Many styles are possible, so formatting can be inconsistent. Some constructs don't work well in all parsers and should be avoided</p>
<p>markdownlint is an extension for the Visual Studio Code editor that includes a library of rules to encourage standards and consistency for Markdown files.</p>
<h1 id="heading-c-flutter-specific">c. flutter specific</h1>
<p>Of course, this are the must have flutter vscode extensions:</p>
<ul>
<li><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter">Flutter</a></li>
<li><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code">Dart</a></li>
<li><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Nash.awesome-flutter-snippets">Awesome Flutter Snippets</a></li>
</ul>
<p>Apart from this there are some good flutter vscode extensions.</p>
<h2 id="heading-1-blochttpsmarketplacevisualstudiocomitemsitemnamefelixangelovbloc"><strong>1. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=FelixAngelov.bloc">Bloc</a></strong></h2>
<p>Support for the bloc library and provides tools for effectively creating blocs for both Flutter and AngularDart apps.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663959807116/24N6281yU.gif" alt="bloc.gif" /></p>
<h2 id="heading-2-dart-barrel-file-generatorhttpsmarketplacevisualstudiocomitemsitemnamemiquelddgdart-barrel-file-generator"><strong>2. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=miquelddg.dart-barrel-file-generator">Dart Barrel File Generator</a></strong></h2>
<p>VSCode extension that generate barrel files for folders containing dart files.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663959878342/FchkpTmhU.gif" alt="barrle_file_generator.gif" /></p>
<h2 id="heading-3-dart-importhttpsmarketplacevisualstudiocomitemsitemnameluanpotterdart-import"><strong>3. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=luanpotter.dart-import">dart-import</a></strong></h2>
<p>Fix Dart/Flutter's imports.</p>
<p>A simple plugin for VSCode to change all Dart/Flutter imports to relative format</p>
<h2 id="heading-4-flutter-widget-snippetshttpsmarketplacevisualstudiocomitemsitemnamealexisvtflutter-snippets"><strong>4. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=alexisvt.flutter-snippets">Flutter Widget Snippets</a></strong></h2>
<p>A set of helpful widget snippets for day to day Flutter development.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960034761/VcV4JbWXV.gif" alt="flutter-snippet-sample.gif" /></p>
<h2 id="heading-5-gradle-language-supporthttpsmarketplacevisualstudiocomitemsitemnamenaco-sirengradle-language"><strong>5. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=naco-siren.gradle-language">Gradle Language Support</a></strong></h2>
<p>An extension to provide Gradle language support for Visual Studio Code, including advanced functionalities like Syntax Highlighting, Keyword Auto-completion Proposals and Duplication Validation.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960161668/1IyCuHwog.png" alt="gradle.png" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960165686/5Gh01yNIJ.png" alt="gradle2.png" /></p>
<h2 id="heading-6-version-lenshttpsmarketplacevisualstudiocomitemsitemnamepflanneryvscode-versionlens"><strong>6. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=pflannery.vscode-versionlens">Version Lens</a></strong></h2>
<p>Shows the latest version for each package using code lens</p>
<p>This extension shows version information when opening a package or project for one of the following:</p>
<ul>
<li><a target="_blank" href="https://www.dotnetfoundation.org/">dotnet</a></li>
<li><a target="_blank" href="https://code.dlang.org/">dub</a></li>
<li><a target="_blank" href="https://jspm.io/">jspm</a></li>
<li><a target="_blank" href="https://maven.apache.org/">maven</a></li>
<li><a target="_blank" href="https://www.npmjs.com/">npm</a></li>
<li><a target="_blank" href="https://pub.dev/">pub</a></li>
<li><a target="_blank" href="https://getcomposer.org/">composer</a></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960281283/R-GsxMuqk.gif" alt="version_lens.gif" /></p>
<h1 id="heading-d-other-extras">d. other extras</h1>
<h2 id="heading-1-image-previewhttpsmarketplacevisualstudiocomitemsitemnamekisstkondorosvscode-gutter-preview"><strong>1. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=kisstkondoros.vscode-gutter-preview">Image preview</a></strong></h2>
<p>Shows image preview in the gutter and on hover</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960576755/AgEycphcT.png" alt="Image preview.png" /></p>
<h2 id="heading-2-material-icon-themehttpsmarketplacevisualstudiocomitemsitemnamepkiefmaterial-icon-theme"><strong>2. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme">Material Icon Theme</a></strong></h2>
<p>Get the Material Design icons into your VS Code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960669379/2Aat2NhUJ.png" alt="fileIcons.png" /></p>
<h2 id="heading-3-thunder-clienthttpsmarketplacevisualstudiocomitemsitemnamerangavvscode-thunder-client"><strong>3. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client">Thunder Client</a></strong></h2>
<p>Thunder Client is a lightweight Rest API Client Extension for Visual Studio Code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663960735558/1PkXhR0nH.gif" alt="thunder-client.gif" /></p>
<h2 id="heading-4-tabouthttpsmarketplacevisualstudiocomitemsitemnamealberttabout"><strong>4. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=albert.TabOut">TabOut</a></strong></h2>
<p>Tab out of quotes, brackets, etc for Visual Studio Code.</p>
]]></content:encoded></item></channel></rss>