<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Irfan Adisukma</title>
    <description>The latest articles on Forem by Irfan Adisukma (@madisukma).</description>
    <link>https://forem.com/madisukma</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1217659%2F5a87edd8-431c-4cf4-ace2-65ca8268e51b.png</url>
      <title>Forem: Irfan Adisukma</title>
      <link>https://forem.com/madisukma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/madisukma"/>
    <language>en</language>
    <item>
      <title>Great Code Starts with Great Planning</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Mon, 24 Feb 2025 07:47:58 +0000</pubDate>
      <link>https://forem.com/tentanganak/great-code-starts-with-great-planning-2mlk</link>
      <guid>https://forem.com/tentanganak/great-code-starts-with-great-planning-2mlk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxs79on4ll0h7qz9u8tg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxs79on4ll0h7qz9u8tg.png" alt="Image description" width="607" height="189"&gt;&lt;/a&gt;&lt;br&gt;
Image Source: &lt;a href="https://www.growmy.tech/post/examples-of-finding-a-niche-in-software-development-industry" rel="noopener noreferrer"&gt;GrowMy.tech&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every developer dreams of writing clean, efficient, and impactful code. But what many overlook is the critical step that comes before the first line of code is even written: planning. Without a clear roadmap, projects can quickly spiral into chaos, leading to wasted time, frustration, and subpar results. Taking the time to carefully plan your approach not only sets a solid foundation but also makes the coding process smoother and more productive. In this article, we'll explore why planning is the cornerstone of successful development and how it can elevate your coding projects to new heights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfcog7roixbb2ctp8ibf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfcog7roixbb2ctp8ibf.jpg" alt="Image description" width="600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Journey of a Beginner Developer
&lt;/h2&gt;

&lt;p&gt;The internet is full of technical tutorials and advice on how to code effectively. But let’s take a step back and look at the bigger picture. No matter how many mistakes you make or how often things don’t go as planned, persistence is key. The journey of a developer comes with challenges, but it’s also incredibly rewarding. Stay committed, keep learning, and trust that your efforts will pay off over time. The code you write today has the potential to make a real impact.&lt;/p&gt;

&lt;p&gt;It’s normal to feel discouraged sometimes, even the most experienced ones, has faced similar struggles. If you're feeling stuck, remember that it's just part of the learning process. Even I come across difficult topics that feel overwhelming at times, but with consistent effort, improvement always follows. Keep going, you’re making progress every day.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Beginner Stage
&lt;/h2&gt;

&lt;p&gt;As a beginner, it often feels like you're on an emotional rollercoaster. Some days, you’re unstoppable, completely focused on coding, excited about your progress, and confident that you’ve found your passion. On other days, the challenges feel too difficult. &lt;br&gt;
A tricky concept or a bug that’s hard to fix might make you question your path, thinking, “Is this really what I want to do for the rest of my life?” It’s frustrating, and it’s easy to feel like the struggle is holding you back.&lt;/p&gt;
&lt;h3&gt;
  
  
  Navigating the Highs and Lows
&lt;/h3&gt;

&lt;p&gt;This stage is tough, but it’s also crucial. It’s a phase where you’ll inevitably make mistakes and lots of them. There are many reasons for this. Perhaps you’re rushing to land your first job in tech, or maybe you’ve taken on low budget projects just to make ends meet. These pressures can lead to poor decisions, overly optimistic timelines, and code that isn’t as clean or efficient as you’d like. But here’s the truth, this is normal. Every seasoned developer you admire has been through this phase. It’s a rite of passage that helps you grow. You may feel like you’re just stumbling along, but every misstep teaches you something valuable. If you keep pushing forward, you’ll find your rhythm and reach the next level of your journey.&lt;/p&gt;

&lt;p&gt;I can confidently tell you this, it’s all worth it. The struggles, the late nights, and even the failed projects they all contribute to your development as a programmer. So, keep coding. Keep learning. The only way to improve is to persevere.&lt;/p&gt;
&lt;h3&gt;
  
  
  Finding Strength in the Beginner Stage
&lt;/h3&gt;

&lt;p&gt;While this stage might feel overwhelming, it’s also one of the most transformative phases of your journey. It’s here that you develop qualities that will serve you for the rest of your career. Among the most important are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Willpower: The determination to keep going, even when the odds feel stacked against you.&lt;/li&gt;
&lt;li&gt;Curiosity: An insatiable hunger to learn more and explore uncharted territories in programming.&lt;/li&gt;
&lt;li&gt;Ambition: The drive to push past your limits and achieve your goals.&lt;/li&gt;
&lt;li&gt;Resilience: The courage to embrace failures and use them as stepping stones toward success.
Humility: The willingness to accept feedback, no matter how critical, and use it to improve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These traits are your superpowers during this stage. Use them to fuel your growth. Let your ambition and curiosity guide you to new knowledge. Push yourself to take on challenges that seem just beyond your reach. And when you stumble, remember that mistakes are simply opportunities to learn.&lt;/p&gt;

&lt;p&gt;At this stage, it’s also important to focus on the bigger picture. Yes, you and I have goals whether that’s building better apps, landing a dream job, or contributing to impactful projects. But our purpose as developers goes beyond personal achievements. With every line of code, we have the opportunity to add value to the world. By solving problems, simplifying processes, and sharing our knowledge, we can create meaningful change.&lt;/p&gt;
&lt;h3&gt;
  
  
  Plan First, Code Later
&lt;/h3&gt;

&lt;p&gt;A great real world example of the "Plan First, Code Later" approach is how Instagram was initially developed.&lt;/p&gt;

&lt;p&gt;In 2010, Instagram (then called "Burbn") was just an idea in the minds of its founders, Kevin Systrom and Mike Krieger. Instead of jumping straight into coding, they followed a structured planning approach before writing a single line of code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iv87w8x8k5qky8fed6b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iv87w8x8k5qky8fed6b.jpg" alt="Image description" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Case Study: Instagram's Early Development
&lt;/h4&gt;

&lt;p&gt;Background&lt;br&gt;
In 2010, Instagram (then called "Burbn") was just an idea in the minds of its founders, Kevin Systrom and Mike Krieger. Instead of jumping straight into coding, they followed a structured planning approach before writing a single line of code.&lt;/p&gt;

&lt;p&gt;Step 1: Analyzing Product Requirements&lt;br&gt;
Initially, Burbn was a location based check-in app with a photo sharing feature. After conducting market research and analyzing user feedback, the team realized that users were mainly interested in the photo-sharing feature rather than the check-ins.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lesson: Instead of coding an unnecessary check-in system, they refined their product requirements and pivoted towards a dedicated photo sharing app.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Step 2: System Architecture &amp;amp; Scalability Planning&lt;br&gt;
Before coding, the team planned how the backend should handle millions of photos and user interactions. They chose AWS for hosting and used Django as their backend framework for rapid development while ensuring scalability.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lesson: Without proper planning, they could have picked a technology stack that wouldn't scale effectively.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Step 3: UX/UI Planning Before Development&lt;br&gt;
They designed a simple and intuitive user interface before writing any code. They focused on a minimalist approach, reducing clutter and unnecessary features, leading to an easy to use app.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lesson: By planning the UI/UX beforehand, they avoided the need for major redesigns after coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outcome&lt;/strong&gt;&lt;br&gt;
Because of their careful planning and iterative approach, Instagram launched with a lightweight, focused, and scalable system. Within two months, it gained 1 million users, proving the power of planning before coding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdbcfam59frvbszvjojh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdbcfam59frvbszvjojh.png" alt="Image description" width="566" height="560"&gt;&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://strategizeyourcareer.com/p/ask-first-code-later" rel="noopener noreferrer"&gt;strategizeyourcareer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In programming, clarity in requirements, design, and implementation plays a crucial role in minimizing the number of fixes or bugs. Let's break this down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Clarify Early → Fewer Fixes Later&lt;br&gt;
When requirements are well defined, developers know exactly what to build. This prevents misunderstandings and unnecessary revisions.&lt;br&gt;
A clear design ensures that architectural decisions are sound from the start, reducing structural issues later.&lt;br&gt;
Writing clean and well documented code makes debugging and future maintenance easier, reducing the chances of introducing new bugs when fixing existing ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lack of Clarity → More Fixes Needed&lt;br&gt;
If requirements are vague, developers might make incorrect assumptions, leading to features that don’t match expectations.&lt;br&gt;
A poorly designed system can cause scalability and maintainability problems, requiring constant refactoring.&lt;br&gt;
Ambiguous or unstructured code can lead to hard to find bugs, increasing debugging time and causing more regressions. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As a beginner, you’ve likely experienced the highs and lows of learning to code. The excitement of progress mixed with the frustration of setbacks is part of the process. But here’s something that can make this stage more manageable: developing a strong foundation in planning before you dive into coding.&lt;/p&gt;

&lt;p&gt;Let's take an example of "Plan First, Code Later" in a real coding scenario using Flutter&lt;/p&gt;
&lt;h4&gt;
  
  
  Before Planning (Jumping Straight into Code)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserListScreen extends StatefulWidget {
  @override
  _UserListScreenState createState() =&amp;gt; _UserListScreenState();
}

class _UserListScreenState extends State&amp;lt;UserListScreen&amp;gt; {
  List&amp;lt;dynamic&amp;gt; users = [];

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

