<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Bhavesh Mankar on Medium]]></title>
        <description><![CDATA[Stories by Bhavesh Mankar on Medium]]></description>
        <link>https://medium.com/@bhaweshmankar2002?source=rss-6a5e7cbb735d------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*M_Egh7DXdbOTZ6hOLkmyIQ.jpeg</url>
            <title>Stories by Bhavesh Mankar on Medium</title>
            <link>https://medium.com/@bhaweshmankar2002?source=rss-6a5e7cbb735d------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 05:21:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@bhaweshmankar2002/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The SQLite Wars: Sqlite3 vs Sqflite]]></title>
            <link>https://medium.com/@bhaweshmankar2002/the-sqlite-wars-sqlite3-vs-sqflite-e8f3d970cca0?source=rss-6a5e7cbb735d------2</link>
            <guid isPermaLink="false">https://medium.com/p/e8f3d970cca0</guid>
            <category><![CDATA[productivity]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Bhavesh Mankar]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 13:22:48 GMT</pubDate>
            <atom:updated>2026-11-04T13:22:48.210Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8zN9g3pvo2Tuzjj0hqH31w.jpeg" /><figcaption>Cover image for the article with a confused dash - which package to choose, sqlite3 or sqflite</figcaption></figure><blockquote>When we were building <a href="https://cenlo.app">Cenlo</a> we needed a local database that could run seamlessly across desktop platforms. We had to make a choice between sqlite3 and sqflite. In this guide, I’ll break down the core differences between them, how they work under the hood, and the patterns we used to make our choice.</blockquote><h4>Contents</h4><ol><li><a href="#4f32">What is SQLite?</a></li></ol><p>2. <a href="#bd45">The Flutter way of implementing SQLite</a></p><p>3. <a href="#76ae">The two approaches</a></p><p>5. <a href="#ea41">How to choose</a></p><p>6. <a href="#5dc1">Closing statement</a></p><p>7. <a href="#c495">About the Author</a></p><h4>What is SQLite?</h4><p>SQLite is a <strong>relational database engine</strong> that gets embedded into an application’s code and runs without a server unlike other relational databases like PostgreSQL or MySQL. The actual data, tables, schemas etc. live inside a <strong>single portable binary</strong> (either .sqlite or .db) making it a perfect for running lightweight applications on <strong>edge hardware</strong>.</p><blockquote><em>Fact: SQLite is the most deployed pieces of software with an estimated trillion active sqlite db instances running across all major operating systems. (</em><a href="https://www.sqlite.org/mostdeployed.html"><em>Source</em></a><em>)</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*pD4mmFSPDJ9CndfnX4kihA.jpeg" /></figure><p>Despite all the advantages of SQLite its biggest <strong>architectural drawback</strong> is <strong>one write at a time</strong>. Though, over the years the SQLite has had support for <a href="https://www.sqlite.org/wal.html">WAL</a> (Write Ahead Logging) mode allowing for <strong>multiple readers</strong> but the <a href="https://sqlite.org/forum/info/6ed6824176b9c773">single write constraint</a> remains. None the less, that is not a problem when running SQLite locally which is exactly what this article is intended towards. <strong>Why is it not a problem running it locally?</strong> Well, when running locally it only processes actions from <strong>one device compared</strong> to thousands or millions of devices for applications running on <strong>client-server architecture.</strong> SQLite can handle such transactions with ease with extremerely rare occurences of hitting nail in the head.</p><blockquote>Note: SQLite is also available for server and intrested readers can check out projects like <a href="https://turso.tech/">turso</a> or <a href="https://pocketbase.io/">pocketbase</a> (I intend to write about PocketBase in future).</blockquote><h4>The flutter way of implementing SQLite?</h4><p>Coming towards Flutter implementing sqlite is easy. Install a package, initialize it in the main function, configure and you should be good to go. But thats not all, choosing which package to install is also important as there are <strong>multiple choices</strong> and these work differently under the hood. Two of them stand out particularly as these are the <strong>most prominent</strong> and <strong>well maintained</strong> namely <a href="https://pub.dev/packages/sqlite3">sqlite3</a> and <a href="https://pub.dev/packages/sqflite">sqflite</a>. Both of them have their own approaches towards how they integrate SQLite engine, their API’s, encryption support and most importantly support for asynchronous code execution.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*lddJVTI1gIJINKg3Sn_kRw.jpeg" /><figcaption>Two buttons meme, choosing local database for flutter</figcaption></figure><p>We will look into both of them and by the end of this article you should be able to choose which one to use for your local-first flutter app (At least I hope so…)</p><h4>The two approaches</h4><p><a href="https://pub.dev/packages/sqflite"><strong>sqflite</strong></a>: Sqflite is one of the most popular packages in the flutter ecosystem due to its <strong>developer friendly API</strong> and its <strong>asynchronous</strong>, <strong>non-blocking</strong> nature making it <strong>easier to integrate</strong>. It is <strong>beginner friendly</strong> and is a great choice if your application targets mobile operating systems i.e <strong>Android and iOS</strong>.</p><p>Under the hood, it utilizes <a href="https://docs.flutter.dev/platform-integration/platform-channels">platfrom channels</a> to interact with the underlying SQLite engine. Due to its reliance on platform channels, bindings must be <strong>implemented</strong> and <strong>maintained separately</strong> for each operating system.</p><p>This <em>architectural descision</em> is responsible for the added <strong>friction</strong> when adding support for new platforms, which is why <strong>sqflite</strong> is <strong>currently limited</strong> to <strong>Android, iOS, and macOS</strong> only.</p><p><a href="https://pub.dev/packages/sqlite3"><strong>sqlite3</strong></a>: Sqlite3, on the other hand uses <a href="https://dart.dev/interop/c-interop">Dart’s FFI (Foreign Function Interface)</a> allowing it to run natively across <strong>all platforms</strong> (even on the web via <a href="https://docs.flutter.dev/platform-integration/web/wasm">WASM</a>). It is a wrapper over the <strong>native C bindings</strong> of SQLite making it <strong>highly performant</strong> and <strong>maintainable</strong>.</p><p>One <strong>drawback</strong> of the sqlite3 plugin is that it lacks asynchronous support out of the box, though this can be easily resolved by pairing it with the <a href="https://pub.dev/packages/sqlite_async">sqlite_async</a> package. This does introduces some fiction as additonal configuration is required but is totally worth it if your application targets both <strong>desktop</strong> (Windows, macOS, Linux) and <strong>mobile</strong> (Android, iOS) or <strong>Web</strong>.</p><p>Its biggest downside, however, is its API , which prefers writing direct, <strong>raw SQL strings</strong> unlike sqflite, which uses of type-safe programmatic interface. This grants developers <strong>great level of control</strong> over the database execution, but is <strong>not developer friendly</strong>.</p><p>Its <strong>low level API</strong> is a big reason why sqlite3 is <strong>rarely used directly</strong> in flutter applications, instead it is used as a dependency of a highly popular ORM (Object Relational Mapper) <a href="https://pub.dev/packages/drift">Drift</a>. (I intend to write about drift soon)</p><h4>How to choose</h4><p>If you ask me performance is not the reason I will make my choice, as forementioned in the article, a local-first application involves only a single device reading and writing to its own storage, data operations will still be exceptionally fast and transactions happen in the blink of an eye.</p><p>It actuallty narrows down to <strong>target platforms, ease of integration, and the level of control</strong> the developer requires.</p><p><strong>1. Targeting by Platform (A Straightforward Choice)</strong></p><ul><li><strong>Mobile-Only (Android &amp; iOS):</strong> Go with sqflite. It is easy to integrate, async by default and is battle-tested for mobile.</li><li><strong>Desktop or Cross-Platform (Desktop + Mobile):</strong> Go with sqlite3. Its Dart FFI layer ensures your database logic works seamlessly across Windows, Linux, and macOS without platform-specific friction.</li><li><strong>Web Integration:</strong> sqlite3 is your only choice.</li></ul><p><strong>2. Balancing Control and Integration: Getting the Best of Both Worlds</strong></p><p>The ultimate dilemma lies in balancing <strong>ease of integration</strong> with <strong>granular control</strong>:</p><ul><li>sqflite gives you immediate ease of integration with its safe, async, programmatic API—but bounds you to mobile platforms.</li><li>sqlite3 gives you absolute control over raw SQL and universal platform support—but forces you into a rigid, low-level, synchronous developer experience.</li></ul><p>To truly get the best of both worlds make use of an ORM like <a href="https://pub.dev/packages/drift">drift</a>. It builds upon <a href="https://pub.dev/packages/sqlite3">sqlite3</a> and provides access to safe, developer-friendly, and non-blocking asynchronous API associated with sqflite ensuring smooth UI performance across all platforms. At the same time, it provides direct control over the database by allowing the execution of raw SQL strings using the customStatement API whenever complex or highly optimized queries are required.</p><blockquote>We leveraged <strong>Drift + sqlite3</strong> architecture when building<em> </em>local storage for <em>Cenlo</em>. It provided type safe api generation via <a href="https://pub.dev/packages/build_runner">build runner</a>, execution of raw sql strings for higher degree of control and compatibility across platforms.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*hKyWFbZH5mkMpsYI28Hakw.jpeg" /><figcaption>Choosing Drift, getting best of both worlds</figcaption></figure><h4>Closing statement</h4><p>I hope this article clarifies the core differences between sqlite3 and sqflite, helping you choose the right fit for your next project. More importantly, the goal is to help developers shift their perspective away from database plugins or benchmarks toward high-level ecosystems like <a href="https://pub.dev/packages/drift"><strong>Drift</strong></a><strong> </strong>which offer a more scalable architecture for modern, cross-platform apps.</p><p>With that, I will take your leave. Please drop your feedback or questions in the comment section below . I’d love to hear which database approach you are using for your apps! Thanks for reading the article, and I will see you in the next one.</p><h4><strong><em>About the Author</em></strong></h4><blockquote>Hi I am <a href="https://www.linkedin.com/in/bhavesh-mankar-73530a233/">Bhavesh</a>, I am a Software Engineer and Flutter Developer. I love working with Flutter for developing cross platform apps. Its been a few months since I am working on <a href="https://cenlo.app">Cenlo</a>, its a time tracking application that is local first and uses machine learning models to classify user activity. I am passionate about local-first architecture, privacy-centric software and I enjoy exploring the evolving landscape of edge computing.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e8f3d970cca0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Local Persistant Storage in Flutter : Getting Started with HiveDB]]></title>
            <link>https://medium.com/@bhaweshmankar2002/local-persistant-storage-in-flutter-getting-started-with-hivedb-71f836eef438?source=rss-6a5e7cbb735d------2</link>
            <guid isPermaLink="false">https://medium.com/p/71f836eef438</guid>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[nosql]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[hive]]></category>
            <dc:creator><![CDATA[Bhavesh Mankar]]></dc:creator>
            <pubDate>Wed, 02 Oct 2024 11:57:46 GMT</pubDate>
            <atom:updated>2024-10-02T12:09:47.215Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4HAnr3sCU1N5kTs4" /><figcaption><a href="https://unsplash.com/photos/a-bunch-of-bees-that-are-on-a-beehive-DIwC450lRGI">https://unsplash.com/photos/a-bunch-of-bees-that-are-on-a-beehive-DIwC450lRGI</a></figcaption></figure><h3>Local Persistant Storage in Flutter : Getting Started with HiveDB</h3><p>Store data with local persistance in flutter apps with the help of hive database.</p><p><a href="https://github.com/isar/hive">GitHub - isar/hive: Lightweight and blazing fast key-value database written in pure Dart.</a></p><h3>Overview</h3><p>I have used <strong>HiveDB</strong> for local persistant storage in many of my apps and it is one of my favorite storage soutions when it comes to ease of use. I recently noticed that the docs for HiveDB are down and are not accessible. The pacakge is not maintained anymore and was last updated <strong>2 years ago(</strong>as of Oct 2024<strong>), </strong>yet has a popularity score of 100% and is one of the most used databases for local persistance storage.</p><p>Since the docs are down I thought of writing a <strong>Getting Started</strong> guide for <strong>HiveDB</strong>.</p><p>We will discuss about the following in this article</p><ol><li>What is <strong>Hive</strong> Database?</li><li><strong>Getting Started</strong> with Hive</li><li><strong>Basic</strong> Hive Concepts</li><li>Generating <strong>TypeAdapters</strong></li><li>Creating a simple <strong>Todo List app</strong></li><li><strong>Conclusion</strong></li></ol><h3>What is Hive Database?</h3><p>Hive is a <strong>lightweight</strong>, efficient <strong>NoSQL</strong> database specifically designed for <strong>local persistent storage</strong> for Flutter applications. It is a <strong>high performant </strong>database with a <strong>key-value</strong> storage model. This makes it appropriate for use in scenerios were quick data retrival is crucial. Hive allows creation of someting called as <strong>TypeAdapters</strong> which allow storing any type object, in addition to the primitive data types like List, Map, DateTime, BigInt and Uint8List that hive supports out of the box.</p><h3>Getting Started with Hive</h3><p>In order to use hive we need to add the following dependencies to the pubspec.yaml file</p><pre>dependencies:<br>  analyzer: ^4.7.0<br>  flutter:<br>    sdk: flutter<br>  #hiveDB related packages<br>  hive: ^2.2.3<br>  hive_flutter: ^1.1.0<br><br>dev_dependencies:<br>  flutter_test:<br>    sdk: flutter<br>  #package to generate dart code<br>  build_runner: ^2.4.8<br>  #hive generator for generating type adapters<br>  hive_generator: ^1.1.0</pre><p>Let’s first go through the dependencies</p><ol><li><a href="https://pub.dev/packages/hive">hive</a>: The package required to use hive database</li><li><a href="https://pub.dev/packages/hive_flutter">hive_flutter</a>: This package makes it easier to use hive in flutter apps.</li><li><a href="https://pub.dev/packages/build_runner">build_runner</a>: This package is used to generate dart code.</li><li><a href="https://pub.dev/packages/hive_generator">hive_generator</a>: This package is used to generate TypeAdapters which can be stored in hive boxes.</li></ol><p>Next, we need to initialize hive in the main function</p><pre>import &#39;package:flutter/material.dart&#39;;<br>import &#39;package:hive_flutter/hive_flutter.dart&#39;;<br><br>void main() async {<br>  //Initialize Hive<br>  await Hive.initFlutter();<br>  runApp(const MainApp());<br>}</pre><p>This will initialize hive and will store the hive box in a valid directory which changes based on the operating system. By default, Hive is initialized in the <strong>application documents directory</strong>. We can optionally specify the directory in which Hive should be initialized.</p><pre>await Hive.initFlutter(&#39;path_to_directory&#39;);</pre><p>This is all the setup needed in order to start using hive. Just add all the required dependencies and use await Hive.initFlutter(); to initialize hive, that’s it.</p><h3>Basic Hive <strong>Concepts</strong></h3><p>In order to work with hive we need to know about some basic hive concepts.</p><h4><strong>Hive Boxes</strong></h4><p>Hive stores data inside boxes in the form of key-value pair. We first need to open a hive box before storing any data. This can be done by using the openBox command</p><pre>var todosBox = await Hive.openBox(&#39;todos&#39;);</pre><p>This will create a hive box where we can store our data. If we navigate to the directory where we have initilized Hive we will notice a new file by the name of <strong>todos.hive. </strong>For example, if a windows application is launched, then the box files by the extension of <strong>.hive</strong> can be found in <strong>Documents dir</strong>. This is the file which will store all the key-value pairs.</p><h4>Operations on Hive Boxes</h4><p>We can easily perform <strong>CRUD(Create, Read, Update, Delete)</strong> operations on Hive Boxes.</p><pre>  //Initialize Hive<br>  await Hive.initFlutter();<br><br>  // Open the box after initializing Hive<br>  var todosBox = await Hive.openBox(&#39;todos&#39;);<br>  <br>  //CREATE<br>  // Store title using put method by specifying the key and value to store<br>  todosBox.put(&#39;title&#39;, &#39;Football Match&#39;);<br>  debugPrint(&#39;Length: ${todosBox.length}&#39;);<br>  <br>  //READ<br>  // Retrive the title using get method by specifying the key<br>  var title = todosBox.get(&#39;title&#39;);<br>  debugPrint(&#39;Title : $title&#39;);<br><br>  //UPDATE<br>  // Update the title using the put method by specifying the key<br>  todosBox.put(&#39;title&#39;, &#39;Grocery Shopping&#39;);<br>  // Retrive the updated title using get by specifying the key<br>  title = todosBox.get(&#39;title&#39;);<br>  debugPrint(&#39;Updated title: $title&#39;);<br><br>  //DELETE<br>  // delete data using delete by specifying the key<br>  todosBox.delete(&#39;title&#39;);<br>  debugPrint(&#39;Length: ${todosBox.length}&#39;);</pre><p>We have performed <strong>CRUD</strong> operations on todosBox using <strong>put()</strong>, <strong>get()</strong> and <strong>delete()</strong> methods. These methods all have a required parameter ‘<strong>key</strong>’ which is used to store and access data.</p><p>Hive also supports auto incrementing keys which allows storing data without specifying ‘key’, instead hive automatically generates incrementing indices and provides methods like <strong>add()</strong>, <strong>getAt()</strong>, <strong>putAt()</strong> and <strong>deleteAt() </strong>for <strong>CRUD </strong>operations.</p><p>We can strore any data in hive as long as we are storing primitive data types, but that’s no fun. What if we want to store some custom data? This is were TypeAdapters come into paly.</p><h3>Generating <strong>TypeAdapters</strong></h3><p>Hive allows storing custom objects with the help of TypeAdapters, but creating TypeAdapters requires boilerplate code. Fortunately, we can make use of build_runner package to generate TypeAdapters with the help of hive_generator.</p><p>In order to generate <strong>TypeAdapers</strong> we need to do some setup. Let’s look at todo_model.dart.</p><pre>//FILE: todo_model.dart<br>import &#39;package:hive/hive.dart&#39;;<br><br>//specify name of the part file which will be generated by hive_generator<br>part &#39;todo_model.g.dart&#39;;<br> <br>//Specify the HiveType with a type id between 0 and 233<br>//NOTE: No two TypeAdapters can have the same typeId<br>@HiveType(typeId: 0)<br>class Todo {<br>  //Annotate class members with HiveField with each <br>  //HiveField having incrementing index<br>  @HiveField(0)<br>  String title;<br>  @HiveField(1)<br>  String description;<br>  <br>  //Default value can be specified if needed<br>  @HiveField(3, defaultValue: false)<br>  bool isDone;<br>  <br>  Todo({<br>    required this.title,<br>    required this.description,<br>    this.isDone = false,<br>  });<br>}</pre><p>Now, let’s breakdown the structure of todo_model.dart</p><p><strong>First</strong>: We have specified the name that the generated file will have as <strong>todo_model.g.dart</strong> here ‘g’ specifies that it is a generated file.</p><p><strong>Second</strong>: We have annotated the class with HiveType which takes an index. Every single class annotated with HiveType should have a <strong>unique index </strong>between <strong>0 to 233</strong></p><p><strong>Third</strong>: We have annotated the class members with <strong>HiveField</strong> each having a incrementing index. Also we have specified <strong>defaultValue</strong> for isDone so if no value is provided isDone will default to false. HiveField can have a <strong>range of 0 to 255</strong></p><p>That’s is thats all the code that we need for generating a TypeAdapter . Now, in order to generate a TypeAdapter type the below command in terminal window.</p><pre>dart run build_runner build</pre><p>After executing the command a file named <strong>todo_model.g.dart</strong> will be generated in the same folder as todo_model.dart . This file will contain the code generated by <strong>hive_generator</strong> package.</p><p>The file will have a class named <strong>TodoAdapter</strong> extending <strong>TypeAdapter&lt;Todo&gt;</strong> and all the <strong>logic</strong> that is required to <strong>convert data from and to binary</strong>. We do not need to know any more information about this file.</p><p>Next Up, in order to use the <strong>TypeAdapter</strong>, we first need to register it.</p><pre>void main() async {<br>  //Initialize Hive  <br>  await Hive.initFlutter();<br>  //Register a TypeAdapter<br>  Hive.registerAdapter(TodoAdapter());<br>}</pre><p>Now, that we have learned about all basic concepts of Hive and can kick things off by looking at a Todo List app.</p><h3>Creating a simple <strong>Todo List app</strong></h3><p>The Todo List app will have a <strong>HomePage</strong> widget with a <strong>ListView</strong> and a <strong>FloatingActionButton</strong>.</p><ol><li>The initStatemethod creates a box called as todosBoxand calls loadTodos.The loadTodos function fetches the list of todos stored in the todosBox. After fetching the Todo items from the box it parses it as list and stores in todosList</li><li>If the<strong> </strong>todosList<strong> </strong>is not empty then<strong> List View </strong>will return <strong>ListTiles. </strong>Each<strong> ListTile </strong>will have its</li></ol><ul><li>title as <strong><em>title</em></strong> of the Todo,</li><li>subtitle as the <strong><em>description</em></strong> of Todo and a</li><li><strong>Checkbox</strong> as the traling widget which will track <strong><em>isDone</em></strong> property of Todo</li><li><strong>IconButton</strong> as leading widget for deleting the Todo</li></ul><p>3. On pressing the <strong>FloatingActionButton </strong>a <strong>SimpleDialog</strong> will be displayed with two <strong>TextFields</strong> for title and description of the Todo. Pressing <strong>ADD </strong>will add the Todo to the todosBox.</p><p>4. <strong>CRUD</strong> operations are handelled by these function functions</p><ul><li><strong>addTodo()</strong>: The addTodo function takes a Todo object as a parameter and adds it to todosBox. If the addition is successful, it updates the todosList by appending the newly created todo.</li><li><strong>deleteTodo()</strong>: The deleteTodo function provides users with the ability to remove completed or unnecessary todo items from their lists. It accepts an index and attempts to delete the corresponding todo from the todosBox. If successful, it updates the todosList to remove the deleted item.</li><li><strong>changeCompletionStatus(): </strong>The changeCompletionStatus accepts an index and a boolean value, this function enables users to toggle the completion status of a specific todo item. When called, it retrieves the todo from the Hive database, updates its isDone property according to the provided value, and saves the modified todo back into the todosBox. This function also updates the todosList to reflect the change.</li></ul><p>Here is the complete code:</p><pre>//FILE: todo_model.dart<br>import &#39;package:hive/hive.dart&#39;;<br><br>//specify name of the part file which will be generated by hive_generator<br>part &#39;todo_model.g.dart&#39;;<br> <br>//Specify the HiveType with a type id between 0 and 233<br>//NOTE: No two TypeAdapters can have the same typeId<br>@HiveType(typeId: 0)<br>class Todo {<br>  //Annotate class members with HiveField with each <br>  //HiveField having incrementing index<br>  @HiveField(0)<br>  String title;<br>  @HiveField(1)<br>  String description;<br>  <br>  //Default value can be specified if needed<br>  @HiveField(3, defaultValue: false)<br>  bool isDone;<br>  <br>  Todo({<br>    required this.title,<br>    required this.description,<br>    this.isDone = false,<br>  });<br>}<br><br>///FILE: main.dart<br>/// The main function initializes Hive and runs the Flutter application.<br>import &#39;package:flutter/material.dart&#39;;<br>import &#39;package:hive_flutter/hive_flutter.dart&#39;;<br>import &#39;package:lib/todo_model.dart&#39;;<br>void main() async {<br>  await Hive.initFlutter();<br>  Hive.registerAdapter(TodoAdapter());<br>  await Hive.openBox&lt;Todo&gt;(&#39;todos&#39;);<br><br>  runApp(const MyApp());<br>}<br><br>class MyApp extends StatelessWidget {<br>  const MyApp({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return const MaterialApp(<br>      home: HomePage(),<br>    );<br>  }<br>}<br><br>class HomePage extends StatefulWidget {<br>  const HomePage({super.key});<br><br>  @override<br>  State&lt;HomePage&gt; createState() =&gt; _HomePageState();<br>}<br><br>class _HomePageState extends State&lt;HomePage&gt; {<br>  late Box&lt;Todo&gt; todosBox;<br>  List&lt;Todo&gt; todosList = [];<br>  bool isLoading = true;<br><br>  final TextEditingController _titleController = TextEditingController();<br>  final TextEditingController _descriptionController = TextEditingController();<br><br>  @override<br>  void initState() {<br>    super.initState();<br>    todosBox = Hive.box&lt;Todo&gt;(&#39;todos&#39;);<br>    loadTodos();<br>  }<br><br>  /// Loads todos from the Hive box and stores it in todosList.<br>  Future&lt;void&gt; loadTodos() async {<br>    setState(() {<br>      isLoading = true;<br>    });<br>    todosList = todosBox.values.toList();<br>    setState(() {<br>      isLoading = false;<br>    });<br>  }<br><br>  /// Adds a new todo to the Hive box and updates the todosList.<br>  Future&lt;void&gt; addTodo({<br>    required Todo todo,<br>  }) async {<br>    try {<br>      await todosBox.add(todo);<br>      setState(() {<br>        todosList.add(todo);<br>      });<br>      debugPrint(&#39;${todo.title} added successfully&#39;);<br>    } catch (e) {<br>      debugPrint(&#39;Error: $e&#39;);<br>    }<br>  }<br><br>  /// Deletes a todo from the Hive box and updates the todosList.<br>  Future&lt;void&gt; deleteTodo(int index) async {<br>    try {<br>      await todosBox.deleteAt(index);<br>      setState(() {<br>        todosList.removeAt(index);<br>      });<br><br>      debugPrint(&#39;Todo Removed Successfully&#39;);<br>    } catch (e) {<br>      debugPrint(&#39;Error: $e&#39;);<br>    }<br>  }<br><br>  /// Changes the completion status of a todo and updates the todosList.<br>  Future&lt;void&gt; changeCompletionStatus(int index, {required bool value}) async {<br>    try {<br>      Todo todo = todosBox.getAt(index)!;<br>      todo.isDone = value;<br>      await todosBox.putAt(index, todo);<br>      setState(() {<br>        todosList[index] = todo;<br>      });<br>      debugPrint(&#39;${todo.title} marked ${value ? &#39;complete&#39; : &#39;incomplete&#39;}&#39;);<br>    } catch (e) {<br>      debugPrint(&#39;Error: $e&#39;);<br>    }<br>  }<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return Scaffold(<br>      appBar: AppBar(<br>        title: const Text(&#39;Todo List&#39;),<br>      ),<br>      body: isLoading<br>          ? const Center(<br>              child: CircularProgressIndicator(),<br>            )<br>          : todosList.isEmpty<br>              ? const Center(<br>                  child: Text(&quot;No Todos Created&quot;),<br>                )<br>              : ListView.builder(<br>                  shrinkWrap: true,<br>                  itemCount: todosList.length,<br>                  itemBuilder: (context, index) {<br>                    return ListTile(<br>                      title: Text(<br>                        todosList[index].title,<br>                        style: TextStyle(<br>                          decoration: todosList[index].isDone<br>                              ? TextDecoration.lineThrough<br>                              : null,<br>                        ),<br>                      ),<br>                      subtitle: Text(<br>                        todosList[index].description,<br>                        style: TextStyle(<br>                          decoration: todosList[index].isDone<br>                              ? TextDecoration.lineThrough<br>                              : null,<br>                        ),<br>                      ),<br>                      leading: Checkbox(<br>                        value: todosList[index].isDone,<br>                        onChanged: (value) {<br>                          changeCompletionStatus(index,<br>                              value: !todosList[index].isDone);<br>                        },<br>                      ),<br>                      trailing: IconButton(<br>                        onPressed: () async {<br>                          await deleteTodo(index);<br>                        },<br>                        icon: const Icon(<br>                          Icons.delete_forever,<br>                          color: Colors.redAccent,<br>                        ),<br>                      ),<br>                    );<br>                  },<br>                ),<br>      floatingActionButton: FloatingActionButton(<br>        onPressed: () async {<br>          showDialog(<br>            context: context,<br>            builder: (context) {<br>              return SimpleDialog(<br>                title: const Text(&quot;Create a Todo&quot;),<br>                contentPadding:<br>                    const EdgeInsets.symmetric(vertical: 18, horizontal: 16),<br>                children: [<br>                  Column(<br>                    children: [<br>                      TextField(<br>                        controller: _titleController,<br>                        decoration: const InputDecoration(<br>                            border: OutlineInputBorder(), label: Text(&quot;Title&quot;)),<br>                      ),<br>                      const SizedBox(<br>                        height: 12,<br>                      ),<br>                      TextField(<br>                        controller: _descriptionController,<br>                        decoration: const InputDecoration(<br>                            border: OutlineInputBorder(),<br>                            label: Text(&quot;Description&quot;)),<br>                      )<br>                    ],<br>                  ),<br>                  const SizedBox(height: 22),<br>                  Row(<br>                    mainAxisAlignment: MainAxisAlignment.spaceAround,<br>                    children: [<br>                      ElevatedButton(<br>                          onPressed: () {<br>                            Navigator.of(context).pop(widget);<br>                          },<br>                          child: const Text(&quot;CANCEL&quot;)),<br>                      ElevatedButton(<br>                        onPressed: () async {<br>                          if (_titleController.text.isEmpty) {<br>                            ScaffoldMessenger.of(context).showSnackBar(<br>                                const SnackBar(<br>                                    duration: Duration(milliseconds: 200),<br>                                    content: Text(&quot;Title cannot be empty&quot;)));<br>                          } else {<br>                            addTodo(<br>                              todo: Todo(<br>                                title: _titleController.text,<br>                                description: _descriptionController.text,<br>                              ),<br>                            ).then((_) {<br>                              _titleController.clear();<br>                              _descriptionController.clear();<br>                            });<br>                          }<br>                          Navigator.of(context).pop();<br>                        },<br>                        child: const Text(&quot;ADD&quot;),<br>                      ),<br>                    ],<br>                  )<br>                ],<br>              );<br>            },<br>          );<br>        },<br>        child: const Icon(Icons.add),<br>      ),<br>    );<br>  }<br>}</pre><p>This is how the output will look,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*WHE3R9OpctewA_HtB4GWXg.gif" /><figcaption>Output of the todo app</figcaption></figure><h3>Conclusion</h3><p>In this article, we explored <strong>Hive</strong>, a lightweight and fast NoSQL database for Flutter applications. We learned how to set up Hive, understood its basic concepts, and generated TypeAdapters for our data models. We also created a simple Todo List app to see how Hive works in action.</p><p>Hive is a great choice as it offers a simple and efficient way to manage local data. Even though Hive is no longer maintained it is still used by many developers. Its ease of use, performance, and flexibility make it a great choice.</p><h4>Happy coding!</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2FHzPtbOKyBoBFsK4hyc%2Ftwitter%2Fiframe&amp;display_name=Giphy&amp;url=https%3A%2F%2Fgiphy.com%2Fgifs%2Ffomoduck-duck-fomo-forever-squad-HzPtbOKyBoBFsK4hyc&amp;image=https%3A%2F%2Fmedia3.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExM2luZ2Nqb2E2Zzhmc3BydmZsZTB4YjNsc3NoNmg5ZTJxbHJqYTZvNiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FHzPtbOKyBoBFsK4hyc%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="435" frameborder="0" scrolling="no"><a href="https://medium.com/media/e52a1c0c637d48e0ca03bf2f7b1b4160/href">https://medium.com/media/e52a1c0c637d48e0ca03bf2f7b1b4160/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71f836eef438" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating Responsive Layouts Part III: Understanding Flexible and Expanded]]></title>
            <link>https://medium.com/@bhaweshmankar2002/creating-responsive-layouts-part-iii-understanding-flexible-and-expanded-d4194cc4cfd6?source=rss-6a5e7cbb735d------2</link>
            <guid isPermaLink="false">https://medium.com/p/d4194cc4cfd6</guid>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[flutter-ui]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[responsive-design]]></category>
            <category><![CDATA[flutter-widget]]></category>
            <dc:creator><![CDATA[Bhavesh Mankar]]></dc:creator>
            <pubDate>Sat, 21 Sep 2024 19:14:19 GMT</pubDate>
            <atom:updated>2024-09-21T19:14:19.336Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hGJH7qH9E0T63PR8U10urw.png" /><figcaption>Creating Responsive Layouts Part III: Understanding Flexible and Expanded</figcaption></figure><p>Understanding Flex Family in flutter for creating Responsive Layouts</p><blockquote><em>Note: This blog assumes that you are using </em><strong><em>vscode</em></strong><em>, </em><strong><em>Android Studio </em></strong><em>or </em><strong><em>IntelliJ </em></strong><em>as IDE.</em></blockquote><p>This is the third part in the <strong>Creating Responsive Layouts</strong> series and focuses on <strong><em>Flexible and Expanded </em></strong>widgets. The previous article can be found <a href="https://medium.com/@bhaweshmankar2002/creating-responsive-layouts-part-ii-exploring-layout-builder-in-flutter-2ad0ec8a6994">here</a>.</p><p>In this blog we will look how <a href="https://api.flutter.dev/flutter/widgets/Flexible-class.html"><strong>Flexible</strong></a> and <a href="https://api.flutter.dev/flutter/widgets/Expanded-class.html"><strong>Expanded</strong></a><strong> </strong>widgets work and learn how to use them for creating a <a href="https://docs.flutter.dev/ui/adaptive-responsive"><strong>Responsive Layouts</strong></a><strong> </strong>in Flutter.</p><ol><li>A bit about the <strong>Flex</strong> family</li><li>Introduction <strong>Flexible</strong> and <strong>Expanded</strong> widgets</li><li>Understanding the <strong>flex </strong>factor</li><li><strong>Conclusion</strong></li></ol><p>So, without a further ado lets jump right in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/0*6zw_hwDRI3vcQfY4.gif" /><figcaption><a href="https://tenor.com/view/lets-just-jump-right-in-peter-draws-let-us-begin-lets-just-start-gif-20941868">https://tenor.com/view/lets-just-jump-right-in-peter-draws-let-us-begin-lets-just-start-gif-20941868</a></figcaption></figure><h3>A bit about the <strong>Flex</strong> family</h3><p>To kick things of let’s first discuss about the <strong>flex family</strong> of widgets.</p><p>Well, <a href="https://api.flutter.dev/flutter/widgets/Flex-class.html"><strong>Flex</strong></a><strong> </strong>is acthually a class, but we will be refering the widget’s that use <strong>Flex</strong> as its <strong>parent(super class)</strong> being part of the <strong>flex family</strong><em>.</em></p><blockquote>Basically, the sub classes that <a href="https://dart.dev/language/extend"><strong>extend</strong></a> the <strong>Flex and related classes</strong> are part of the “<strong>flex family” of widgets</strong>.</blockquote><p>For some beginners, <strong>Flex</strong> might be a new word and might confuse them. But, we have all used Flex widget in the form of <a href="https://api.flutter.dev/flutter/widgets/Row-class.html"><strong>Row</strong></a> and <a href="https://api.flutter.dev/flutter/widgets/Column-class.html"><strong>Column</strong></a>. If we look at the code of <strong>Row</strong> and <strong>Column</strong> they both extend the <strong>Flex class</strong>. The only difference being the <a href="https://api.flutter.dev/flutter/painting/Axis.html"><strong>Axis</strong></a> <strong><em>direction</em></strong>. Row by default has direction of <strong><em>Axis.horizontal</em></strong> and Column by default has direction of <strong><em>Axis.vertical</em></strong>.</p><blockquote>So we can say that,</blockquote><blockquote>Row is a Flex widget which lay’s out its childrens “horzontally” and Column is a Flex widget which lay’s out its childrens “vertically”.</blockquote><p>Alos, We can put <strong>Flexible</strong> and <strong>Expanded</strong> widgets in the same family because of the close relation that these widgets have with the other <strong>Flex</strong> family widgets.</p><p>Now, since we have discussed about the <strong>Flex</strong> widgets its time to move to the next section.</p><h3>Introduction <strong>Flexible</strong> and <strong>Expanded</strong> widgets</h3><p>Let’s say we have a Row with some fixed size widgets in it. As we keep on adding more widgets we will arrive at the point were the widgets might not fit themselves in this Row and flutter will throw the <a href="https://docs.flutter.dev/testing/common-errors#a-renderflex-overflowed">RenderFlex overflowed</a> error that we all hate.</p><p>The reason we had this error in the first place was because of the use of <strong>fixed size </strong>widgets. The first lesson to keep in mind while <strong>creating responsive layouts</strong> is</p><blockquote>“Do Not Use Fixed Values for Determining Widget Size”</blockquote><p>If using fixed values for sizing widgets we might end up getting the <a href="https://docs.flutter.dev/testing/common-errors#a-renderflex-overflowed">RenderFlex Overflowed</a> error in one device or the other. So what’s the solution?</p><p>Well, the solution is eazy peasy. We can use relative sizes with help from either <strong>MediaQuery</strong> or <strong>LayoutBuilder</strong> widgets. Problem Solved….</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/0*ytXvuxg5ky0lL-sB.gif" /><figcaption><a href="https://tenor.com/view/lemons-gif-7268237712032466449">https://tenor.com/view/lemons-gif-7268237712032466449</a></figcaption></figure><p>Well, technically, the problem is solved and we won’t get any errors, but, here are some thing we need to consider before using <strong>MediaQuery</strong> or <strong>LayoutBuilder</strong>.</p><ol><li>Both <strong>MediaQuery</strong> and <strong>LayoutBuilder</strong> require a little <strong>boilerplate code</strong> before they can be of any use.</li><li>As discussed in previous articles of the series, using <strong>MediaQuery</strong> excessively can lead to <strong>performance issues</strong>. Same can be said about <strong>LayoutBuilder</strong>, any changes in constraints will result in rebuilding of the entire widget tree returned by <strong><em>builder</em></strong> function.</li><li>Even if we can deal with the above two reasons, we will need to make <strong>calculations</strong>, which can get ugly if you are bad at math as I am, ahh… just kidding. The calculations in such cases are usually simple but it’s better to avoid them whenever possible.</li></ol><p>Due to the above reasons it is best to avoid using <strong>MediaQuery</strong> or <strong>LayoutBuilder,</strong> inside the <strong>flex widgets</strong>.</p><blockquote>So, what can we do? Are there any options? Maybe some prebuilt widget……</blockquote><p>This is were <strong>Flexible</strong> and <strong>Expanded</strong> come into play. The good thing about these widgets is that they are a part of the <strong>flex family</strong> and were made for use inside flex widgets for relative sizing of the childern widgets.</p><p>The biggest adavntage of <strong>Flexible</strong> and <strong>Expanded</strong> widget is that, it does not require any boilerplate code and it extremely easy to use.</p><p>First, let’s have a look on how to use the <strong>Flexible</strong> widget,</p><pre>import &#39;package:flutter/material.dart&#39;;<br><br>class FlexibleExample extends StatelessWidget {<br>  const FlexibleExample({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return Scaffold(<br>      body: Row(<br>        children: [<br>          Flexible(<br>            flex: 1,<br>            child: Container(<br>              color: Colors.red,<br>              height: 100,<br>              child: const Center(child: Text(&#39;Flex 1&#39;)),<br>            ),<br>          ),<br>          Flexible(<br>            flex: 2,<br>            child: Container(<br>              color: Colors.green,<br>              height: 100,<br>              child: const Center(child: Text(&#39;Flex 2&#39;)),<br>            ),<br>          ),<br>          Flexible(<br>            flex: 1,<br>            child: Container(<br>              color: Colors.blue,<br>              height: 100,<br>              child: const Center(child: Text(&#39;Flex 1&#39;)),<br>            ),<br>          ),<br>        ],<br>      ),<br>    );<br>  }<br>}</pre><p>The above code uses <strong>Flexible</strong> widget for determing the width of the child <strong>Containers. </strong>We have specified the <strong><em>flex</em></strong> value which tells Flexible the amount of space that should be accquired by the child widget. The first(red) and last(blue) <strong>Container</strong> have the same <strong><em>flex</em></strong> value of <strong><em>1</em></strong> and will accquire less space in comparison to the second(green) <strong>Container </strong>which has a <strong><em>flex</em></strong> value of <strong><em>2</em></strong> accquiring more space, twice as much as first and second containers in our case. We can say that,</p><blockquote>“Higher” the flex value = “Larger” the space accquired</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/929/1*9zRmy46-bCaenExEtSgswA.png" /><figcaption>Flexible Example</figcaption></figure><p>Now, coming towards the <strong>Expanded</strong> widget, if we look at the code for <strong>Expanded</strong> it is just a <strong>Flexible</strong> widget with its <strong>FlexFit</strong> <strong><em>fit</em></strong> defaulting to <strong><em>FlexFit.tight</em></strong>. Specifiing the <strong>FlexFit</strong> as <strong><em>tight</em></strong> makes a <strong>Flexible</strong> fill all the remaining space and for the ease of use and understanding, this <strong>Flexible</strong> is “termed” as <strong>Expanded</strong>.</p><p>Let’s take a look at another code:</p><pre>import &#39;package:flutter/material.dart&#39;;<br><br>class ExpandedExample extends StatelessWidget {<br>  const ExpandedExample({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return Scaffold(<br>      body: Row(<br>        children: [<br>          Flexible(<br>            flex: 10,<br>            child: Container(<br>              color: Colors.red,<br>              height: 100,<br>              child: const Center(child: Text(&#39;Flex 1&#39;)),<br>            ),<br>          ),<br>          Container(<br>            color: Colors.green,<br>            height: 100,<br>            width: 200,<br>            child: const Center(child: Text(&#39;Fixed Width Container&#39;)),<br>          ),<br>          Expanded(<br>            child: Container(<br>              color: Colors.teal,<br>              height: 100,<br>              child: const Center(child: Text(&#39;Expanded&#39;)),<br>            ),<br>          ),<br>        ],<br>      ),<br>    );<br>  }<br>}</pre><p>In the code for <strong>ExpandedExample</strong> we have <strong>a Flexible </strong>widget, a <strong>fixed width Container</strong> and an <strong>Expanded </strong>widget inside a <strong>Row</strong>. The Flexible widget accquires the most space with a <strong><em>flex</em></strong> of <strong><em>10</em></strong>, fixed width container accquires a <strong><em>width</em></strong> of <strong><em>200</em></strong> and the <strong>Expanded</strong> widget fills the remaining space.</p><p>If we were to run the same code across devices of different screen dimensions we would get the same output. Aint’t that great.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/931/1*K6UuoPR2IOXftx4rqr3d8Q.png" /><figcaption>Expanded Example</figcaption></figure><p>OK. All of that is good, but what’s the thing with the <strong><em>flex</em></strong> value. Higher the flex value means larger the space accquired is understandable, but</p><blockquote>How does flutter calculates the size of widgets based on flex value?</blockquote><p>Well, that brings us to the next section of the article.</p><h3>Understanding the <strong>flex </strong>factor</h3><p>In order to fully understand how <strong>Flexible</strong> and <strong>Expanded</strong> work it is essential to understand the <strong><em>flex</em></strong> value or we can also say the <strong>“flex factor”</strong>.</p><p>The <strong>flex factor</strong> informs flutter about the amount of space the <strong>Flexible</strong> should accquire across the main axis. We can say that,</p><blockquote>Flex Factor determines the amount of space a <strong>Flexible</strong> should accquire relative to other widgets.</blockquote><p>This could be understood by a taking simple example,</p><h4>The Pizzamigos</h4><p>Let’s say there were three friends who have ordered a large pizza and they decide to distribute the pizza based on how hungry they are. The first friend is not that hungry so get’s a single pizza slice. The second friend is starving and gets 5 pizza slices. The third firend is hungry as well gets 4 slices.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*mlYv0dAMic1f1QE-.gif" /><figcaption><a href="https://giphy.com/gifs/pizza-grab-hut-WnCZNAxAil4XWkuuVg">https://giphy.com/gifs/pizza-grab-hut-WnCZNAxAil4XWkuuVg</a></figcaption></figure><p>Now, going by that analogy,</p><blockquote>The <strong>pizza</strong> is the “<strong>available space”</strong>,</blockquote><blockquote><strong>friends</strong> are the “<strong>widgets”</strong> and</blockquote><blockquote><strong>slices</strong> are the “<strong>flex factor”</strong>.</blockquote><p>Suppose that we have available width of <strong><em>400px</em></strong> and we use a <strong>Row</strong> with three <strong>Containers</strong>. Since we want a responsive layout we use <strong>Flexible </strong>to hold containers and specify the <strong>flex factor</strong> as <strong><em>1, 5 </em></strong>and <strong><em>4</em></strong> respectively.</p><p>Now, during layout process after determining the available space, flutter</p><ol><li>First <strong>adds the flex values</strong> 1 + 5 + 4 = 10 and calculates the size of individual part 1 part = 400px/10 = <strong>40px</strong></li><li>Then it<strong>, sub divides</strong> the available width into <strong>10 parts</strong>.</li><li><strong>Next</strong> flutter calculates space requested by each <strong>Flexible </strong>via flex factor</li></ol><ul><li>The first container with a flex factor of 1 gets <strong>1 part</strong> from the available space (1 * 40 pixels = <strong>40 pixels</strong>).</li><li>The second container with a flex factor of 5 gets <strong>5 parts</strong> from the available space (5 * 40 pixels = <strong>200 pixels</strong>).</li><li>The third container with a flex factor of 4 gets <strong>4 parts</strong> from the available space (4 * 40 pixels = <strong>160 pixels</strong>).</li></ul><p>Just like the pizza slices are divided among the friends based on how hungry they are, the available space in a <strong>Row</strong> or <strong>Column</strong> or <strong>Flex </strong>is divided among the widgets based on their respective <strong>flex factors</strong>. This ensures that space distribution relative and helps in <strong>Creating Responsoive Layouts</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/356/0*RZpySYkhmX0N1g5s.gif" /><figcaption><a href="https://giphy.com/gifs/filmeditor-will-ferrell-elf-xUySTD7evBn33BMq3K">https://giphy.com/gifs/filmeditor-will-ferrell-elf-xUySTD7evBn33BMq3K</a></figcaption></figure><h3>Conclusion</h3><p>In this article, we had an overview of <strong>Flex family</strong> in Flutter and origins of <strong>Row</strong> and <strong>Column</strong> widgets. Then we learned about <strong>Flexible</strong> and <strong>Expanded</strong> widgets, which help in creating responsive layouts by relatively sizing widgets based the flex factor. We also had a look at how the flex factor determines the size of widgets. Understanding these concepts is essential for designing flexible and well-balanced layouts in <strong>Flutter</strong>.</p><p>For reading the entire blog,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/0*bLy6wbUzmtYDnhlu.gif" /><figcaption><a href="https://giphy.com/gifs/latenightseth-thanks-thank-you-26gsjCZpPolPr3sBy">https://giphy.com/gifs/latenightseth-thanks-thank-you-26gsjCZpPolPr3sBy</a></figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4194cc4cfd6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating Responsive Layouts Part II: Exploring Layout Builder in Flutter]]></title>
            <link>https://medium.com/@bhaweshmankar2002/creating-responsive-layouts-part-ii-exploring-layout-builder-in-flutter-2ad0ec8a6994?source=rss-6a5e7cbb735d------2</link>
            <guid isPermaLink="false">https://medium.com/p/2ad0ec8a6994</guid>
            <category><![CDATA[responsive-design]]></category>
            <category><![CDATA[flutter-ui]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[flutter-widget]]></category>
            <category><![CDATA[flutter-app-development]]></category>
            <dc:creator><![CDATA[Bhavesh Mankar]]></dc:creator>
            <pubDate>Fri, 13 Sep 2024 06:38:42 GMT</pubDate>
            <atom:updated>2024-09-13T06:54:20.071Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M-rd1y49_6m-K6TUTAQ7IQ.jpeg" /><figcaption>Featured Image: Creating Responsive Layouts Part II: Exploring Layout Builder in Flutter</figcaption></figure><p>A Deep Dive into Layout Builder: Creating Responsive Layouts in Flutter</p><blockquote>Note: This blog assumes that you are using <strong>vscode</strong>, <strong>Android Studio </strong>or <strong>IntelliJ </strong>as IDE.</blockquote><p>This is the second part of the <strong>Creating Responsive Layouts</strong> series and focuses on <strong><em>Layout Builders</em></strong>. The first part was about <strong><em>Media Query</em></strong> and can be found <a href="https://medium.com/@bhaweshmankar2002/creating-responsive-layouts-exploring-media-query-in-flutter-2537f6467cbf">here</a>.</p><p>In this blog we will be exploring <a href="https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html"><strong>Layout Builder</strong></a> and understand how it contributes in creating <a href="https://docs.flutter.dev/ui/adaptive-responsive"><strong>Responsive Layouts</strong></a> in Flutter.</p><ol><li><strong>What </strong>is Layout Builder?</li><li><strong>How </strong>to use Layout Builder?</li><li>Creating <strong>Responsive layouts</strong> using Layout Builder</li><li><strong>Conclusion</strong></li></ol><p>So, without a further ado lets get started.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2Fl0IyjiXOXTX6Yemsg%2Ftwitter%2Fiframe&amp;display_name=Giphy&amp;url=https%3A%2F%2Fgiphy.com%2Fgifs%2Fl0IyjiXOXTX6Yemsg&amp;image=https%3A%2F%2Fmedia3.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExcTV2aWw3ODFhdW9hY3k4MWY1ZGM5dDkzMzB6b2JlMjZ2bzNqY2g4cSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fl0IyjiXOXTX6Yemsg%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="246" frameborder="0" scrolling="no"><a href="https://medium.com/media/63479fdb754ea332796f5f97bdf1cd69/href">https://medium.com/media/63479fdb754ea332796f5f97bdf1cd69/href</a></iframe><h3>What is Layout Builder?</h3><p><a href="https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html"><strong>Layout Builder</strong></a> is an amazing widget in flutter which provides information about the <strong>constraints </strong>that are enforced by the parent widget. As a beginner, one might mix up constraints and size, but there is a difference.</p><p><a href="https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html"><strong>BoxConstraints</strong></a><strong> </strong>or <a href="https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html"><strong>constraints</strong></a> are basically a set of four values <strong>minimum</strong> and <strong>maximum <em>width</em></strong> and <strong>minimum</strong> and <strong>maximum <em>height </em></strong>which are padded down by the parent widget<strong><em>, </em></strong>whereas <a href="https://api.flutter.dev/flutter/dart-ui/Size-class.html"><strong>Size</strong></a><strong> </strong>refers to the acthual dimensions of the widget after the layout has been rendered. Size of a widget cannot be bigger or smaller than maximum and minimum constraints.</p><p>So, to sum it up we can say that,</p><blockquote>A widget can have a any “size” as long as it fits within the “constraints”, which are enforced by the parent widget.</blockquote><p>This is were LayoutBuilder comes in. See, <strong>LayoutBuilder</strong> is the widget which informs about those <strong>incoming constraints</strong>. Once we know what the constraints are, we can then craft the layout to fit the available space in an efficient way.</p><p><a href="https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html"><strong>BoxConstraints</strong></a>, that is everything LayoutBuilder has to offer, there is nothing more. Next up let’s discuss how to use LayoutBuilder.</p><h3>How to use Layout Builder?</h3><p>Using LayoutBuilder must be eazy, just give it a child…… wait, wait, wait…hold on, LayoutBuilder does not have a <strong><em>child</em></strong> property as rest of the widgets.</p><p>As the name suggests it is a <a href="https://api.flutter.dev/flutter/widgets/Builder-class.html"><strong>Builder</strong></a><strong> </strong>widget and instead of <strong><em>child</em></strong> it has a required <a href="https://api.flutter.dev/flutter/widgets/ConstrainedLayoutBuilder/builder.html"><strong><em>builder</em></strong></a><strong> </strong>property, this <strong><em>builder</em></strong> property is acthually a <strong>Widget Function </strong>that takes two required parameters, <a href="https://api.flutter.dev/flutter/widgets/BuildContext-class.html"><strong>BuildContext</strong></a><strong> </strong>and <a href="https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html"><strong>BoxConstraints</strong></a><strong> </strong>and then returns a <a href="https://api.flutter.dev/flutter/widgets/Widget-class.html"><strong>Widget</strong></a>. This is how we to do it</p><pre>import &#39;package:flutter/material.dart&#39;;<br><br>void main() {<br>  runApp(const MainApp());<br>}<br><br>class MainApp extends StatelessWidget {<br>  const MainApp({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return const MaterialApp(home: LayoutBuilderExample()); //Used LayoutBuilderExample() as home<br>  }<br>}<br><br>class LayoutBuilderExample extends StatelessWidget {<br>  const LayoutBuilderExample({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return LayoutBuilder( //Used Layout Builder<br>      builder: (context, constraints) { //This is how we implement builder function<br>        return Container(   //The widget returned by builder function<br>          width: constraints.maxWidth, //set the width based on constraints<br>          height: constraints.maxHeight, //set the height based on constraints<br>          color: Colors.purple,<br>        );<br>      },<br>    );<br>  }<br>}</pre><p>In the above code we have defined a <a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html"><strong>StatelessWidget</strong></a><strong> </strong>called as <strong>LayoutBuilderExample, </strong>used as home, whose build method returns a <strong>LayoutBuilder</strong>. In the <strong><em>builder</em></strong> function we have specified two parameters BuildContext <strong><em>context</em></strong> and BoxConstraints <strong><em>constraints,</em></strong> which are the constraints passed down by the parent widget. In our case the parent widget is the device screen itself so constraints will be same as screen dimensions. Next, the LayoutBuilder returns a purple <a href="https://api.flutter.dev/flutter/widgets/Container-class.html"><strong>Container</strong></a><strong> </strong>whose dimensions are based on constraints incoming from the parent widget. We have specified the width and height of the container to occupy the maximum space the parent widget has to offer by using <strong><em>maxWidth</em></strong> and <strong><em>maxHeight</em></strong> properties respectively.</p><p>Now lets paint the same container but this time we will use <strong><em>minWidth</em></strong> and <strong><em>minHeight</em></strong> for specifying values for width and height respectively.</p><pre>// Modify the LayoutBuilderExample as follows <br>LayoutBuilder(<br>      builder: (context, constraints) {<br>        //Some Print Statements<br>        debugPrint(<br>            &quot;Max Width: ${constraints.maxWidth}\t\t\tMax Height: ${constraints.maxHeight}&quot;);<br>        debugPrint(<br>            &quot;Min Width: ${constraints.minWidth}\t\t\tMin Height: ${constraints.minHeight}&quot;);<br><br>        return Container(<br>          width: constraints.minWidth, //use minWidth instead of maxWidth<br>          height: constraints.minHeight, //use minHeight instead of maxHeight<br>          color: Colors.purple,<br>        );<br>      },<br>    );<br></pre><p>Aaand…. nothing changed. We still get the exact same output. Does that mean both min and max values are the exact same? To verify that we have have already included some print statements in our code. On checking the console of the IDE, we find that the output of print statements show exact same value for min and max values of width and height respectively. But why are the min and max constraints the exact same? Is there some problem?</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Ftenor.com%2Fembed%2F11589099662761765329&amp;display_name=Tenor&amp;url=https%3A%2F%2Ftenor.com%2Fview%2Fno-confused-why-stupid-penguin-gif-11589099662761765329&amp;image=https%3A%2F%2Fmedia.tenor.com%2FoNTAvdmDqdEAAAAN%2Fno-confused.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=tenor" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/0f1ca8e1a86dc362c70c569be1b824c2/href">https://medium.com/media/0f1ca8e1a86dc362c70c569be1b824c2/href</a></iframe><p>Well, there is no problem, its just how flutter renders the widgets. It is essential to understand how constraints work and how it affets the rendering of a widget. Fortunately, all of that is clearly explained in an <a href="https://medium.com/flutter-community/flutter-the-advanced-layout-rule-even-beginners-must-know-edc9516d1a2">article</a> by <a href="https://medium.com/@marcglasberg"><strong>Marcelo Glasberg</strong></a>, which can also be found in flutter docs <a href="https://docs.flutter.dev/ui/layout/constraints">here</a>.</p><p>I recommend the readers to check <a href="https://medium.com/flutter-community/flutter-the-advanced-layout-rule-even-beginners-must-know-edc9516d1a2">article</a> by <strong>Marcelo Glasberg </strong>before reading further, as it is important to understand constraints to fully grasp the concepts discussed in this article.</p><h3>Creating <strong>Responsive layouts</strong> using Layout Builder</h3><p>Now, lets move towards the most intresting section of the article which is creating responsive layouts using LayoutBuilder. We will also look at some exceptions that developers might face while creating responsive layouts while using LayoutBuilder.</p><p>So, LayoutBuilder informs us about constraints, and we can leverage this information to create responsive layouts. But how exactly?</p><p>Well, by now we know that constraints is a set of four doubles <strong><em>minWidth</em></strong>, <strong><em>minHeight</em></strong>, <strong><em>maxWidth</em></strong> and <strong><em>maxHeight </em></strong>incoming from parent widget<strong><em>. </em></strong>If you look at the code with which we created purple container, we have used the values of <strong><em>maxWidth</em></strong> and <strong><em>maxHeight</em></strong> for defining containers dimension. In our case <strong>parent</strong> was the <strong>screen</strong> so the incoming constraints were the acthual height and the width of the screen due to which the container filled the entire screen. Also the reasons due to which we observed the same results when using <strong><em>minWidth</em></strong> and <strong><em>minHeight </em></strong>were because, of the incoming constraints being <a href="https://medium.com/flutter-community/flutter-the-advanced-layout-rule-even-beginners-must-know-edc9516d1a2#:~:text=Tight%20%C3%97%20Loose%20Constraints">tight</a>(min and max constraints are the same).</p><p>OK. Now, let’s look at another example where we can use layout builder to do the same thing we used MediaQuery for. In the first article of the series we looked at a small code snippet(<a href="https://medium.com/@bhaweshmankar2002/creating-responsive-layouts-exploring-media-query-in-flutter-2537f6467cbf#:~:text=You%20can%20use%20something%20like%20this%20which%20determines%20the%20type%20of%20layout%20based%20on%20screen%20width">here</a>) which used MediaQuery to get the device dimensions and return either desktop, tablet or mobile layout.</p><p>We can achive the same functionality using LayoutBuilder. Here’s how,</p><pre>//I recommend readers to read the comments for better understanding of code<br><br>import &#39;package:flutter/material.dart&#39;;<br><br>void main() {<br>  runApp(const MainApp());<br>}<br><br>class MainApp extends StatelessWidget {<br>  const MainApp({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return const MaterialApp(home: LoadLayout());<br>  }<br>}<br><br>class LoadLayout extends StatelessWidget {<br>  const LoadLayout({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return LayoutBuilder( //Used Layout Builder<br>      builder: (context, constraints) {<br>        if (constraints.maxWidth &gt;= 1024) {<br>          return buildDesktopLayout(parentConstraints: constraints);<br>        } else if (constraints.maxWidth &gt;= 600) {<br>          return buildTabletLayout(parentConstraints: constraints);<br>        } else {<br>          return buildMobileLayout(parentConstraints: constraints);<br>        }<br>      },<br><br>      //Alternatively switch-case can be used instead of if-else<br>      // builder: (context, constraints) {<br>      //   return switch (constraints.maxWidth) {<br>      //     &gt;= 1024 =&gt; buildDesktopLayout,<br>      //     &gt;= 600 =&gt; buildTabletLayout,<br>      //     _ =&gt; buildMobileLayout,<br>      //   }(parentConstraints: constraints);<br>      // },<br>    );<br>  }<br>}<br><br>//layout functions desktop<br>Widget buildDesktopLayout({required BoxConstraints parentConstraints}) {<br>  return Scaffold(<br>    appBar: AppBar(<br>      title: Text(<br>          //display acthual height of the screen<br>          &quot;Desktop(Acthual Height: ${parentConstraints.maxHeight})&quot;), <br>    ),<br>    body: buildBody(color: Colors.indigo),<br>  );<br>}<br>//layout functions tablet<br>Widget buildTabletLayout({required BoxConstraints parentConstraints}) {<br>  return Scaffold(<br>    appBar: AppBar(<br>      title: Text(&quot;Tablet(Acthual Height: ${parentConstraints.maxHeight})&quot;),<br>    ),<br>    body: buildBody(color: Colors.purple),<br>  );<br>}<br>//layout functions mobile<br>Widget buildMobileLayout({required BoxConstraints parentConstraints}) {<br>  return Scaffold(<br>    appBar: AppBar(<br>      title: Text(&quot;Mobile(Acthual Height: ${parentConstraints.maxHeight})&quot;),<br>    ),<br>    body: buildBody(color: Colors.teal),<br>  );<br>}<br><br>//buildBody  function<br>Widget buildBody({required Color color}) {<br>  return LayoutBuilder(<br>    builder: (context, constraints) {<br>      return Container(<br>        width: constraints.maxWidth, //width based on maxWidth constraint<br>        height: constraints.maxHeight, ////width based on maxWidth constraint<br>        color: color,<br>        child: Center(<br>          child: Text(<br>            //display available height and width<br>            &quot;Width:${constraints.maxWidth} , Height:${constraints.maxHeight} &quot;, <br>            style: TextStyle(<br>              color: Colors.white,<br>              fontSize: constraints.maxWidth *<br>                 0.04, //fontSize based on incoming maxWidth constraint<br>              fontWeight: FontWeight.bold,<br>            ),<br>          ),<br>        ),<br>      );<br>    },<br>  );<br>}</pre><p>Here is the breakdown of the code,</p><p>In the code above we have a widget called as <strong>LoadLayout</strong> which returns a LayoutBuilder. Since <strong>LoadLayout</strong> is used as <strong><em>home</em></strong>, the parent is screen itself and screen dimensions are constraints. We have defined three functions called <strong><em>buildDesktopLayout()</em></strong>, <strong><em>buildTabletLayout()</em></strong>, <strong><em>buildMobileLayout()</em> </strong>which are returned by the <strong><em>builder</em></strong> function based on the <strong><em>maxWidth</em></strong> constraint. Each of the layout function takes <strong><em>parentConstraint</em></strong> as a parameter and returns a <strong>Sacffold</strong> which has an <strong><em>appBar</em></strong> whose title is a <strong>Text</strong> widget which the name of the layout and the <strong><em>maxHeight </em></strong>constraint. For the <strong><em>body</em></strong> of the Scaffold we have used a function called as <strong><em>buildBody()</em></strong>. It takes a <strong><em>color</em></strong> parameter. This function also returns a <strong>LayoutBuilder</strong>. Its <strong><em>builder</em></strong> function returns a <strong>Container</strong> whose <strong><em>color</em></strong> property is set to ‘<strong><em>color’ which is passed during function call</em></strong>. Container’s child is a <strong>Centre</strong> whose child is a <strong>Text</strong> widget which displays the incoming constraints.</p><p>Select <strong>Windows(windows-x64)</strong> as the device in IDE and run the application.</p><p>Initially, it will return <strong><em>buildDesktopLayout()</em></strong> with indigo color and since our application is responsive we can resize the app and see the magic. As the width is reduced and certain <strong>breakpoint</strong> is reached different layouts are returned by LayoutBuilder of <strong>LoadLayout</strong>. Also since we have used maxWidth to set <strong><em>fontSize</em></strong>, the font size reduces as the window shrinks horizontally.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DRKelaamdzA4rE4YNOvWNQ.gif" /><figcaption>Responsive Layout</figcaption></figure><p>There is one thing all of us might have noticed and that is the height. Why is the height displayed differently on the <strong><em>appBar</em></strong> and on the <strong><em>body</em></strong>? That is because LayoutBuilder, as we all know by now, informs about constrains incoming from the parent. Since we have two LayoutBuilders used at <strong>different places</strong> with <strong>different parents</strong> we get <strong>different constraints</strong>.</p><p>The <em>first</em> LayoutBuilder is used by <strong>LoadLayout</strong> whose parent is the screen itself, so naturally, the <strong>constraints will be tight </strong>and will be equal to acthual dimensions of the screen. In our code we have passed these constraints to the <strong><em>parentConstraints</em></strong> parameter of layout functions and displayed the acthual height in the <strong><em>appBar.</em></strong></p><p>The <em>second </em>LayoutBuilder is used by <strong><em>buildBody() </em></strong>function which is called by layout functions as the Scaffold’s <strong><em>body</em></strong>. So for <strong><em>buildBody()</em></strong> function‘s LayoutBuilder it is the <strong>Scaffold</strong> who <strong>is the parent</strong>. Since the <strong><em>appBar</em></strong> is already present, the available space will be less and vertical constraint i.e the height, will be reduced. This is the <strong>available height</strong> which is displayed at the Centre of the Container by the Text widget. Also, the <strong><em>fontSize</em></strong> of the text is adjusted based on <strong><em>maxWidth, </em></strong>so as the window is resized, the text responds to these changes and adjusts its size accordingly.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Ftenor.com%2Fembed%2F7587630708480898421&amp;display_name=Tenor&amp;url=https%3A%2F%2Ftenor.com%2Fview%2Fi-think-i%27m-beginning-to-get-the-hang-of-this-scrooge-charles-hutchins-i-think-you-should-leave-with-tim-robinson-i%27m-starting-to-get-it-gif-7587630708480898421&amp;image=https%3A%2F%2Fmedia.tenor.com%2FaUyt7anZdXUAAAAN%2Fi-think-i%2527m-beginning-to-get-the-hang-of-this-scrooge.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=tenor" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/d1261275e4dc54db307beeebb6316ac8/href">https://medium.com/media/d1261275e4dc54db307beeebb6316ac8/href</a></iframe><p>Isn’t that great. We can use <strong>LayoutBuilder</strong> anywere in the Widget tree make informed decisions about the layout based on the availabiliy of space. This allows us to create responsive layout which looks alomost identical across various devices and platforms. We can combime MediaQuery and LayoutBuilder to create responsive apps. But thats not all there other things like GridView, ListView, Responsive Text etc. that are needed to be taken into account for creating a trully responsive app. In further parts we will look at each of these and discuss about <strong>Creating Responsive Layouts</strong>.</p><h3><strong>Conclusion</strong></h3><p>Using <strong>LayoutBuilder</strong> in Flutter apps is a helps in adjusting layouts dynamically based on available space, making sure the app looks great on any device. The combination of <strong>LayoutBuilder</strong> with other widgets like <strong>MediaQuery</strong> allows making informed layout decisions, enhancing both usability and user experience. Whether designing for mobile, tablet, or desktop, <strong>LayoutBuilder</strong> provides information needed to build responsive apps.</p><p>I hope that this blog provided you with the information required about LayoutBuilder and helped in understanding how responsive layouts are created.</p><p>For reding the entire blog,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/0*eAMh31P7ZrNo2DI1.gif" /><figcaption><a href="https://tenor.com/view/thank-you-sticker-thanks-sticker-line-sticker-cat-sticker-orange-cat-gif-26476683">https://tenor.com/view/thank-you-sticker-thanks-sticker-line-sticker-cat-sticker-orange-cat-gif-26476683</a></figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2ad0ec8a6994" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating Responsive Layouts Part I: Exploring Media Query in Flutter]]></title>
            <link>https://medium.com/@bhaweshmankar2002/creating-responsive-layouts-exploring-media-query-in-flutter-2537f6467cbf?source=rss-6a5e7cbb735d------2</link>
            <guid isPermaLink="false">https://medium.com/p/2537f6467cbf</guid>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[responsive-design]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Bhavesh Mankar]]></dc:creator>
            <pubDate>Tue, 03 Sep 2024 05:28:46 GMT</pubDate>
            <atom:updated>2024-09-08T13:34:27.095Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5e6DJ9k7NZkDpZXf9DCLgA.png" /><figcaption>Featured Image: Creating Responsive Layouts Part I: Exploring Media Query in Flutter</figcaption></figure><p>A Deep Dive into MediaQuery: Creating Responsive Layouts in Flutter</p><blockquote>Note: This blog assumes that you are using <strong>vscode</strong>, <strong>AndroidStudio </strong>or <strong>IntelliJ </strong>as IDE.</blockquote><p>In this medium blog we will discuss about <a href="https://api.flutter.dev/flutter/widgets/MediaQuery-class.html"><strong>Media Query</strong></a> class and find how it helps in creating <a href="https://docs.flutter.dev/ui/adaptive-responsive"><strong>Responsive Layouts</strong></a> in Flutter.</p><ol><li><strong>What</strong> is Media Query?</li><li><strong>How</strong> to use MediaQuery?</li><li>Exploring the <strong>MediaQuery</strong> class.</li><li>Creating <strong>Responsive layouts</strong> using Media Query.</li><li><strong>Best Practices</strong> while using MediaQuery.</li><li><strong>Conclusion</strong></li></ol><p>So, without a further ado lets begin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*HFu3MOQQ0ngs8k3w.gif" /><figcaption><a href="https://tenor.com/uamW.gif">https://tenor.com/uamW.gif</a></figcaption></figure><h3>1. What is Media Query?</h3><p>In Flutter, <strong>MediaQuery</strong> is a widget that provides information about the device’s screen and user preferences. It allows developers to build responsive and adaptive user interfaces by giving access to various details about the screen’s size, orientation, pixel density, and more. How, you ask?</p><p>Just use,</p><pre>//Use this anywere in the Widget tree where BuildContext context is available<br> MediaQuery.of(context);</pre><p>And you will now have access to everything MediaQuery has to offer.</p><blockquote><strong><em>But how MediaQuery acthually does this?</em></strong></blockquote><p>MediaQuery achives this using <strong>BuildContext </strong>which provides a high-level overview of the current state of the device screen by creating an instance of something called as <a href="https://api.flutter.dev/flutter/widgets/MediaQueryData-class.html"><strong>MediaQueryData</strong></a><strong>. </strong>It is <strong>MediaQueryData </strong>which gives access to screen’s size, orientation, pixel density, and more.</p><p>By utilizing MediaQuery developers can create layouts that can morph to fit in device screen based on device width, device height, orientation, brightness, etc. and create consistent layout on devices of various screen sizes and <a href="https://docs.flutter.dev/reference/supported-platforms"><strong>Platforms</strong></a>(Android, IOS, Windows, Linux and Web).</p><h3>2. How to use MediaQuery?</h3><p>As discussed above its quite easy to use MediaQuery, just use the <strong><em>of()</em></strong> method and you are good to go, the only requirement is <a href="https://api.flutter.dev/flutter/widgets/BuildContext-class.html"><strong>BuildContext</strong></a><strong>. </strong>MediaQuery can be used anywere in the widget tree where the <strong>BuildContext </strong>is available.</p><p>Next, in order to access the data provided by <strong>MediaQuery.of(context)</strong> you can create a variable of type <strong>MediaQueryData, </strong>let’s say <strong>mediaQuery</strong>. Now once we have instance of MediaQueryData, just use “ <strong>mediaQuery. ”</strong> and you will be able to see everything MediaQueryData has to offer.(Assuming you are using vscode, Android Studio or IntelliJ IDE)</p><p>Putting all things together, this is how you use MediaQuery.</p><pre>//NOTE: only use MediaQuery where BuildContext context is available<br><br>//EITHER you can specify the type for mediaQuery as MediaQueryData<br>final MediaQueryData mediaQuery = MediaQuery.of(context);<br><br>//OR eleminate the type and let dart infer it<br>final mediaQuery = MediaQuery.of(context);<br><br>//Voilla, now you have access to everything MediaQuery has to offer.</pre><blockquote>All of that is right but what is the deal with BuildContext<strong> </strong>and it being “available” in order to use MediaQuery?</blockquote><p>Look, it’s simple and nothing to be worried about. Here is the thing, every widget in flutter application(Stateless, Stateful or Inherited Widget) has a <a href="https://api.flutter.dev/flutter/widgets/Text/build.html"><strong>build()</strong></a><strong> </strong>method which is <strong>used to build a widget</strong> and is a <strong>necessary override </strong>while creating any widget. This build method takes a <strong>required argument </strong>which is (<em>drumroll…</em>), <strong>BuildContext</strong>.</p><p>Now, since <strong>BuildContext</strong> is available to all the widgets inside the <strong>build()</strong> method and we can use the MediaQuery.of() method and pass the BuildContext to it.</p><p>We will typically write something like this in order to create a Widget and use MediaQuery.of(context) inside the <strong>build(BuildContext context) </strong>method where the<strong>BuildContext is available</strong>.</p><pre>//Example Usage 1<br>//MediaQuery being used inside a widget<br><br>class MyCustomWidget extends StatelessWidget {<br>  const MyCustomWidget({super.key});<br>  @override<br>  Widget build(BuildContext context) {<br>    //this MediaQueryData instance can be only used<br>    //inside the build method where the context is available<br>    final MediaQueryData mediaQuery = MediaQuery.of(context);<br><br>    return Scaffold(<br>      body: Center(<br>        child: Container(<br>          width: mediaQuery.size.width * 0.5, //50% of screen width<br>          height: mediaQuery.size.height * 0.5, //50% of screen height<br>          color: Colors.blue,<br>        ),<br>      ),<br>    );<br>  }<br>}</pre><blockquote>Another question: Can we use MediaQuery outside widgets?</blockquote><p>The short answer is <strong><em>YES</em></strong>. But there are things we must know.</p><p>Remember the only requirement for using MediaQuery.of(context) is <strong>BuildContext</strong>. This means that one can define a function and use BuildContext as its <strong>parameter</strong>. Then MediaQuery can be used inside the function block.</p><p>This approach is usually used to create reusable code blocks or to create a separate function to manage a growing widget tree ensuring code maintainability.</p><blockquote><strong>Note</strong>: Such a function should be only called where build context is available, preferably inside the widget’s <strong>build()</strong> method.</blockquote><p>Check the below example.</p><pre>//Example Usage 2<br>//MediaQuery being used inside a function<br><br>class MyCustomWidget extends StatelessWidget {<br>  const MyCustomWidget({super.key});<br>  @override<br>  Widget build(BuildContext context) {<br>    return Scaffold(<br>      body: Center(<br>        child: customContainer(context), //call customContainer and pass context<br>      ),<br>    );<br>  }<br>}<br><br>//A funtion using MediaQuery<br><br>Container customContainer(BuildContext context) {<br>  final mediaQuery = MediaQuery.of(context);<br>  return Container(<br>    width: mediaQuery.size.width * 0.5, //50% of screen width<br>    height: mediaQuery.size.height * 0.5, //50% of screen height<br>    color: Colors.blue,<br>  );<br>}<br></pre><p>Both the approaches will produce the same output i.e, a blue Container occupying 50% of screen width and 50% of screen height .</p><p>That all we need to know about using MediaQuery in flutter.</p><p>Now let’s move to some important aspects of MediaQuery and see what MediaQuery offers and how we can utilise it.</p><h3>3. Exploring the MediaQuery class.</h3><p>Now, we have covered all the basics of <a href="https://api.flutter.dev/flutter/widgets/MediaQuery-class.html"><strong>MediaQuery</strong></a><strong> </strong>and we are ready to start exploring the MediaQuery or should I say MediaQueryData class. Remember, as discussed in previous sections it is <a href="https://api.flutter.dev/flutter/widgets/MediaQueryData-class.html"><strong>MediaQueryData</strong> </a>that provides information about device screen and user preferences.</p><p>Media Query has multiple data members(properties) that contain information about device specific information like devicePixelRatio, orientation, padding, size, etc. Let’s go through all such important data members.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/0*MaHJZbZz6zQgbXIS.gif" /><figcaption><a href="https://tenor.com/bE4um.gif">https://tenor.com/bE4um.gif</a></figcaption></figure><blockquote><strong>Note:</strong> I will be covering the important and most used data members and not all of them. Consider <a href="https://api.flutter.dev/flutter/widgets/MediaQueryData-class.html">reading the docs</a> for a detailed info</blockquote><p><strong>A. Data members containing information about “<em>Device Screen”</em></strong></p><p><strong>1. <em>devicePixelRatio</em>:</strong> Device Pixel Ratio or DPR refers to the ratio between the physical pixels on screen and logical pixels. Physical pixels are the acthual pixels present on the display of the device, whereas one logical pixel corrosponds to ‘n’ number of physical pixels. We can say something like <strong>1 logical pixel </strong>corrospond to <strong>3 physical pixels</strong>. If DPR for a device is 3 then, 3 physical pixels will be used to draw a line which is 1px wide. Logical Pixels are same across various devices and are used to maintain “visual consistancy”. DPR is mostly used in dealing with images with different resolutions to maintain consistancy. This is how to use <strong><em>devicePixelRatio</em></strong></p><pre>//devicePixelRatio is useful when dealing with images heiping<br>//in pixel perfect rendering<br>final double dpr = MediaQuery.of(context).devicePixelRatio;</pre><p><strong>2. <em>padding</em>:</strong> Padding in context of MediaQuery refers to the amount of space reserved by operating system like the system status bar(the one were we see the time and battery percentage, etc.) and the bottom region which is used for naviagtion and other puropses which may vary from one platform to other. Padding is of type <a href="https://api.flutter.dev/flutter/painting/EdgeInsets-class.html"><strong>EdgeInsets</strong></a><strong> </strong>and has four offsets, namely botton, top, right and left. Bottom padding will have some value if the keyboard is not visible, if keyboard is Up then bottom padding will be reduced to “0”. This is how to use <strong><em>padding</em></strong></p><pre>//padding helps in determining the position and amount of pixels reserved by <br>//the operating system for its own puropses<br>final EdgeInsets padding = MediaQuery.of(context).padding;<br>debugPrint(&quot;Padding =&gt; Bottom: ${padding.bottom}, Top: ${padding.top}, Left: ${padding.left}, Right: ${padding.right}&quot;);</pre><p><strong>3. <em>orientation</em>: </strong>Orientation is as simple as it gets. It refers to the <strong>alignment and positioning</strong> of the device. There are two types of orientations, <strong>Landscape </strong>and <strong>Potrait</strong>. In Potrait orienatation the width of device is smaller than the height and in Landscape orientation it just gets reversed, now the width is larger and height is smaller. Orientation plays a crucial role in determining the layout of the application, as changing the orientation affects the amount of information or data that can be displayed on the screen. A good application is one which accounts for different orientations. This is how to use <strong><em>orientation</em></strong></p><pre>//orientation helps in determining the position and alignment of device<br>final Orientation orientation= MediaQuery.of(context).orientation;<br>if(orientation == Orientation.landscape){<br>  debugPrint(&quot;Device is in Landscape mode&quot;);<br>}else{<br>  debugPrint(&quot;Device is in Potrait mode&quot;);<br>}</pre><p><strong>4. <em>size</em>: </strong>Size is the most used data member offered by the MediaQueryData class and provides a lot of information about the device screen. But we will be talking about three, namely <strong>width</strong>, <strong>height </strong>and <strong>aspectRatio</strong>. As the name suggests width gives the <strong>device width</strong>, height gives the <strong>device height</strong> and aspectRatio gives the <strong>ratio of device width to device height</strong>. This is how to use <strong><em>size</em></strong></p><pre>//size property helps in getting device screen dimensions<br>final Size size = MediaQuery.of(context).size;<br>debugPrint(&quot;Size =&gt; Width: ${size.width}, Height: ${size.width}, AspectRatio: ${size.aspectRatio}&quot;);</pre><p><strong>5. <em>viewInsets</em> :</strong> ViewInsets is of type <a href="https://api.flutter.dev/flutter/painting/EdgeInsets-class.html"><strong>EdgeInsets</strong></a>. <strong><em>viewInsets </em></strong>is used to determine the amount of space consumed by the keyboard when it is visible. If the keyboard is not visible the all the four offsets of viewInsets i.e bottom, top, right and left will be “0”. View Insets bottom offset will be non zero when the keyboard is up and every other offset will be zero (always). This is how to use <strong><em>viewInsets</em></strong></p><pre>//viewInsets property helps in determining weather the keyboard is up or not<br>//If the keyboard is up then bottom property of viewInsets will provide the <br>//amount of space consumed by the keyboard<br>final EdgeInsets viewInsets = MediaQuery.of(context).viewInsets;<br>if(viewInstes.bottom &gt; 0){<br>debugPrint(&quot;Keyboard is UP&quot;);<br>}</pre><p><strong>6. <em>viewPadding</em>: </strong>ViewPadding is also of type <a href="https://api.flutter.dev/flutter/painting/EdgeInsets-class.html"><strong>EdgeInsets</strong></a><strong> </strong>and is similar to padding in a lot of ways. The only difference between padding and viewPadding is bottom value of <strong><em>padding </em></strong>becomes “0” when keyboard is visible whereas <strong><em>viewPadding </em></strong>despite the visibility of keyboard has a non zero bottom value for viewPadding. Basically viewPadding remembers what the bottom padding was unlike padding<strong><em> </em></strong>which forgets bottom padding value and sets it to “0”. Everything else is same. Consider using viewPadding instead of padding for consistant layouts. This is how to use <strong><em>viewPadding</em></strong></p><pre>//viewPadding property does not forget what the bottom padding was<br>//even if the keyboard is visible<br>final EdgeInsets viewPadding = MediaQuery.of(context).viewPadding;<br>debugPrint(&quot;ViewPadding =&gt; Bottom: ${padding.bottom}, Top: ${padding.top}, Left: ${padding.left}, Right: ${padding.right}&quot;);</pre><p><strong>B. Data Members containing information about “<em>User Prefrences</em></strong></p><p><strong>1<em>. accessibleNavigation</em>: </strong>Accessible Navigation is of type bool and returns <strong>true</strong> if screen reader service like <strong>TalkBack</strong>(for Android) or <strong>VoiceOver</strong>(for IOS) are enabled. This allows developer to craft apps that are asseccible to users with visual impairments. This is how to use <strong><em>accessibleNavigation</em></strong></p><pre>//accessibleNavigation returns if screen reader service is enabled by the user<br>final bool scrReaderStatus = MediaQuery.of(context).accessibleNavigation;<br>if(scrReaderStatus == true){<br>  debugPrint(&quot;The screen reader service is enabled&quot;);<br>  //DO Something<br>}</pre><p><strong>2. <em>alwaysUse24HourFormat </em>: </strong>This data member is of type bool and is related to the time format the user has configured on his device. There are two time formats <strong>12 hour(AM/PM)</strong> and <strong>24 hour</strong> format. If the user has opted for 24 hour format then <strong><em>alwaysUse24HourFormat </em></strong>will return true, else it will return false. This property is used when creating apps that display time on the app like a todo list app or a clock app. This is how to use <strong><em>alwaysUse24HourFormat</em></strong></p><pre>//alwaysUse24HourFormat returns true if the <br>//user has opted for 24 Hour time format in device settings<br>final bool is24Hr = MediaQuery.of(context).alwaysUse24HourFormat;<br>if(is24Hr == true){<br>  debugPrint(&quot;The user has opted for 24 hr time format&quot;);<br>  //DO Something<br>}</pre><p><strong>3. <em>boldText</em>: </strong>Bold Text can be considered as an accessibility feature and is of type bool. If user has enabled Bold Text in device settings then the text appears enhanced or bold. If the user has enabled bold text the <strong><em>boldText </em></strong>property will return true. We can make adjustments to the TextStyles fontWeight porperty to handel such situations. This is how to use <strong><em>boldText</em></strong></p><pre>//boldText returns true if user has enabled &quot;Bold Text&quot;<br>//switch in device accessibility settings<br>final bool usingBoldText = MediaQuery.of(context).boldText;<br>//Somewere inside widget tree<br>Text(<br>  &#39;Hello, World!&#39;,<br>  style: TextStyle(<br>    fontWeight: usingBoldText == true ? FontWeight.bold : FontWeight.normal),<br>  ),<br>);<br><br>//NOTE: It will be better to change the font weight app wide using the <br>//themeData property of the MaterialApp</pre><p><strong>4. <em>disableAnimations</em>: </strong>Disable Animations property is of type bool and returns true if the user has disabledAnimations. This is usually done by users who have motion sensitivity or users who just prefer a static user interface. The good thing is MediaQuery provides a way by which we can account for such changes. This is how to use <strong><em>disableAnimations</em></strong></p><pre>//disableAnimations returns true if user has disabled animations<br>//in device accessibility settings<br>final bool hasDisabledAnimation = MediaQuery.of(context).disableAnimations;<br>if(hasDisabledAnimation == true){<br>  debugPrint(&quot;User has disabled animations&quot;);<br>  //Implement the logic to disable animations <br>}</pre><p><strong>5. <em>platformBrightness</em>: </strong>Platform Brightness is the property which developers will find using often. It tells weather user has enabled light theme or dark theme. platformBrightness returns an emum called <strong>Brighthess </strong>which has two values <strong>light</strong> and <strong>dark</strong>. It is common for users to change the system theme mode based on time of the day or just personal preference, so accounting for such changes is must. This is how to use <strong><em>platformBrightness</em></strong></p><pre>//platformBrightness helps in determining which theme is used <br>//by the user<br>final Brightness platformBrightness = MediaQuery.of(context).platformBrightness;<br>if(platformBrightness == Brightness.dark){<br>  debugPrint(&quot;System is using dark mode&quot;);<br>  //Load the ThemeData configured for dark theme<br>} else{<br>  debugPrint(&quot;System is using light mode&quot;);<br>  //Load the ThemeData configured for dark theme<br>}<br><br>//NOTE: The MaterialApp provides a way to deal with platform brightness<br>//It has a property called as themeMode which is of type ThemeMode <br>//and is used to load themes based on value passed to themeMode property<br><br>return MaterialApp(<br>  theme: ThemeData.light(), //light theme<br>  darkTheme: ThemeData.dark(), //dark theme<br>  //Automatically switches between dark and light theme based on &quot;system theme&quot;<br>  themeMode: ThemeMode.system, <br>  home: MyHomePage(),<br>);</pre><p>I have covered some of the most used data members of the MediaQueryData class but this is not all of them. Be sure to check the documentation for <a href="https://api.flutter.dev/flutter/widgets/MediaQueryData-class.html"><strong>MediaQueryData</strong></a> to learn more.</p><h3>4. Creating <strong>Responsive layouts</strong> using Media Query.</h3><p>Now, we have came a long way exploring Media Query and are familier with its in’s and out’s. This is the right time to discuss about creating responsive designs and the role of Media Query in creating responsive apps.</p><p>Media Query tells us about the device screen which is basically everything that is required to build a responsive layout. What are you waiting for, just start using Media Query and create responsive layous.</p><p>Well….yeah that’s all.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/0*jixJ2A-LqyGvtU95.gif" /><figcaption><a href="https://tenor.com/c7QXPiaXDh7.gif">https://tenor.com/view/what-why-huh-steve-harvey-confused-gif-2486632017667011481</a></figcaption></figure><p>Look what I said is true, MediaQuery is the very essencce of creating responsive layout in flutter, but that’s not all of it. Let me explain.</p><h4><strong>So, calm down.</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*C-0YENiaRCGx9H0w.gif" /><figcaption><a href="https://tenor.com/view/calm-down-gif-24013477">https://tenor.com/view/calm-down-gif-24013477</a></figcaption></figure><p>Knowing what MediaQuery is and utilizing MediaQuery for creating Responsive Layouts are flip sides of the same coin. It is esssential understanding both of them to create what is called as a “<strong><em>Responsive App ”, </em></strong>one that is <strong>visually consistent</strong>, offers <strong>optimal user experience</strong> acrross <strong>wide range of devices</strong>, <strong>adapts </strong>and <strong>uses the</strong> <strong>available space in best possible way</strong>, so on and so fourth. Covering all of that in a single blog post is not possible, but don’t worry as this is just the first part and <strong>next parts </strong>will make it way more clear.</p><p>Now, since we have included the section about responsive layouts in the blog post, lets see a tiny example that will demonstrate the power of MediaQuery in creating responsive app.</p><p>Let’s say you are coding a application and it will be used on smartphones, tablets, desktop and web as well. Thats a lot of screens to account for. How do we know which device screen the app is been used on, and how can MediaQuery help in this situation?</p><p>You can use something like this which determines the type of layout based on screen width</p><pre>double screenWidth = MediaQuery.of(context).size.width;<br>double screenHeight = MediaQuery.of(context).size.height;<br><br>if (screenWidth &gt;= 1024) {<br>  // Desktop layout<br>  return buildDesktopLayout();<br>}else if (screenWidth &gt;= 600) {<br>  // Tablet layout<br>  return buildTabletLayout();<br>}else {<br>  // Mobile layout<br>  return buildMobileLayout();<br>}</pre><p>This is one of the most used approaches while creating responsive layout, but the above code only accounts for screen width and there are a many things to consider. It’s best to save them for later parts.</p><p>Till then,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/467/0*hzs-iiqViJhZrKyO.gif" /><figcaption><a href="https://tenor.com/view/womens-day-stay-tuned-updated-gif-16526278">https://tenor.com/view/womens-day-stay-tuned-updated-gif-16526278</a></figcaption></figure><h3><strong>Best Practices</strong> while using MediaQuery</h3><blockquote>Media Query is a great, but using it can be expensive. Why, you ask?</blockquote><p>Using MediaQuery.of(context) returns an instance of MediaQueryData which has multiple data members, so any change in any of the data members(properties) of MediaQueryData class results in rebuilding of the widget. Any minor change and the entire widget gets rebuilt, and this is expensive. <strong>But how expensive can it be?</strong></p><p>To understand this, let’s say that the user has toggled the system theme from light to dark, now since <strong><em>brightness </em></strong>is a data member of MediaQueryData class this will result in rebuilding of all the widgets that depend on MediaQueryData. And if you have multiple MediaQueries used in different parts of the application, it will have significant impact on the performance of the application.</p><p>It is best to avoid the use MediaQueryData or <strong>use it in a separate widget</strong> so that only that widget rebuilds rather than the entire parent widget.</p><blockquote>If not MediaQueryData the how do we get information about the device screen and user perferences?</blockquote><p>Don’t worry, flutter has one alternate way.</p><p>As an example, instead of using <strong>MediaQuery.of(context).size</strong>, we can use <a href="https://api.flutter.dev/flutter/widgets/MediaQuery/sizeOf.html"><strong>MediaQuery.sizeOf(context)</strong></a>, this will only result in rebuilding of widget when <strong><em>size</em></strong> property of MediaQueryData changes. This is a much better approach as changes in something like system theme will not result in rebuilding the widgets depending on <strong>MediaQuery.sizeOf(context)</strong>.</p><p>Every data member of MediaQueryData class we discussed about can be retrived using <strong>MediaQuery.<em>{property}</em>of(context)</strong> instead of <strong>MediaQuery.of(context).<em>{property}</em></strong>.</p><p>If we only need <strong><em>devicePixelRatio </em></strong>using <strong>MediaQuery.devicePixelRatioOf(context)</strong> will be better than using MediaQuery.of(context).devicePixelRatio;</p><p>Same can be said about all the other data members or properties of MediaQueryData class. We can leveragethis to minimize dependency on MediaQuery.of(context) or MediaQueryData class</p><h3>Conclusion</h3><p>In conclusion, <strong>MediaQuery</strong> is an essential tool for building responsive and adaptive user interfaces in Flutter. By leveraging MediaQuery, developers can create layouts that can adjust themselves to fit various screen sizes, orientations, and device types, ensuring a consistent and optimal user experience across smartphones, tablets, desktops, and the web.</p><p>I hope that this blog has provided you with the information required about MediaQuery and a overview of Responsive Design.</p><p>And for reding the entire blog,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*LdKXUcSrwrasbgX_.gif" /><figcaption><a href="https://giphy.com/gifs/illustration-thanks-thank-you-3ohs4kI2X9r7O8ZtoA">https://giphy.com/gifs/illustration-thanks-thank-you-3ohs4kI2X9r7O8ZtoA</a></figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2537f6467cbf" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>