  Future&amp;lt;void&amp;gt; fetchUsers() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
    if (response.statusCode == 200) {
      setState(() {
        users = json.decode(response.body);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Users')),
      body: users.isEmpty
          ? Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(users[index]['name']),
                  subtitle: Text(users[index]['email']),
                );
              },
            ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Problems with This Approach&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mixes UI and Business Logic: The fetchUsers() function is inside the UI, making it hard to test and maintain.&lt;/li&gt;
&lt;li&gt;Tight Coupling: If the API changes, we must modify the UI code as well.&lt;/li&gt;
&lt;li&gt;No Error Handling: What if the API fails? There’s no proper error message.&lt;/li&gt;
&lt;li&gt;Not Scalable: If we add caching or other data sources (like a database), we will need major rewrites.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  After Planning (More Effective &amp;amp; Maintainable Code)
&lt;/h4&gt;

&lt;p&gt;Step 1: Define a Model (User.dart)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  factory User.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Separate Data Layer (UserService.dart)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:convert';
import 'package:http/http.dart' as http;
import 'user.dart';

class UserService {
  Future&amp;lt;List&amp;lt;User&amp;gt;&amp;gt; fetchUsers() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));

    if (response.statusCode == 200) {
      List&amp;lt;dynamic&amp;gt; data = json.decode(response.body);
      return data.map((json) =&amp;gt; User.fromJson(json)).toList();
    } else {
      throw Exception('Failed to load users');
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3: Use State Management (UserProvider.dart)
&lt;/h4&gt;



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

class UserProvider extends ChangeNotifier {
  final UserService _userService = UserService();
  List&amp;lt;User&amp;gt; _users = [];
  bool _isLoading = true;
  String? _error;

  List&amp;lt;User&amp;gt; get users =&amp;gt; _users;
  bool get isLoading =&amp;gt; _isLoading;
  String? get error =&amp;gt; _error;

  UserProvider() {
    fetchUsers();
  }

  Future&amp;lt;void&amp;gt; fetchUsers() async {
    try {
      _isLoading = true;
      notifyListeners();

      _users = await _userService.fetchUsers();
      _error = null;
    } catch (e) {
      _error = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 4: UI with Separation of Concerns (UserListScreen.dart)
&lt;/h4&gt;



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

class UserListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Users')),
      body: Consumer&amp;lt;UserProvider&amp;gt;(
        builder: (context, userProvider, child) {
          if (userProvider.isLoading) {
            return Center(child: CircularProgressIndicator());
          }

          if (userProvider.error != null) {
            return Center(child: Text('Error: ${userProvider.error}'));
          }

          return ListView.builder(
            itemCount: userProvider.users.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(userProvider.users[index].name),
                subtitle: Text(userProvider.users[index].email),
              );
            },
          );
        },
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 5: Setup in main.dart
&lt;/h4&gt;



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

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) =&amp;gt; UserProvider()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: UserListScreen(),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits of This Planned Approach&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Separation of Concerns – Business logic, API calls, and UI are separate.&lt;/li&gt;
&lt;li&gt;Scalability – Easy to add caching, local storage, or database.&lt;/li&gt;
&lt;li&gt;Better Error Handling – Provides clear error messages.&lt;/li&gt;
&lt;li&gt;Easier Testing – Can unit test the UserService without involving UI.&lt;/li&gt;
&lt;li&gt;Less Boilerplate in UI – The UI is now focused only on presentation.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;When starting a new project or feature, it’s tempting to jump straight into coding because it feels productive. However, skipping the planning phase often leads to inefficiencies, unnecessary mistakes, and wasted effort. Taking the time to plan ensures a smoother development process and helps create cleaner, more maintainable code.&lt;/p&gt;

&lt;p&gt;Planning doesn’t always mean lengthy documentation. For small tasks, it might be as simple as confirming requirements: “You want the button text changed to blue, right?” For larger projects, it involves defining specifications, gathering feedback, and securing approval. Without this step, you risk solving the wrong problem or overlooking key considerations.&lt;/p&gt;

&lt;p&gt;Writing code too soon can also make it harder to communicate ideas, especially with non technical colleagues. Conversations, diagrams, and brainstorming sessions are often faster ways to clarify concepts than jumping into code. While coding can sometimes help explore a problem, any early code should be treated as a rough draft rather than a final solution.&lt;/p&gt;

&lt;p&gt;Even informal planning breaking down a task into steps or considering potential challenges can help avoid issues later. By thinking ahead, you can refine your approach before writing code, ultimately saving time and reducing frustration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good planning doesn’t slow you down; it helps you move faster by preventing costly mistakes and ensuring you build the right solution from the start.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thank you for reading this article! I hope it helps you understand the importance of planning before coding and how it can lead to better, more efficient development. Happy coding! 🚀&lt;/p&gt;




&lt;p&gt;References:&lt;br&gt;
&lt;a href="https://letterstoanewdeveloper.com/2020/10/19/plan-first-then-code/" rel="noopener noreferrer"&gt;letterstoanewdeveloper&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.linkedin.com/pulse/design-first-code-later-michael-rothrock/" rel="noopener noreferrer"&gt;Michael Rothrock&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.theatlantic.com/technology/archive/2014/07/instagram-used-to-be-called-brbn/373815/" rel="noopener noreferrer"&gt;Instagram Was First Called 'Burbn'&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>Flutter Image Compression: Ensuring High Quality</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Thu, 14 Nov 2024 06:05:58 +0000</pubDate>
      <link>https://forem.com/tentanganak/flutter-image-compression-ensuring-high-quality-25m1</link>
      <guid>https://forem.com/tentanganak/flutter-image-compression-ensuring-high-quality-25m1</guid>
      <description>&lt;p&gt;In today's digital age, we often find ourselves taking countless photos and capturing memories on our devices. As developers, we want to make sure these images are not just beautiful but also efficient when it comes to uploading them to a server. This is where image compression comes into play. &lt;/p&gt;

&lt;p&gt;In this article, we’ll explore the ins and outs of image compression for Flutter, focusing specifically on how to balance quality and performance for seamless uploads. Whether you're building a social media app or a photo-sharing platform, understanding the art of compression can greatly enhance user experience while keeping your application running smoothly. Let's dive in!&lt;/p&gt;




&lt;p&gt;Image compression is the process of reducing the file size of an image without significantly compromising its quality. It involves removing unnecessary data or utilizing algorithms to make the file smaller. This is essential for several reasons, especially when uploading images to a server.&lt;/p&gt;

&lt;p&gt;Firstly, larger image sizes can lead to longer upload times. For users with slower internet connections or limited bandwidth, this delay can result in frustration and a poor overall experience. By compressing images before uploading, developers can ensure faster transfer speeds, making the process more seamless for users.&lt;/p&gt;

&lt;p&gt;Secondly, uploading large images can strain server resources. This can lead to increased storage costs and slower response times for other users accessing the server. By utilizing image compression, developers not only enhance the user experience but also optimize server performance and efficiency.&lt;/p&gt;

&lt;p&gt;So, understanding and implementing image compression is vital for any Flutter developer looking to improve their app's performance and user satisfaction, particularly in scenarios where image uploads are a common requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Image Conversion
&lt;/h2&gt;

&lt;p&gt;When it comes to image compression, there are two main types: lossy compression and lossless compression. Each has its pros and cons, making them suitable for different situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lossy Compression
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu69ylrhfblpcty8ym72d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu69ylrhfblpcty8ym72d.jpg" alt="Source: https://news.txst.edu/research-and-innovation/2021/txst-researchers-to-explore-state-of-the-art-lossy-compression.html" width="800" height="229"&gt;&lt;/a&gt;&lt;a href="https://news.txst.edu/research-and-innovation/2021/txst-researchers-to-explore-state-of-the-art-lossy-compression.html" rel="noopener noreferrer"&gt;Image Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it is&lt;/strong&gt;: Lossy compression reduces image size by permanently removing some of the image’s data. It focuses on discarding details that are less noticeable to the human eye, making the file significantly smaller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact on Quality&lt;/strong&gt;: This approach often leads to a small reduction in image quality, which may or may not be visible depending on the level of compression used. Higher compression ratios (smaller file sizes) often result in more noticeable quality loss, like blurring or pixelation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use Cases&lt;/strong&gt;: Ideal for photos or images with lots of colors and gradients, like user-uploaded images or social media photos. JPEG is a common format that uses lossy compression.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lossless Compression
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq0rk614dfsqwqvrkazw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq0rk614dfsqwqvrkazw.png" alt="Source: https://cyberhoot.com/cybrary/lossless-compression/" width="800" height="450"&gt;&lt;/a&gt;&lt;a href="https://cyberhoot.com/cybrary/lossless-compression/" rel="noopener noreferrer"&gt;Image Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it is&lt;/strong&gt;: Lossless compression reduces file size without removing any image data. It achieves smaller files by finding and compressing patterns within the image, but no data is lost, so you can fully restore the original image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact on Quality&lt;/strong&gt;: Since no data is discarded, image quality remains intact. However, lossless compression doesn’t reduce file size as dramatically as lossy compression, especially for complex images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use Cases&lt;/strong&gt;: Suitable for graphics, icons, or images where high fidelity is crucial, like logos or text-heavy images. PNG and GIF formats often use lossless compression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Between Lossy and Lossless
&lt;/h2&gt;

&lt;p&gt;When Quality is Crucial: Use lossless compression to retain full detail, especially if users may zoom in or if the image contains text or important details.&lt;/p&gt;

&lt;p&gt;When Reducing File Size is Key: Use lossy compression when you need a smaller file size and can afford slight quality loss, especially for thumbnails or images in feed views where high resolution isn't necessary.&lt;/p&gt;

&lt;p&gt;In summary, the choice between lossy and lossless compression hinges on your specific needs: prioritize speed and smaller files with lossy compression, or opt for higher quality with lossless compression. Understanding these options will help you optimize image uploads in your Flutter app.&lt;/p&gt;




&lt;p&gt;In this article, we'll focus more on lossy compression to achieve slight quality loss and smaller file sizes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Original (974Kb)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczc9jtrgkzpl3knvf4ag.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczc9jtrgkzpl3knvf4ag.jpg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;a href="https://www.goodfon.com/cats/wallpaper-koshka-dom-uiut-27.html" rel="noopener noreferrer"&gt;Image Source&lt;/a&gt;&lt;br&gt;
I have this original size file with 974Kb file size.&lt;/p&gt;

&lt;p&gt;And then i do compression with 90% Quality.&lt;/p&gt;
&lt;h3&gt;
  
  
  90% Quality (318Kb)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx2ztfkay7cj4my80ytq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx2ztfkay7cj4my80ytq.jpeg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;With 90% quality we can achieve the compression rate at approximately 67.35% from 974Kb to 318Kb.&lt;/p&gt;
&lt;h3&gt;
  
  
  50% Quality (109Kb)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6km1p6pij1wdo4yj2lx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6km1p6pij1wdo4yj2lx.jpeg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;With 50% quality we can achieve the compression rate at approximately 88.81% from 974Kb to 109Kb.&lt;/p&gt;
&lt;h3&gt;
  
  
  10% Quality (55Kb)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fed1mwvzfhujcx6m7i0e2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fed1mwvzfhujcx6m7i0e2.jpeg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;With 10% quality we can achieve the compression rate at approximately 94.35% from 974Kb to 55Kb.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifkqkedcgj9a7do3sxwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifkqkedcgj9a7do3sxwa.png" alt="Image description" width="800" height="200"&gt;&lt;/a&gt;Based on the quality comparisons above, you can decide which compression level works best for your needs. If image quality is a priority (for product photos or detailed images), higher quality settings (e.g., 90 or above) should be used to maintain clarity, though they result in larger file sizes.&lt;/p&gt;

&lt;p&gt;If file size is more important, such as for thumbnails or gallery images, lower quality settings (e.g., 50 or even 10) can be used to significantly reduce file size, though some visual degradation will occur. The choice depends on whether you prioritize visual fidelity or performance and size optimization.&lt;/p&gt;


&lt;h2&gt;
  
  
  Code Implementation
&lt;/h2&gt;

&lt;p&gt;Here’s an example of a function to compress an image in Flutter using the flutter_image_compress package:&lt;/p&gt;

&lt;p&gt;You only need these two packages:&lt;br&gt;
&lt;a href="https://pub.dev/packages/flutter_image_compress" rel="noopener noreferrer"&gt;Flutter Image Compress Package&lt;/a&gt;&lt;br&gt;
&lt;a href="https://pub.dev/packages/image_picker" rel="noopener noreferrer"&gt;Flutter Image Picker&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Pick Image
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:image_picker/image_picker.dart';

final ImagePicker picker = ImagePicker();
File? selectedImage;

Future&amp;lt;void&amp;gt; pickImage() async {
    final pickedFile = await picker.pickImage(source: ImageSource.gallery);
    if (pickedFile != null) {
      setState(() {
        selectedImage = File(pickedFile.path);
      });
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Compress Image
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:path/path.dart' as path;
import 'package:flutter_image_compress/flutter_image_compress.dart';

Future&amp;lt;XFile&amp;gt; compressImageFile(
    {
      required File imageFile, 
      int quality = 80, 
      CompressFormat format = CompressFormat.jpeg
    }) async {

    DateTime time = DateTime.now();
    final String targetPath = path.join(
      Directory.systemTemp.path, 'imagetemp-${format.name}-$quality-${time.second}.${format.name}'
    );

    final XFile? compressedImageFile = await FlutterImageCompress.compressAndGetFile(
      imageFile.path,
      targetPath,
      quality: quality,
      format: format
    );

    if (compressedImageFile == null){
      throw ("Image compression failed! Please try again.");
    }
    debugPrint("Compressed image saved to: ${compressedImageFile.path}");
    return compressedImageFile;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;UI Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    if (selectedImage != null) 
      Image.file(
        selectedImage!
      ),
    TextButton(
      onPressed: () async {
        await pickImage();
      }, 
      child: const Text("Pick Image")
    ),

    ElevatedButton(
      onPressed: () {
        if (selectedImage != null) {
          compressImageFile(
            imageFile: selectedImage!,
            quality: 10
          );
        }
      }, 
      child: const Text("Compress")
    )
  ],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd like to view the code for the sample usage app above, please visit:&lt;br&gt;
&lt;a href="https://github.com/irfanadisukma/flutter-image-compression" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References:&lt;br&gt;
&lt;a href="https://www.dhiwise.com/post/guide-to-improving-app-performance-with-flutter-resize-image" rel="noopener noreferrer"&gt;www.dhiwise.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.adobe.com/uk/creativecloud/photography/discover/lossy-compression.html#:~:text=By%20definition%2C%20lossy%20compression%20removes,hence%20the%20term%20'lossy'." rel="noopener noreferrer"&gt;www.adobe.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://cyberhoot.com/cybrary/lossless-compression/" rel="noopener noreferrer"&gt;cyberhoot.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>compression</category>
      <category>image</category>
    </item>
    <item>
      <title>Flutter Scrolling Techniques: Using GlobalKey and Scrollable.ensureVisible() for Precise Navigation</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Fri, 27 Sep 2024 09:27:02 +0000</pubDate>
      <link>https://forem.com/tentanganak/flutter-scrolling-techniques-using-globalkey-and-scrollableensurevisible-for-precise-navigation-59pf</link>
      <guid>https://forem.com/tentanganak/flutter-scrolling-techniques-using-globalkey-and-scrollableensurevisible-for-precise-navigation-59pf</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3581ev2nsshkgqaeh5o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3581ev2nsshkgqaeh5o.gif" alt="https://dribbble.com/shots/4697421-Scroll-Scroll"&gt;&lt;/a&gt;&lt;br&gt;
In Flutter development, creating smooth and intuitive user experiences often involves guiding users to specific parts of the interface. One powerful technique to achieve this is programmatically scrolling to a particular widget. Whether it’s navigating to a section in a long form, focusing on a specific item in a list, or highlighting a new message in a chat, the ability to scroll to a specific widget enhances the overall usability and interactivity of an app.&lt;/p&gt;

&lt;p&gt;Flutter provides several tools, such as &lt;strong&gt;GlobalKey&lt;/strong&gt;, and &lt;strong&gt;Scrollable.ensureVisible()&lt;/strong&gt;, that allow developers to precisely control scrolling behavior. By leveraging these tools, developers can ensure that important content is brought into view, reduce the need for excessive manual scrolling, and create more seamless navigation experiences. This not only helps in improving user engagement but also makes the application more accessible and efficient, especially for apps with complex layouts or dynamic content.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how to effectively scroll to a specific widget in Flutter using these techniques, ensuring that your app's navigation is as smooth and user-friendly as possible.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Components
&lt;/h2&gt;

&lt;p&gt;When building a Flutter app, having control over how users navigate through content is crucial for creating a smooth and engaging experience. To achieve precise scrolling behavior, three key components can be used: GlobalKey, and Scrollable.ensureVisible(). Let's dive deeper into each of these components and understand their roles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GlobalKey&lt;/strong&gt;: In Flutter, GlobalKey is a type of key that is used to uniquely identify a widget within the entire widget tree. It enables developers to access the widget's context, state, and position, which is essential when you need to perform actions such as scrolling to a particular widget or manipulating its state. By assigning a GlobalKey to a widget, you can easily reference it later in your code to trigger actions like scrolling, focusing, or even retrieving information about its dimensions and position. And it's compatible with all Flutter versions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scrollable.ensureVisible()&lt;/strong&gt;: The Scrollable.ensureVisible() method is a utility function provided by Flutter that ensures a given widget is visible within its scrollable ancestor. When called, it automatically calculates the distance and direction to scroll in order to bring the target widget into view. This is particularly useful for creating smooth and automated scrolling effects, such as jumping to a specific section of a form, highlighting a newly added item in a list, or scrolling to a widget based on user actions (e.g., clicking a button). By combining Scrollable.ensureVisible() with GlobalKey, developers can programmatically control the visibility of widgets, enhancing navigation and user experience. This feature available and supported from Flutter 1.5.4 onwards. So make sure you are using latest version.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding these two components— GlobalKey, and Scrollable.ensureVisible()—you can effectively implement advanced scrolling behaviors in your Flutter app, ensuring that users can easily navigate and interact with content in a seamless and intuitive way.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating the UI
&lt;/h2&gt;

&lt;p&gt;To demonstrate, let's create a simple UI containing a list of items.&lt;/p&gt;

&lt;p&gt;Step-by-Step Example&lt;br&gt;
Here is a basic example of setting up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ListView.builder(
 shrinkWrap: true,
 itemCount: 50,
 physics: const NeverScrollableScrollPhysics(),
 itemBuilder: (context, index) {
   return ListTile(title: Text("Item $index"));
 }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's add GlobalKey to identify widgets&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final GlobalKey _scrollKey = GlobalKey();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assign this key to the widget within your ListView:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ListTile(
  key: _scrollKey,
  title: Text('Scroll to Me!'),
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the code should be like this with additional handling to override generated list item with the GlobalKey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ListView.builder(
 shrinkWrap: true,
 itemCount: 50,
 physics: const NeverScrollableScrollPhysics(),
 itemBuilder: (context, index) {
   if (index == 25) {
     return ListTile(
      key: _scrollKey,
      title: Text("Scroll to Me!")
     );
   }
   return ListTile(title: Text("Item $index"));
 }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's add ScrollC&lt;/p&gt;

&lt;p&gt;Let's add one final touch by adding Button to trigger the scroll using &lt;strong&gt;Scrollable.ensureVisible()&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void _scrollToTarget() {
  // Ensure the widget is built
  WidgetsBinding.instance.addPostFrameCallback((_) {
    final context = _scrollKey.currentContext;
    if (context != null) {
      Scrollable.ensureVisible(
        context,
        duration: const Duration(seconds: 1), 
        curve: Curves.easeInOut);
      }
   });
 }

ListView(
 children: [
  ElevatedButton(
    onPressed: () async {
      _scrollToTarget();
    },
    child: const Text('Scroll to Widget'),
  ),
  ListView.builder(
    shrinkWrap: true,
    itemCount: 50,
    physics: const NeverScrollableScrollPhysics(),
    itemBuilder: (context, index) {
      if (index == 25) {
       return ListTile(
        key: _scrollKey,
        title: Text("Scroll to Me!")
       );
      }
      return ListTile(title: Text("Item $index"));
     }
   )
  ],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the output!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp13197b8saqjahfsxgco.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp13197b8saqjahfsxgco.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How Scrollable.ensureVisible works?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Scrollable.ensureVisible() method takes in the BuildContext of a widget and scrolls the nearest scrollable parent to make that widget fully visible. &lt;/p&gt;

&lt;p&gt;When Scrollable.ensureVisible() is called, Flutter calculates the position of the widget represented by the context in its scrollable ancestor.&lt;br&gt;
If the widget is already fully visible, no scrolling occurs. If not, the method scrolls the closest scrollable widget (like CustomScrollView, ListView, etc.) to bring the target widget into view.&lt;br&gt;
The scrolling animation takes place over the specified duration and follows the provided curve, resulting in a smooth scroll to the target widget.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dbrl94yl9l4ki9ys8vz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dbrl94yl9l4ki9ys8vz.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is real example of how we implement this technique on Tentang Anak App, we have a feature section that must be highlighted and we help user to find this section by tapping the button, and it will automatically scroll to the specific section.&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Tips
&lt;/h2&gt;

&lt;p&gt;When implementing scrolling to a specific widget in Flutter using  GlobalKey, and Scrollable.ensureVisible(), it's essential to consider performance and handle potential edge cases to ensure a smooth and responsive user experience. Here are some additional tips to help you optimize your implementation:&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Considerations
&lt;/h3&gt;

&lt;p&gt;When working with large lists or complex scrollable content, performance optimization is crucial. Here are some strategies to enhance scrolling and rendering performance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Avoid Unnecessary Rebuilds: Ensure that widgets within your ListView do not rebuild unnecessarily. Use const constructors where possible, and consider using state management solutions like Provider or Riverpod to manage the state outside of the widget tree, avoiding frequent rebuilds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Virtualization with ListView.builder and GridView.builder: If your use case involves scrolling a long list of items (e.g., 100+ items), consider using ListView.builder or GridView.builder instead of SliverList to optimize memory usage. These builders only render visible items and reuse widgets that move out of the viewport.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Throttle or Debounce User Actions: If you allow users to trigger scrolling actions frequently (e.g., tapping buttons to scroll to different items), consider throttling or debouncing these actions to prevent excessive animations and rendering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the automaticKeepAlive property: For complex widgets that need to maintain their state while scrolling, consider using AutomaticKeepAlive widgets to manage their lifecycles better without unnecessarily keeping off-screen widgets alive.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Handling Edge Cases
&lt;/h3&gt;

&lt;p&gt;Proper handling of edge cases ensures that your scroll functionality works reliably in all scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ensure Widget Exists in the Viewport: Before calling Scrollable.ensureVisible(), ensure that the target widget is part of the scrollable content. If the widget is dynamically added or removed (e.g., in response to user actions), you need to verify that it is indeed within the scrollable ancestor when the method is called. Failing to do so can result in exceptions or unexpected behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle Already Visible Widgets: If the widget is already fully visible in the viewport, Scrollable.ensureVisible() won’t trigger any scrolling. Make sure to handle such cases gracefully to avoid redundant animations or confusing the user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle Partially Visible Widgets: Consider scenarios where the widget is only partially visible (e.g., cut off at the top or bottom of the viewport). Scrollable.ensureVisible() will scroll to bring it fully into view, but you might want to adjust the duration or curve for smoother transitions, especially if only a small amount of scrolling is needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check for Nested Scroll Views: If you have nested scroll views, calling Scrollable.ensureVisible() may not work as expected if the target widget is inside a nested scroll view. Ensure that the method is called on the correct scrollable parent and that the widget’s context is properly obtained from the right GlobalKey.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manage Focus and Accessibility: After scrolling to a widget, you may want to give it focus (e.g., for an input field) or announce it to assistive technologies. Use FocusScope.of(context).requestFocus() or Semantics to enhance accessibility and provide visual feedback.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consider Scroll Offset Adjustments: Sometimes, you may want to scroll a bit past the widget or stop before it reaches the top or bottom of the viewport to provide some padding or context. You can use alignment or calculate offsets manually to achieve more precise positioning.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By considering these performance tips and handling edge cases, you can implement scrolling to specific widgets in a way that is both efficient and robust, ensuring a seamless and intuitive experience for your Flutter app users. This approach not only improves usability but also contributes to a polished, professional app that handles a variety of user interactions gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Scrolling to a specific widget in Flutter can significantly enhance the user experience by ensuring that important content is always visible and easily accessible. &lt;/p&gt;

&lt;p&gt;In summary, using GlobalKey, and Scrollable.ensureVisible() together provides a powerful toolkit for creating responsive and user-friendly scrolling experiences in Flutter apps. This approach not only guides users through different sections of an app smoothly but also enhances the overall usability and accessibility, leading to a more polished and professional application.&lt;/p&gt;

&lt;p&gt;See the full code&lt;br&gt;
&lt;a href="https://github.com/irfanadisukma/auto-scroll-widget" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>flutterwidget</category>
    </item>
    <item>
      <title>Revenue Potential: Earn Money with Flutter and Google AdMob</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Fri, 28 Jun 2024 15:47:34 +0000</pubDate>
      <link>https://forem.com/madisukma/revenue-potential-earn-money-with-flutter-and-google-admob-1ilk</link>
      <guid>https://forem.com/madisukma/revenue-potential-earn-money-with-flutter-and-google-admob-1ilk</guid>
      <description>&lt;p&gt;Flutter, Google’s open-source UI toolkit, is widely used for creating natively compiled applications across mobile, web, and desktop platforms from a single codebase. By incorporating Google AdMob, you can effectively monetize these apps. This guide will walk you through the process.&lt;/p&gt;

&lt;p&gt;Google AdMob is a robust mobile advertising platform developed by Google, specifically designed to help developers monetize their mobile applications through various ad formats. AdMob supports both Android and iOS platforms, making it versatile for developers targeting multiple mobile operating systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features of Google AdMob:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ad Formats: AdMob offers various ad formats including:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Banner Ads: These are rectangular ads that appear at the top or bottom of the screen. They are typically less intrusive and can be easily integrated into different parts of the app's layout.&lt;/li&gt;
&lt;li&gt;Interstitial Ads: Full-screen ads that cover the interface of an app. They are often displayed at natural transition points within the app, such as between levels of a game or when switching content in a news app.&lt;/li&gt;
&lt;li&gt;Rewarded Ads: Users can choose to watch these ads in exchange for in-app rewards, such as virtual currency or additional content. They provide a positive user experience by giving something valuable in return for their time.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Monetization Models: AdMob supports various monetization models:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Cost Per Click (CPC): Advertisers pay each time a user clicks on the ad.&lt;/li&gt;
&lt;li&gt;Cost Per Thousand Impressions (CPM): Advertisers pay for every thousand times the ad is shown&lt;/li&gt;
&lt;li&gt;Cost Per Acquisition (CPA): Advertisers pay when a specific action is completed, such as an app install or sign-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Targeting and Optimization:&lt;br&gt;
AdMob provides advanced targeting options based on user demographics, interests, and behavior. This helps in delivering relevant ads to maximize engagement and revenue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration and Reporting:&lt;br&gt;
Integrating AdMob into a Flutter app involves adding the AdMob SDK to your project and configuring ad units. Google provides comprehensive reporting tools to track ad performance, impressions, clicks, and revenue generated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Policy Compliance:&lt;br&gt;
AdMob enforces strict policies to ensure a positive user experience and maintain advertiser trust. Developers must adhere to these policies regarding ad content, placement, and behavior within their apps.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Benefits of Using Google AdMob:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue Generation: AdMob provides developers with a straightforward way to generate revenue from their apps through ads, without requiring direct payments from users.&lt;/li&gt;
&lt;li&gt;Cross-Platform Support: AdMob supports both Android and iOS platforms, allowing developers to monetize their apps across different devices and operating systems.&lt;/li&gt;
&lt;li&gt;Easy Integration: Integrating AdMob into Flutter apps is relatively simple with clear documentation and SDK support provided by Google.&lt;/li&gt;
&lt;li&gt;Analytics and Optimization: Developers can use AdMob’s analytics tools to monitor ad performance and optimize their monetization strategy based on real-time data.&lt;/li&gt;
&lt;li&gt;Global Reach: AdMob connects developers with a global network of advertisers, ensuring a wide range of ad inventory and opportunities for monetization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging Google AdMob, Flutter developers can effectively monetize their applications while maintaining a positive user experience through relevant and engaging advertisements. It's essential for developers to understand AdMob’s capabilities and best practices to maximize their app’s revenue potential.&lt;/p&gt;




&lt;p&gt;Sign Up for AdMob:&lt;br&gt;
Create an AdMob account at &lt;a href="//admob.google.com"&gt;Google AdMob&lt;/a&gt; and set-up your account.&lt;/p&gt;

&lt;p&gt;Add your application&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf7sr1xcgif4ofsfpboo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf7sr1xcgif4ofsfpboo.png" alt="Image description" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setup your application based on the available options&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rrnkdz7q5cyelm94m1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rrnkdz7q5cyelm94m1q.png" alt="Image description" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill the application name:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1yrk5eja4ucn2urwif5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1yrk5eja4ucn2urwif5.png" alt="Image description" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now you have successfully add your application to AdMob&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt8ahmplzfva20dmpws0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt8ahmplzfva20dmpws0.png" alt="Image description" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgwyh6wc2s5p79kl5wrk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgwyh6wc2s5p79kl5wrk.png" alt="Image description" width="800" height="378"&gt;&lt;/a&gt;&lt;br&gt;
Create an Ad Unit:&lt;br&gt;
Create a new app and generate an ad unit ID. You’ll use this ID to set up ads in your Flutter app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81h6j3avazb7gn8svb31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81h6j3avazb7gn8svb31.png" alt="Image description" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
Select the ad units that you want to address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbrhcrsrv2a6ssb21ykr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbrhcrsrv2a6ssb21ykr.png" alt="Image description" width="800" height="490"&gt;&lt;/a&gt;&lt;br&gt;
In this example, i choose Banner and name it Homepage Banner as i will show the ad on home page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsus9h2bttsvgt3yql8u7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsus9h2bttsvgt3yql8u7.png" alt="Image description" width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9v8glo97usabia37rf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9v8glo97usabia37rf9.png" alt="Image description" width="800" height="263"&gt;&lt;/a&gt;&lt;br&gt;
Now we have ad unit created.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Now, let's integrate our Flutter App with Google AdMob.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Integrating AdMob with Flutter&lt;br&gt;
Add Dependencies:&lt;br&gt;
Open your pubspec.yaml file and add the &lt;a href="https://pub.dev/packages/google_mobile_ads"&gt;&lt;em&gt;google_mobile_ads&lt;/em&gt;&lt;/a&gt; dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  flutter:
    sdk: flutter
  google_mobile_ads: ^1.0.0

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Initialize AdMob&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your main.dart file, initialize AdMob:&lt;br&gt;
&lt;/p&gt;

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Ecommerce App'),
        ),
        body: MyHomePage(),
      ),
    );
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Load and Display Ads&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, I will provide examples of implementing AdMob for Banner Ads and Interstitials. For examples of other types of ads, you can read more &lt;a href="https://support.google.com/admob/answer/6128738?sjid=17584835076702521641-AP"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/admob/android/banner"&gt;Banner ads&lt;/a&gt; are rectangular ads that occupy a portion of an app's layout. They stay on screen while users are interacting with the app, either anchored at the top or bottom of the screen or inline with content as the user scrolls. Banner ads can refresh automatically after a certain period of time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  BannerAd? _bannerAd;

  @override
  void initState() {
    super.initState();
    _bannerAd = BannerAd(
      adUnitId: '&amp;lt;AD-UNIT-ID&amp;gt;',
      size: AdSize.banner,
      request: AdRequest(),
      listener: BannerAdListener(),
    )..load();
  }

  @override
  void dispose() {
    _bannerAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: &amp;lt;Widget&amp;gt;[
        if (_bannerAd != null)
          Container(
            alignment: Alignment.center,
            child: AdWidget(ad: _bannerAd!),
            width: _bannerAd!.size.width.toDouble(),
            height: _bannerAd!.size.height.toDouble(),
          ),
      ],
    );
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developers.google.com/admob/android/interstitial"&gt;Interstitial&lt;/a&gt; ads are full-screen ads that cover the interface of their host app. They're typically displayed at natural transition points in the flow of an app, such as between activities or during the pause between levels in a game. When an app shows an interstitial ad, the user has the choice to either tap on the ad and continue to its destination or close it and return to the app.&lt;br&gt;
&lt;/p&gt;

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

void _createInterstitialAd() {
  InterstitialAd.load(
    adUnitId: '&amp;lt;AD-UNIT-ID&amp;gt;',
    request: AdRequest(),
    adLoadCallback: InterstitialAdLoadCallback(
      onAdLoaded: (ad) {
        _interstitialAd = ad;
      },
      onAdFailedToLoad: (err) {
        print('Failed to load an interstitial ad: ${err.message}');
      },
    ));
}

void _showInterstitialAd() {
  if (_interstitialAd != null) {
    _interstitialAd!.show();
  }
}

@override
void dispose() {
  _interstitialAd?.dispose();
  super.dispose();
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing Your Ads&lt;/strong&gt;&lt;br&gt;
Before launching your app, ensure to test your ads using AdMob’s designated test ad units to confirm their proper display and ensure they do not detract from the user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release Your App&lt;/strong&gt;&lt;br&gt;
Once you have completed thorough testing and integrated ads seamlessly, you can proceed to publish your app on the Google Play Store and/or Apple App Store. Be sure to adhere to the specific guidelines for publishing on Google Play and the App Store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Monetizing your Flutter app through Google AdMob offers a lucrative opportunity to generate income from your efforts. By adhering to the outlined steps, you can effortlessly incorporate ads into your app, ensuring a consistent revenue stream while delivering value to your users. It's essential to follow best practices and Google's guidelines to maintain a positive user experience and optimize your earnings.&lt;/p&gt;

</description>
      <category>admob</category>
      <category>revenue</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Creating a Context-Free Navigation Function in Flutter</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Fri, 28 Jun 2024 15:41:27 +0000</pubDate>
      <link>https://forem.com/tentanganak/creating-a-context-free-navigation-function-in-flutter-53ok</link>
      <guid>https://forem.com/tentanganak/creating-a-context-free-navigation-function-in-flutter-53ok</guid>
      <description>&lt;p&gt;Navigating between screens is a fundamental aspect of mobile app development in Flutter. Typically, navigation relies on the BuildContext to move from one screen to another, but this can become cumbersome in complex applications. &lt;/p&gt;

&lt;p&gt;When navigation needs to be triggered from non-widget classes, such as services or view models, accessing BuildContext can lead to tightly coupled and less maintainable code. As your app grows, managing navigation through context can clutter the codebase and make it harder to maintain, especially for background tasks or services that require navigation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02e2i9bbpwsh7439az9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02e2i9bbpwsh7439az9x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A context-free navigation approach offers a clean and efficient solution by decoupling navigation logic from the BuildContext, allowing greater flexibility and control over your app’s navigation flow. While the &lt;strong&gt;go_router&lt;/strong&gt; package is a powerful tool that provides a declarative approach to routing and deep linking, it still typically requires BuildContext for navigation actions. &lt;/p&gt;

&lt;p&gt;In this article, we'll explore how to implement a basic global navigation system in Flutter by setting up a global navigator key and creating a dedicated navigation service. This method not only serves as a context-free navigation solution but also functions as a global context, simplifying the navigation process and enabling you to initiate navigation commands from anywhere in your app. By the end of this guide, you'll have a streamlined navigation architecture that enhances your development workflow and improves your app’s user experience. Let's dive in and transform the way you handle navigation in your Flutter projects.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;The Problem with Context-Dependent Navigation:&lt;/strong&gt; 🚨&lt;br&gt;
Discuss common issues developers face with context-dependent navigation, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Difficulty navigating from non-widget classes or services.&lt;/li&gt;
&lt;li&gt;Complicated navigation flows where passing context around becomes cumbersome.&lt;/li&gt;
&lt;li&gt;Situations where context is not immediately available, leading to convoluted code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❇️ &lt;strong&gt;The Solution: Context-Free Navigation:&lt;/strong&gt;&lt;br&gt;
Introduce the concept of context-free navigation and its advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplifies navigation logic.&lt;/li&gt;
&lt;li&gt;Enhances code readability and maintainability.&lt;/li&gt;
&lt;li&gt;Enables navigation from any part of the app, including services, controllers, or background tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of you might be using various packages for handling navigation in Flutter apps, such as go_router and get. These packages are powerful tools that help manage navigation efficiently. However, in this article, we will explore how to optimize Flutter's built-in navigation capabilities without relying on third-party plugins. This approach not only streamlines your app but also deepens your understanding of how Flutter's navigation system works.&lt;/p&gt;

&lt;p&gt;Let's take a look the flutter basic navigation,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr662roir3qyi4swyyzuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr662roir3qyi4swyyzuj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydey83tkvb0ba4prwbmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydey83tkvb0ba4prwbmc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The examples above are powerful and sufficient for basic navigation. However, they are only applicable when navigating from a class or widget that has a BuildContext. There will be cases where you need to navigate from a controller that does not have a BuildContext. While it's possible to pass the BuildContext through functions, this approach can be cumbersome and require additional effort.&lt;/p&gt;




&lt;p&gt;🚀 So, let's start creating our own context-free navigation function.&lt;/p&gt;

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

class NavigationService {
  static final GlobalKey&amp;lt;NavigatorState&amp;gt; navigatorKey = GlobalKey&amp;lt;NavigatorState&amp;gt;();
}


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

&lt;/div&gt;

&lt;p&gt;We'll create a class named NavigationService,&lt;/p&gt;

&lt;p&gt;This GlobalKey is used to get a reference to the NavigatorState of the application. By using a global key, you can access the navigator from anywhere in your app.&lt;/p&gt;

&lt;p&gt;Now let's add the functions into the &lt;strong&gt;NavigationService&lt;/strong&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;push&lt;/strong&gt;&lt;/p&gt;

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

Future&amp;lt;T?&amp;gt; push&amp;lt;T extends Object?&amp;gt;(Route&amp;lt;T&amp;gt; route) async {
  return navigatorKey.currentState?.push&amp;lt;T&amp;gt;(route);
}


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

&lt;/div&gt;

&lt;p&gt;This method pushes a new route onto the navigator using a Route object. and route is the new route to push.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pushNamed&lt;/strong&gt;&lt;/p&gt;

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

Future&amp;lt;T?&amp;gt; pushNamed&amp;lt;T extends Object?&amp;gt;(
  String routeName, {
  Object? arguments,
}) async {
  return navigatorKey.currentState?.pushNamed&amp;lt;T&amp;gt;(
    routeName,
    arguments: arguments,
  );
}


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

&lt;/div&gt;

&lt;p&gt;This method pushes a new route onto the navigator using a named route. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;routeName is the name of the route. &lt;/li&gt;
&lt;li&gt;arguments is an optional parameter that allows passing data to the new route.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;pushReplacementNamed&lt;/strong&gt;&lt;/p&gt;

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

Future&amp;lt;T?&amp;gt; pushReplacementNamed&amp;lt;T extends Object?, TO extends Object?&amp;gt;(
  String routeName, {
  Object? arguments,
  TO? result,
}) async {
  return navigatorKey.currentState?.pushReplacementNamed&amp;lt;T, TO&amp;gt;(
    routeName,
    arguments: arguments,
    result: result,
  );
}


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

&lt;/div&gt;

&lt;p&gt;This method replaces the current route with a new route using a named route.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;routeName is the name of the new route.&lt;/li&gt;
&lt;li&gt;arguments is an optional parameter to pass data to the new route.&lt;/li&gt;
&lt;li&gt;result is an optional parameter to return a result to the previous route.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;pushNamedAndRemoveUntil&lt;/strong&gt;&lt;/p&gt;

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

Future&amp;lt;T?&amp;gt; pushNamedAndRemoveUntil&amp;lt;T extends Object?&amp;gt;(
  String routeName, {
  Object? arguments,
  bool Function(Route&amp;lt;dynamic&amp;gt;)? predicate,
}) async {
  return navigatorKey.currentState?.pushNamedAndRemoveUntil&amp;lt;T&amp;gt;(
    routeName,
    predicate ?? (_) =&amp;gt; false,
    arguments: arguments,
  );
}


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

&lt;/div&gt;

&lt;p&gt;This method pushes a new route and removes all previous routes until the predicate returns true.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;routeName is the name of the new route.&lt;/li&gt;
&lt;li&gt;arguments is an optional parameter to pass data to the new route.&lt;/li&gt;
&lt;li&gt;predicate is a function that determines which routes to remove. If not provided, all previous routes are removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;popUntil&lt;/strong&gt;&lt;/p&gt;

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

void popUntil(String route) {
  navigatorKey.currentState?.popUntil(ModalRoute.withName(route));
}


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

&lt;/div&gt;

&lt;p&gt;This method pops routes off the navigator until the specified route is reached.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;route is the name of the route to pop until.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;goBack&lt;/strong&gt;&lt;/p&gt;

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

void goBack&amp;lt;T extends Object?&amp;gt;({T? result}) {
  navigatorKey.currentState?.pop&amp;lt;T&amp;gt;(result);
}


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

&lt;/div&gt;

&lt;p&gt;This method pops the current route off the navigator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;result is an optional parameter to pass a result to the previous route.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The functions above are navigation functions commonly used in Flutter apps.&lt;br&gt;
Now let's add one more step, ☝🏻&lt;/p&gt;

&lt;p&gt;Go to your MaterialApp and attach the NavigationService navigatorKey.&lt;/p&gt;

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

void main() {
  runApp(
    MaterialApp(
      navigatorKey: NavigationService.navigatorKey,
      home: HomePage(),
    ),
  );
}


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

&lt;/div&gt;

&lt;p&gt;Now you can then use the NavigationService to perform navigation from anywhere in your app, without needing a BuildContext:&lt;/p&gt;

&lt;p&gt;Here's the usage&lt;/p&gt;

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

NavigationService().pushNamed('/secondPage');


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

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

NavigationService().push(
 MaterialPageRoute(builder: (context) =&amp;gt; AnotherScreen()),
);


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

&lt;/div&gt;




&lt;p&gt;🕝 &lt;strong&gt;Before Context-Free Navigation&lt;/strong&gt;&lt;/p&gt;

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

Navigator.push(
  context, 
  MaterialPageRoute(
    builder: (builder) =&amp;gt; const LoginPage()
  )
);

Navigator.pushNamed(
 context, '/foo', arguments: someObject
)


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

&lt;/div&gt;

&lt;p&gt;✅ &lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

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

NavigationService().push(
 MaterialPageRoute(builder: (context) =&amp;gt; AnotherScreen()),
);

NavigationService().pushNamed('/secondPage', arguments: someObject);


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

&lt;/div&gt;

&lt;p&gt;Now you're no longer need to pass BuildContext when navigating. &lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The NavigationService class provides a set of utility methods to handle navigation in a Flutter application without requiring direct access to a BuildContext. This is achieved using a global NavigatorState key. Below are the key functionalities provided by the class:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Global Navigation Key:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;navigatorKey: A global key to access the navigator state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Navigation Methods:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pushNamed: Navigates to a named route with optional arguments.&lt;/li&gt;
&lt;li&gt;push: Pushes a route onto the navigator stack.&lt;/li&gt;
&lt;li&gt;pushReplacementNamed: Replaces the current route with a named route.&lt;/li&gt;
&lt;li&gt;pushNamedAndRemoveUntil: Pushes a named route and removes routes until the predicate returns true.&lt;/li&gt;
&lt;li&gt;pushAndRemoveUntil: Pushes a route and removes routes until the predicate returns true.&lt;/li&gt;
&lt;li&gt;goBack: Pops the top-most route off the navigator.&lt;/li&gt;
&lt;li&gt;popUntil: Pops routes until the specified route is reached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context-Free Navigation: Allows navigation operations without needing the BuildContext, useful for scenarios where the context isn't available.&lt;/li&gt;
&lt;li&gt;Utility Methods: Provides comprehensive navigation methods, covering various use-cases such as pushing, replacing, and popping routes.&lt;/li&gt;
&lt;li&gt;Global Accessibility: The global navigator key makes the navigator state accessible throughout the app, facilitating easy and centralized navigation handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using NavigationService, developers can simplify and streamline navigation in their Flutter applications, especially in situations where direct access to the context is challenging. And by creating our own navigation, we can reduce the dependency on public plugins or packages, which may not be maintained by the publisher.&lt;/p&gt;




&lt;p&gt;✅ That's all!&lt;/p&gt;

&lt;p&gt;I hope you find this guide useful and that it enhances your application! Thank you for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Code:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/irfanadisukma/context-free-navigation" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>navigation</category>
      <category>dart</category>
    </item>
    <item>
      <title>Cost-Effective Image Management: Maximizing Efficiency Through Network Image Caching in Mobile Apps</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Tue, 11 Jun 2024 03:02:51 +0000</pubDate>
      <link>https://forem.com/tentanganak/cost-effective-image-management-maximizing-efficiency-through-network-image-caching-in-mobile-apps-1hhn</link>
      <guid>https://forem.com/tentanganak/cost-effective-image-management-maximizing-efficiency-through-network-image-caching-in-mobile-apps-1hhn</guid>
      <description>&lt;p&gt;In today's mobile app landscape, user experience is paramount, with performance and cost efficiency playing pivotal roles in app success. One often-overlooked aspect that significantly impacts both these factors is the management of images. Incorporating image caching strategies can not only enhance app performance by reducing load times but also contribute to significant cost savings in data usage and server resources. &lt;/p&gt;

&lt;p&gt;In this article, we delve into the benefits of caching network images in mobile app development, exploring how this optimization technique can positively influence both user satisfaction and the bottom line.&lt;/p&gt;




&lt;p&gt;Mobile apps can utilize various caching techniques to enhance performance and reduce load times. Here are some common caching methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web Caching&lt;/strong&gt;: This technique involves storing static content, like images and JavaScript files, in the browser’s cache. By doing so, it significantly reduces the time required for an app to load, as these resources do not need to be repeatedly fetched from the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Caching&lt;/strong&gt;: This approach stores frequently accessed data, such as user profiles or product catalogs, locally on the device. It minimizes the need for constant database queries, thereby speeding up data retrieval and enhancing the app’s responsiveness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Caching&lt;/strong&gt;: This method caches data generated by the app itself, such as the results of computations or rendered views. It is particularly beneficial for apps that perform intensive calculations or render complex interfaces, as it reduces processing time and improves overall performance.&lt;/p&gt;

&lt;p&gt;Several advanced caching strategies can further enhance the performance of mobile apps. These advanced methods include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache-Aside&lt;/strong&gt;: This strategy involves storing data in both the cache and the database. When the app needs to access a specific piece of data, it first checks the cache. If the data is not found there, the app queries the database and then stores the retrieved data in the cache for future access. This approach reduces the number of database queries, thereby improving performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache Invalidation&lt;/strong&gt;: To ensure that the cache always contains the most up-to-date data, this strategy invalidates the cache whenever data in the database is updated. When the next request for this data is made, it is fetched directly from the database and the cache is updated accordingly. This method helps prevent stale data from being served to users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distributed Caching&lt;/strong&gt;: This strategy involves storing cached data in multiple locations, such as across different servers or in the cloud. By distributing the cache, the load on any single server is reduced, which enhances performance and reliability, particularly in high-traffic scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The optimal caching strategy for a specific mobile app depends on the app’s unique requirements. However, by implementing advanced caching techniques, mobile app developers can greatly enhance their app’s performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is it important to cache images?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2f1q02ct4b6o7elhkngd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2f1q02ct4b6o7elhkngd.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot above shows detailed network image requests from CloudFront. It is evident that these static image objects have tens of thousands of requests, with a total bandwidth usage exceeding 1GB.&lt;/p&gt;

&lt;p&gt;This scenario represents cost inefficiency, as these images could be stored directly within the application files. However, storing images within the application increases its download and installed size, which can be a concern for users who are sensitive to app size.&lt;/p&gt;

&lt;p&gt;Storing images on the server and making the app request them introduces new challenges, including increased loading times and high bandwidth usage, as illustrated in the image above.&lt;/p&gt;

&lt;p&gt;By implementing a caching strategy, we can reduce network requests, which in turn helps lower costs such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost Inefficiency of Network Requests&lt;/strong&gt;&lt;br&gt;
The image illustrates a high number of requests and significant bandwidth usage for static images. When these images are repeatedly requested from the server, it not only increases the load on the server but also results in higher operational costs due to the increased data transfer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact of Embedding Images in the App&lt;/strong&gt;&lt;br&gt;
One potential solution to avoid network requests is embedding these images directly in the app. However, this leads to a substantial increase in the app's size, affecting both the download size and the installed footprint. This can be a significant drawback for users with limited storage or data plans, potentially leading to reduced app adoption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges with Server-Stored Images&lt;/strong&gt;&lt;br&gt;
When images are stored on a server and the app fetches them dynamically, it introduces several issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Loading Time: Every time an image is requested, the app must wait for the server response, which can lead to delays in image rendering, affecting user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High Bandwidth Usage: As shown in the CloudFront data, frequent requests for images consume a large amount of bandwidth, increasing operational costs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Benefits of Implementing Caching Strategy&lt;/strong&gt;&lt;br&gt;
Implementing a caching strategy can mitigate these issues effectively:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Reduced Network Requests: By caching images locally on the user's device after the first download, subsequent requests for the same images can be served from the local cache, significantly reducing the number of server requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cost Reduction: With fewer network requests, there is less data transfer, leading to lower bandwidth costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved User Experience: Cached images load faster since they are served from local storage rather than fetched from a remote server, resulting in a smoother and more responsive app experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;We only need these two plugins;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/cached_network_image" rel="noopener noreferrer"&gt;Cached Network Image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/flutter_cache_manager" rel="noopener noreferrer"&gt;Flutter Cache Manager&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of us are already familiar with those packages, as it is a popular choice for displaying images in Flutter applications that implement caching strategies.&lt;/p&gt;

&lt;p&gt;Cached Network Image is a flutter library to show images from the internet and keep them in the cache directory.&lt;/p&gt;

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

CachedNetworkImage(
  imageUrl: src,
  fit: fit,
  height: height,
  width: width
)


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

&lt;/div&gt;

&lt;p&gt;This is a common use case for CachedNetworkImage, where cached network images are stored and retrieved using the flutter_cache_manager.&lt;/p&gt;

&lt;p&gt;Now, we can customize this widget by adding a CacheManager to manage its configuration. &lt;/p&gt;

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

CachedNetworkImage(
  imageUrl: src,
  fit: fit,
  height: height,
  width: width,
  cacheManager: CacheManager( // Cache Manager Config
    Config(
      src,
      stalePeriod: const Duration(hours: 1),
    ),
  ),
)


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

&lt;/div&gt;

&lt;p&gt;The configuration object specifies the source URL (src) and the stale period (stalePeriod) for the cached image. This allows for more precise control over how images are cached, ensuring that the app retrieves the most up-to-date images when needed while still benefiting from the performance enhancements of caching.&lt;/p&gt;

&lt;p&gt;In this case, the stale period is set to 1 hour, meaning that the image will be considered stale and fetched from the network after 1 hour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe11xohtq6gnqfwxhfvf0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe11xohtq6gnqfwxhfvf0.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's an explanation of how CacheNetworkManager and CacheManager work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;CacheNetworkImage is a widget used to display an image from a URL source in the Flutter app UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When CacheNetworkImage is requested to display an image from a specific URL, it asks for the image from CacheManager.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CacheManager is a package used to manage image caching in the Flutter app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CacheManager checks if the requested image is available in the local cache or not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the image is available in the cache, CacheManager returns the image from the cache to CacheNetworkImage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the image is not available in the cache, CacheManager downloads the image from the requested URL and stores it in the local cache.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After downloading and storing the image in the cache, CacheManager returns the image to CacheNetworkImage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CacheNetworkImage then displays the received image from CacheManager in the Flutter app UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By using CacheNetworkImage and CacheManager, the Flutter app can display images from URLs efficiently by utilizing the local cache. This helps reduce bandwidth and image loading time, especially if the same image is requested repeatedly. Also offers several benefits for application performance, network efficiency, and cost-effective bandwidth usage:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Improvement&lt;/strong&gt;&lt;br&gt;
By caching images locally on the device, CachedNetworkImage reduces the time it takes to load images after the first download. This leads to faster image rendering and a smoother user experience, especially when the same images are used repeatedly within the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network Efficiency&lt;/strong&gt;&lt;br&gt;
With a CacheManager configured, the app can manage how often it fetches images from the network. The stalePeriod parameter ensures that images are only re-downloaded after a specified period, minimizing unnecessary network requests. This reduces the load on the server and helps prevent network congestion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost-Effective Bandwidth Usage&lt;/strong&gt;&lt;br&gt;
Reducing the number of image downloads translates directly into lower data usage. For users on metered connections, this means less data consumption and potential cost savings. For the app provider, it means reduced bandwidth costs, as fewer network requests are made to fetch the same resources.&lt;/p&gt;

&lt;p&gt;Implementing a caching strategy for static images is a cost-effective solution that balances the need for a smaller app size with the demand for quick and efficient image loading. This approach not only reduces operational costs but also enhances the overall user experience by decreasing load times and conserving bandwidth.&lt;/p&gt;

</description>
      <category>caching</category>
      <category>mobile</category>
    </item>
    <item>
      <title>The Importance of Analytics in Mobile Application 📊</title>
      <dc:creator>Irfan Adisukma</dc:creator>
      <pubDate>Wed, 08 May 2024 03:28:36 +0000</pubDate>
      <link>https://forem.com/tentanganak/the-importance-of-analytics-in-mobile-application-135o</link>
      <guid>https://forem.com/tentanganak/the-importance-of-analytics-in-mobile-application-135o</guid>
      <description>&lt;p&gt;In the modern age of technology, mobile applications play a central role in our daily lives, offering a wide range of functionalities from entertainment to productivity. The development and optimization of mobile apps involve intricate processes, and analytics plays a vital role in ensuring the success and longevity of such projects. By collecting and analyzing data, mobile app development companies gain valuable insights into user behavior, preferences, and trends, allowing them to make informed decisions and improve user experiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is it important?&lt;/strong&gt;🤔&lt;br&gt;
Analytics is a critical component of mobile app development, enabling developers and organizations to gather valuable insights into the usage and performance of their apps. Here's a simplified breakdown of the subject:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definition and Scope of Analytics in Mobile App -Development&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analytics in mobile app development involves the collection, measurement, and analysis of data related to app usage and user behavior.&lt;/li&gt;
&lt;li&gt;It helps developers understand how users interact with the app, identify strengths and weaknesses, and make informed decisions for enhancements.&lt;/li&gt;
&lt;li&gt;The scope of mobile app analytics covers tracking user actions, app performance, and user experience to optimize functionality and success.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve the scope of mobile app analytics in tracking user actions, app performance, and user experience to optimize functionality and success, the types of data collected include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Types of Data Collected in Mobile Apps&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User Engagement Data: Includes metrics like downloads, active users, session duration, and retention rates.&lt;/li&gt;
&lt;li&gt;User Behavior Data: Tracks actions such as clicks, taps, swipes, and interactions with specific features.&lt;/li&gt;
&lt;li&gt;Device and Technical Data: Provides insights into devices, operating systems, and app versions to address compatibility issues.&lt;/li&gt;
&lt;li&gt;Crash and Error Reports: Assist in identifying and resolving bugs and technical issues affecting app stability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The various types of data collected in mobile apps, including user engagement data, user behavior data, device and technical data, and crash and error reports, play a crucial role in the importance of analytics for improving user experience and app performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Importance of Analytics in Improving User Experience and App Performance&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data-driven Decision-Making: Analytics empowers developers to make decisions based on data rather than assumptions.&lt;/li&gt;
&lt;li&gt;Enhancing User Experience: Understanding user behavior helps optimize layouts, features, and content for a smoother user experience.&lt;/li&gt;
&lt;li&gt;Bug Fixing and Stability: Analytics aid in identifying and fixing crashes and errors, resulting in a more stable app.&lt;/li&gt;
&lt;li&gt;Personalization: Data insights enable personalized app experiences tailored to individual user preferences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To effectively leverage analytics for improving user experience and app performance, mobile app developers rely on key metrics and analytics tools that provide insights into critical areas such as user conversion, churn, retention, and engagement&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Metrics and Analytics Tools Used in Mobile App Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Key Metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conversion Rate: Percentage of users completing a specific desired action.&lt;/li&gt;
&lt;li&gt;Churn Rate: Percentage of users discontinuing app usage over time.&lt;/li&gt;
&lt;li&gt;Retention Rate: Percentage of users continuing app usage after the initial interaction.&lt;/li&gt;
&lt;li&gt;Average Session Length: Average time spent by users in the app during a session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Analytics Tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/analytics"&gt;Firebase Analytics&lt;/a&gt;: Provides real-time app usage data and integrates with other Firebase features.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://clevertap.com/"&gt;Clevertap&lt;/a&gt;: CleverTap is a comprehensive mobile marketing platform that provides advanced analytics and engagement tools to help businesses better understand and engage with their mobile app users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Tentang Anak, we mainly use these analytics tools to obtain more detailed data. Here is some example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Analytics&lt;/strong&gt;
Here's some example of the overview data from Firebase Analytics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapvq9s7nnt9dye1t86uh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapvq9s7nnt9dye1t86uh.png" alt="Image description" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This screenshot provides an overview of user activity and analytics data from the Firebase platform for a mobile app. Here's an explanation of the different sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User activity over time:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This graph shows the user activity trend over time for different time periods (30 days, 7 days, and 1 day).&lt;br&gt;
The blue line represents the user activity for the last 30 days, and the purple lines represent the 7-day and 1-day user activity trends.&lt;/p&gt;

&lt;p&gt;The graph helps visualize fluctuations in user activity and identify any significant spikes or dips during the given time frame.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Users in Last 30 Minutes:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This section provides real-time data on active users within the last 30 minutes. The bar chart displays the number of users per minute, allowing you to monitor current user activity levels.&lt;/p&gt;

&lt;p&gt;Below the chart, there is a list of top countries where users are located, along with the corresponding user counts (which have been blurred for confidentiality).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Users by App Version&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This graph shows the distribution of users across different app versions. The blue line represents the user count for a specific app version (e.g., 2.19.2 on April 21st).&lt;/p&gt;

&lt;p&gt;The graph helps track user migration to newer app versions and identify potential issues or adoption rates for each version.&lt;br&gt;
The "View app versions" link likely provides more detailed information about app versions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that some values, such as user counts and specific country names, have been blurred due to confidentiality and privacy concerns. However, the overall structure and information presented in the overview remain visible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcpwh2320x775tfaix7p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcpwh2320x775tfaix7p.png" alt="Image description" width="800" height="527"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnbwz26robweagzudcdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnbwz26robweagzudcdx.png" alt="Image description" width="760" height="890"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqnlzb1q3qw15yjcny6j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqnlzb1q3qw15yjcny6j.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This Firebase overview provides valuable insights into user activity patterns, real-time user engagement, geographical distribution, and app version adoption. It can help developers and product teams monitor app performance, identify areas for improvement, and make data-driven decisions regarding feature releases, user acquisition, and overall app strategy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clevertap&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's some example of the overview data from &lt;strong&gt;Clevertap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e7j4o5bmn4496bmgue9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e7j4o5bmn4496bmgue9.png" alt="Image description" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have an event named &lt;strong&gt;'Event_Name'&lt;/strong&gt;, which is triggered when a user taps on one our interaction.&lt;/p&gt;

&lt;p&gt;Let's take an example from March 24th to April 23rd, this event was recorded 1,700 times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsz3l6u92rdczwa0xlepr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsz3l6u92rdczwa0xlepr.png" alt="Image description" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;From these &lt;strong&gt;1,700 events&lt;/strong&gt;, we can break down the data further. Out of the 1,700 events, there were &lt;strong&gt;1,060 unique users&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1,053&lt;/strong&gt; of these users were using &lt;strong&gt;mobile devices&lt;/strong&gt;, while &lt;strong&gt;7&lt;/strong&gt; were using &lt;strong&gt;tablets&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Among the 1,060 users, the highest percentage &lt;strong&gt;(7%)&lt;/strong&gt; performed the event between &lt;strong&gt;8:00 PM and 9:00 PM&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Additionally, &lt;strong&gt;31%&lt;/strong&gt; of the users triggered the event more than once.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This overview data provides insights into the usage of the our feature, including the number of events, unique users, device types, peak usage hours, and the frequency of usage by individual users.&lt;/p&gt;

&lt;p&gt;In addition to data, we can also obtain user activity behavior if we use analytics. Here's an example of an overview of user activity data on &lt;strong&gt;Clevertap&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjqt219m4d3y1l1obmzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjqt219m4d3y1l1obmzg.png" alt="Image description" width="800" height="164"&gt;&lt;/a&gt;&lt;br&gt;
The data shows details about user activity captured by the analytics platform. It provides two key metrics related to user behavior:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avg. visit duration is an hour:&lt;/strong&gt;&lt;br&gt;
This metric indicates that, on average, users spend approximately one hour per visit or session within the app. The "visit duration" or "session length" refers to the total time a user spends actively engaged with the app during a single visit or session before exiting or switching to another app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avg. time between visits is 4 hours:&lt;/strong&gt;&lt;br&gt;
This metric represents the average time gap between consecutive visits or sessions by the same user. It suggests that, on average, users return to the app and start a new session approximately every 4 hours.&lt;/p&gt;

&lt;p&gt;These two metrics provide valuable insights into user engagement patterns and can help understand how frequently users are interacting with the app and for how long they typically stay engaged during each visit or session.&lt;br&gt;
Such information can be useful for various purposes, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User retention analysis&lt;/strong&gt;: Understanding visit frequency and duration can help assess user retention and identify potential churn risks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User experience optimization&lt;/strong&gt;: If the average visit duration is shorter than expected, it may indicate opportunities to improve the user experience or introduce features that encourage longer engagement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content and feature planning&lt;/strong&gt;: The average time between visits can inform decisions about the ideal frequency for releasing new content or features to align with user behavior patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource allocation&lt;/strong&gt;: These metrics can help optimize resource allocation, such as server capacity or push notification scheduling, based on anticipated user activity patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, these user activity details provide quantitative data to support data-driven decision-making and continuous improvement efforts within the app development and product management processes.&lt;/p&gt;




&lt;p&gt;Now let's deep dive into the user interaction overview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4jx7cn8342dmk3lxjno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4jx7cn8342dmk3lxjno.png" alt="Image description" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The data represents an overview of user activity events tracked by Clevertap analytics platform. Here's an explanation of each event and a conclusion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Notification Clicked (82 times):&lt;/strong&gt;
This event is triggered when a user clicks on a push notification sent by the app. It occurred 82 times within the specified date range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push Impressions (80 times):&lt;/strong&gt;
This event records the number of times a push notification was displayed or viewed by users, which happened 80 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity Set (1 time):&lt;/strong&gt;
This event is likely related to user identification or authentication within the app, which occurred once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Version Changed (25 times):&lt;/strong&gt;
This event tracks when a user updates to a new version of the app, which happened 25 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reachable By (101 times):&lt;/strong&gt;
This event could be related to tracking user devices or channels through which users can be reached, such as push notifications or in-app messages, which occurred 101 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Concluded (191 times):&lt;/strong&gt;
This event is triggered when a user ends or exits an active session within the app, which happened 191 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enter HomePage Commerce (26 times):&lt;/strong&gt;
This event is likely related to users accessing the homepage or a specific section of the app related to commerce or e-commerce features, which occurred 26 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product Viewed (1 time):&lt;/strong&gt;
This event tracks when a user views a product page or details within the app, which happened once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, the data shows the timestamp, event properties (like app version, device type), and other contextual information for some of the events.&lt;/p&gt;

&lt;p&gt;The user activity overview provided by Clevertap analytics offers valuable insights into various user interactions and behaviors within the app. It tracks crucial events such as push notification engagement, user sessions, navigation patterns, product interactions, and version updates. This data can help developers and product teams identify areas for improvement, optimize user experiences, and make informed decisions based on real user data.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;"Without data, you're just another person with an opinion." - W. Edwards Deming&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;CONCLUSION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With all of these, we can draw conclusions about the importance of mobile app analytics for applications. Here are some of the benefits we can derive:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Personalization Enhancement&lt;/strong&gt; 👍🏻&lt;br&gt;
Mobile app analytics facilitate tailoring mobile applications to individual user preferences and behaviors, thereby enhancing user experiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Insightful Data Provision&lt;/strong&gt; 💡&lt;br&gt;
By leveraging mobile app analytics, businesses gain access to insightful, data-driven information, empowering them to make informed decisions and optimize app functionalities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Accurate Data Aggregation&lt;/strong&gt; 🎯&lt;br&gt;
Mobile app analytics ensure the collection of accurate and relevant data, enabling businesses to understand user interactions, preferences, and trends with precision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Enhanced ROI and Performance&lt;/strong&gt; 💰🔄&lt;br&gt;
Through mobile app analytics, businesses can track key performance metrics, optimize marketing strategies, and ultimately drive higher returns on investment (ROI) by improving app performance and user engagement.&lt;/p&gt;

&lt;p&gt;__&lt;/p&gt;

&lt;p&gt;In conclusion, the significance of mobile app analytics cannot be overstated. It serves as the cornerstone for optimizing user experiences, enhancing app performance, and driving business success in the competitive digital landscape. By leveraging data-driven insights, businesses can tailor their apps to meet user preferences, increase engagement, and ultimately achieve higher returns on investment. &lt;/p&gt;

&lt;p&gt;Leave a comment, and share if you found this article helpful, as spreading awareness about the power of mobile app analytics can benefit both developers and users alike. 💻&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.userexperior.com/blog/key-benefits-mobile-app-analytics-tools"&gt;userexperior.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://splendapp.com/en/mobile-app-analytics/#:~:text=Mobile%20app%20analytics%20provide%20a,strategies%2C%20and%20overall%20user%20experience"&gt;splendapp.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>analytics</category>
      <category>metrics</category>
      <category>clevertap</category>
      <category>googleanalytics</category>
    </item>
  </channel>
</rss>
