Google search engine
Home Blog Page 12

Explore Speed Dial In Flutter

0

In Flutter, Speed Dial is a well-known floating action button (FAB) variation generally used to give users admittance to different actions or choices from a single button. At the point when the FAB is tapped, it extends to rapidly uncover a list of extra more modest buttons or actions, empowering users to perform related errands.

This article will Explore Speed Dial In Flutter. We will implement a demo program and I would like to show how to use speed dial in Flutter using the flutter_speed_dial package in your Flutter applications.

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.

For Flutter Speed Dial:

flutter_speed_dial | Flutter package
Flutter plugin to implement a beautiful and dynamic Material Design Speed Dial with labels, animated icons…pub.dev


Table Of Contents::

Introduction

Implementation

Code Implement

Code File

Conclusion



Introduction:

A Speed Dial in Flutter is a proficient and easy-to-use method for dealing with various actions in a single Floating Action Button. It works on UI/UX plan while further developing availability to key elements.

This demo video shows how to use speed dial in Flutter and how a speed dial will work using the flutter_speed_dial package in your Flutter applications. We will show a user a fab button on a screen when the user presses the button it expands to reveal a list of additional smaller buttons or actions by using the flutter_speed_dial package. It will be shown on your device.

Demo Module::


Implementation:

Step 1: Add the dependencies

Add dependencies to pubspec — yaml file.

dependencies:
  flutter:
    sdk: flutter
  flutter_lints: ^3.0.0

Step 2: Import

import 'package:flutter_speed_dial/flutter_speed_dial.dart';

Step 3: Run flutter packages get in the root directory of your app.

Code Implement:

You need to implement it in your code respectively:

Create a new dart file called main.dart inside the lib folder.

In the main.dart file, we will create a new class was SpeedDialDemo(). In this new class, we will add the body. In the body we will add the center widget. Inside the widget, we will add an Image with height and width and text is ‘PLease tap the FAB button’.

Center(
child: Column(
children: [
Image.asset(
"assets/logo.png",
height: MediaQuery.of(context).size.height * 0.45,
width: MediaQuery.of(context).size.width * 0.7,
),
const Text(
'PLease tap the FAB button',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
],
)),

Then, now we will add the floatingActionButton. In this button we will add SpeedDial() function. Inside this function, we will add animatedIcon, backgroundColor, foregroundColor, overlayColor, overlayOpacity, etc.

floatingActionButton: SpeedDial(
  animatedIcon: AnimatedIcons.menu_close,
  backgroundColor: Colors.teal.shade100,
  foregroundColor: Colors.black,
  overlayColor: Colors.white,
  overlayOpacity: 0.5,
  spacing: 12.0,
  spaceBetweenChildren: 9.0,
  direction: SpeedDialDirection.up,
  children: [
    SpeedDialChild(
      child: const Icon(Icons.message),
      label: 'Chat',
      onTap: () => print('Chat'),
    ),
    SpeedDialChild(
      child: const Icon(Icons.call),
      label: 'Call',
      onTap: () => print('Call'),
    ),
    SpeedDialChild(
      child: const Icon(Icons.camera_alt),
      label: 'Camera',
      onTap: () => print('Camera'),
    ),
  ],
),

Its children, we will add three SpeedDialChild() function. In this function, we will three icons on the child, label and onTap(). When user tap the the button is expands and show icons.

When we run the application, we ought to get the screen’s output like the underneath screen capture.

Code File:

import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:flutter_speed_dial_demo/splash_screen.dart';

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

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
home: const Splash(),
);
}
}

class SpeedDialDemo extends StatefulWidget {
const SpeedDialDemo({super.key});

@override
State<SpeedDialDemo> createState() => _SpeedDialDemoState();
}

class _SpeedDialDemoState extends State<SpeedDialDemo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
title: const Text('Flutter Speed Dial Demo'),
backgroundColor: Colors.teal.shade100,
),
body: Center(
child: Column(
children: [
Image.asset(
"assets/logo.png",
height: MediaQuery.of(context).size.height * 0.45,
width: MediaQuery.of(context).size.width * 0.7,
),
const Text(
'PLease tap the FAB button',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
],
)),
floatingActionButton: SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
backgroundColor: Colors.teal.shade100,
foregroundColor: Colors.black,
overlayColor: Colors.white,
overlayOpacity: 0.5,
spacing: 12.0,
spaceBetweenChildren: 9.0,
direction: SpeedDialDirection.up,
children: [
SpeedDialChild(
child: const Icon(Icons.message),
label: 'Chat',
onTap: () => print('Chat'),
),
SpeedDialChild(
child: const Icon(Icons.call),
label: 'Call',
onTap: () => print('Call'),
),
SpeedDialChild(
child: const Icon(Icons.camera_alt),
label: 'Camera',
onTap: () => print('Camera'),
),
],
),
);
}
}

Conclusion:

In the article, I have explained the spead dial in a flutter; you can modify this code according to your choice. This was a small introduction to speed dial On User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying the Speed Dial in your flutter projectsWe will show you what the Introduction is. Make a demo program for working on speed dial Using the flutter_speed_dial package in your flutter applications. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on FacebookGitHubTwitter, and LinkedIn for any flutter-related queries.

We welcome feedback and hope you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Related: Explore Exception Handling In Flutter

Related: Explore Dart String Interpolation

Related: Explore Design Patterns in Flutter


Flutter’s Architecture: Understanding the Internals

0

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table of Contents

Overview of Flutter’s Architecture

Flutter Engine: The Heart of Rendering

The Flutter Rendering Pipeline: How the UI is Drawn

Layers

Skia: The Graphics Engine Behind Flutter

Dart and Skia: How They Communicate

Deep Dive into Flutter’s Rendering Pipeline

Layout Phase: Determining Size and Position

Paint Phase: Rendering Visuals to the Screen

Compositing: Combining Layers for Efficient Rendering

Conclusion

References


Introduction

Flutter has quickly become one of the most popular frameworks for building cross-platform mobile apps. With its rich widget ecosystem, beautiful UIs, and seamless performance across iOS and Android, Flutter is an attractive choice for developers. However, behind the scenes, Flutter’s architecture is based on a highly optimized and custom rendering pipeline that ensures it runs efficiently and provides developers with great flexibility.

In this post, we will examine Flutter’s internal architecture, focusing on its enginerendering pipeline, and widget drawing on the screen. We will also discuss how Dart interacts with Skia, Flutter’s graphics library, and the custom rendering system that powers its high performance.


Overview of Flutter’s Architecture

Flutter’s architecture comprises several layers that work together to provide an efficient, high-performance development experience. Let’s start with a quick overview of the key components:

  • Dart App: It composes widgets into the desired user interface (UI) and implements the business logic necessary for the application. This involves arranging the widgets in a way that meets the design requirements and ensuring the app’s functionality is driven by the appropriate business rules and processes.
  • Flutter Framework (Dart-based): This is the top layer of Flutter where developers work directly. It includes all the widgets, libraries, and tools to create the app’s UI and business logic. The framework itself is written in Dart, which provides an expressive, easy-to-learn programming environment.
  • Flutter Engine: This is the core engine written in C++ that powers Flutter’s rendering capabilities. It handles low-level operations such as rendering, input handling, text layout, and more. The engine communicates with the underlying platform (iOS/Android) through platform channels to access native features.
  • Platform Layer: This layer provides access to the device’s platform-specific features such as sensors, cameras, geolocation, etc. Flutter communicates with this layer using platform channels, which allow the Dart code to interact with native code (Java/Kotlin for Android and Swift/Objective-C for iOS).
  • Embedder: The system coordinates with the underlying operating system to gain access to essential services, such as rendering surfaces, accessibility, and input management. It also takes responsibility for managing the event loop, ensuring that events are processed efficiently. Additionally, the system exposes a platform-specific API that allows the Embedder to be integrated seamlessly into applications, enabling smooth interaction with the operating system’s services and resources.
  • Runner: It composes the components exposed by the platform-specific API of the Embedder into an app package that is runnable on the target platform. This process is an integral part of the app template generated by the flutter create command, ensuring that the necessary resources and configurations are in place for the app to function properly on the specified platform.

Flutter Engine: The Heart of Rendering

The Flutter Engine is the core of Flutter’s performance and rendering. It is written in C++ and is responsible for managing the entire lifecycle of a Flutter app. The engine provides several essential components, including:

  • Dart VM: Flutter runs Dart code using the Dart Virtual Machine (VM). This VM is highly optimized for Flutter and enables efficient execution of business logic. During development, Flutter uses Just-In-Time (JIT) compilation to speed up the development cycle. In production, Ahead-Of-Time (AOT) compilation is used for optimized performance.
  • Skia Graphics Library: Skia is a 2D graphics library used by Flutter to draw everything from simple shapes, text, and images to complex animations. Skia provides hardware-accelerated rendering, making it ideal for Flutter’s performance-oriented needs.
  • Text Layout: Flutter uses Skia’s text layout engine to render text across various fonts, languages, and styles. It includes features such as font rasterization and text shaping.
  • Platform Channels: Flutter’s engine also manages communication with native platform code through platform channels, allowing Flutter apps to access native features like camera, geolocation, and more.

The Flutter Rendering Pipeline: How the UI is Drawn

One of the most interesting aspects of Flutter’s architecture is its custom rendering pipeline. Unlike many mobile UI frameworks, Flutter does not rely on the native platform’s rendering system. Instead, it builds its UI using its own set of widgets, which it then renders via Skia. Let’s walk through the rendering pipeline step by step:

1. Widget Creation

In Flutter, everything is a widget. Widgets are the building blocks of the UI, and they describe what should appear on the screen. However, widgets themselves do not draw anything. They are immutable and declarative, meaning that when a widget is updated, it results in a new widget tree being created.

Text("Hello, Flutter!", style: TextStyle(fontSize: 24, color: Colors.blue));

This widget does not actually paint anything on the screen. It simply describes that there should be text with the specified style. Flutter uses a widget tree to manage the app’s UI, where each widget represents a part of the UI (like text, images, buttons, etc.).

2. Element Tree

Widgets are lightweight and immutable. When a widget changes, it doesn’t modify the existing widget directly; instead, it creates a new widget. The Flutter framework converts this widget tree into an Element tree.

An Element is a runtime representation of a widget and holds references to the widget’s state. This allows Flutter to keep track of the UI and the state of each widget efficiently.

3. RenderObject Tree

Once the element tree is established, Flutter transforms it into a RenderObject tree. RenderObjects are the low-level objects responsible for laying out and painting widgets on the screen. They handle the layout of widgets in terms of size and position.

The RenderObject tree can be thought of as a more detailed version of the widget tree, where each object has the responsibility for rendering itself. For instance, a Container widget might be represented as a RenderBox object.

Container(
padding: EdgeInsets.all(8.0),
child: Text("Hello, Flutter!"),
)

In this example, the Container widget would have a corresponding RenderBox that handles the layout and painting of the text and the padding inside.

4. Layout Phase

The layout phase is responsible for determining the size and position of every widget. It starts at the root of the RenderObject tree and moves downward, recursively calculating the layout of each widget.

In the layout phase, each RenderObject must decide its size (width and height) and its position relative to its parent and children. Flutter’s layout system is highly flexible, allowing widgets to behave in a variety of ways (e.g., wrapping, expanding, or aligning).

Here’s an example of a Column widget containing two children

Column(
children: [
Text("First Item"),
Text("Second Item"),
],
)

During the layout phase, the Column will calculate the size and position of the two Text widgets, aligning them vertically.

5. Paint Phase

Once the layout phase is completed, Flutter enters the paint phase. This is when the actual drawing happens. In this phase, each RenderObject will paint itself onto the screen using the Canvas API.

For example, the Text widget will use Skia’s text rendering capabilities to draw the text, while a Container might use a rectangle to render its background.

renderBox.paint(canvas, offset);

In this code, renderBox is the RenderObject associated with the widget, and paint() is responsible for drawing the widget onto the screen.

6. Compositing

Once the widgets are painted, Flutter may need to combine the rendered layers into a single image that can be displayed on the screen. This process is called compositing.

Flutter uses a layered compositing system to combine multiple layers of UI elements. This allows Flutter to optimize the rendering process, ensuring that only the parts of the screen that have changed need to be redrawn.

For example, if a Container widget changes, only that widget’s layer will be re-rendered, not the entire screen.


Layers

Layers are an important concept of the Flutter framework, which are grouped into multiple categories in terms of complexity and arranged in the top-down approach. The topmost layer is the UI of the application, which is specific to the Android and iOS platforms. The second topmost layer contains all the Flutter native widgets. The next layer is the rendering layer, which renders everything in the Flutter app. Then, the layers go down to Gestures, foundation library, engine, and finally, core platform-specific code. The following diagram specifies the layers in Flutter app development.


Skia: The Graphics Engine Behind Flutter

One of the central pillars of Flutter’s performance and rendering capabilities is Skia, an open-source 2D graphics library that powers much of Flutter’s graphical rendering. Skia provides an efficient and flexible framework for drawing complex visual elements such as text, images, shapes, and animations on the screen. It is a low-level graphics engine used by both Android and Chrome, but Flutter leverages it as its primary means of rendering UI content. Skia’s high-performance rendering system is one of the main reasons Flutter apps can run so smoothly across platforms.

Key Features of Skia in Flutter

Here are the most crucial features of Skia, which are leveraged by Flutter to render high-performance graphics:

  • Cross-Platform Rendering: Skia is platform-agnostic, meaning it can be used across various platforms, including Android, iOS, macOS, Linux, and Windows. Since Flutter relies on Skia for rendering, it can ensure consistent visual fidelity across all platforms.
  • Low-Level Graphics API: Skia provides a low-level API to interact with 2D drawing primitives like paths, text, and bitmaps. Flutter interacts with Skia using this API to render widgets and layouts to the screen efficiently.
  • GPU Acceleration: One of the most important aspects of Skia’s performance is its ability to leverage GPU acceleration for rendering tasks. Skia uses OpenGL, Vulkan, Metal, and other platform-specific graphics APIs to offload rendering to the GPU, which makes rendering smoother and more efficient, particularly when dealing with animations and complex visual effects.
  • Vector Graphics: Skia supports vector graphics, which means that the visual elements drawn on the screen can be scaled up or down without losing quality. This is particularly important for Flutter apps, as it ensures that the UI looks crisp on devices with varying screen sizes and resolutions.
  • High-Performance Text Rendering: Skia comes with a robust text rendering engine capable of high-performance text layout, including the ability to handle complex scripts, kerning, text alignment, and font rasterization. Flutter uses Skia’s text rendering capabilities for all its text-based widgets, ensuring that text is drawn accurately and efficiently.
  • Image Rendering: Skia is responsible for rendering images in Flutter as well. Whether it’s raster images (like PNGs, JPEGs) or vector images (like SVGs), Skia is used to efficiently display these images with high performance. It also supports hardware-accelerated image manipulation, which makes operations like image scaling and transformations fast and efficient.

Skia handles the low-level drawing operations, while Flutter’s engine controls the rendering pipeline, managing the communication between the Dart code and Skia.


Dart and Skia: How They Communicate

Flutter’s use of Dart for the business logic of the application and Skia for rendering might seem like a disconnect, but thanks to the Flutter engine, these two layers work together seamlessly. The engine acts as a bridge between the high-level Dart code and the low-level Skia rendering commands.

When you write your Flutter app using Dart, you’re primarily working with Widgets that describe the structure and behavior of your UI. However, once the widget tree is constructed, the Flutter engine translates these high-level descriptions into low-level rendering commands that Skia can process.

The Dart VM and Skia work together to make sure Flutter’s rendering pipeline runs smoothly. The Dart code, running in the Dart VM, is responsible for creating the widget tree and managing the app’s state. When a widget needs to be drawn, the Dart code interacts with the Flutter engine, which then communicates with Skia to render the widget to the screen.

Dart communicates with Skia through the Flutter engine, which acts as a bridge. The engine converts the high-level widget description in Dart into low-level drawing commands that Skia can understand. Skia, in turn, takes care of the actual drawing operations, using GPU resources when available to speed up rendering.

This separation allows Flutter to remain efficient while providing developers with a high-level declarative API to build UIs. By abstracting away the details of low-level rendering, Flutter enables developers to focus on building beautiful and performant apps without needing to worry about graphics rendering details.

How Dart and Skia Communicate:

  • Dart VM: When you write Dart code, it’s compiled by the Dart VM. During development, Just-In-Time (JIT) compilation is used, which makes hot-reloading possible. In production, Ahead-Of-Time (AOT) compilation ensures faster startup times and better performance.
  • Flutter Engine: The engine contains a C++ layer that acts as an intermediary between Dart and Skia. It translates the layout and painting instructions from Dart into commands that Skia can process.
  • Skia: Skia is where the actual drawing happens. It interacts with the hardware through GPU APIs (like OpenGL, Vulkan, and Metal) to render the graphics and text to the screen. The Canvas API in Flutter acts as a wrapper around Skia’s lower-level functions, allowing you to draw shapes, images, and text.

For instance, when you call a setState() method in your Flutter app, which causes the widget tree to rebuild, Flutter’s engine will rebuild the RenderObject tree, and any changes to the layout or appearance of the UI will be communicated to Skia to be redrawn.

For example, consider the following Flutter widget that draws a circle:

CustomPaint(
size: Size(200, 200),
painter: CirclePainter(),
)

The CirclePainter class would extend the CustomPainter class and override the paint method to draw the circle using Skia’s Canvas:

class CirclePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}

Example: Flutter and Skia for Custom Drawing

Consider a scenario where you want to draw a custom shape (say, a custom circle) using Flutter’s CustomPainter class. The CustomPainter allows you to use Skia’s rendering API to draw shapes directly onto the screen. Here’s an example of how you might do this:

class CirclePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;

// Drawing a circle using Skia's Canvas API
canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false; // We don't need to repaint unless the custom painter logic changes
}
}

class MyCustomPaintWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CirclePainter(),
size: Size(200, 200), // Size of the area to paint
);
}
}

In this example:

  • CustomPainter extends CustomPainter and overrides the paint() method, which is called to render the widget.
  • Canvas is used to draw the circle. Internally, the drawCircle method uses Skia’s API to paint the circle onto the screen.
  • The Paint object specifies the color and the style of the drawing.

Skia is at the heart of this process, enabling Flutter to draw the custom shapes using optimized, low-level rendering routines.


Deep Dive into Flutter’s Rendering Pipeline

Now, let’s go even deeper into understanding how Flutter’s rendering pipeline works and how it helps Flutter achieve its high performance. The rendering pipeline is the series of steps that take place after the widget tree is built and before the app’s UI is displayed on the screen. This process allows Flutter to efficiently transform widget descriptions into pixel-perfect visuals, and it involves key components like Widget Trees, Element Trees, RenderObject Trees, Layout, Painting, and Compositing.

Each of these steps plays a crucial role in ensuring that Flutter apps perform well, even when complex UIs and animations are involved.

Widget Tree to RenderObject Tree: The Transformative Process

The journey from a simple Text widget to a rendered element on the screen starts with the widget tree. Each widget defines part of the UI (e.g., a button, text field, etc.). When a widget is placed inside another widget, Flutter’s framework begins by converting the widget tree into an Element tree and then into a RenderObject tree.

Widget Tree

The widget tree is a hierarchy of Flutter widgets that are declarative descriptions of the UI. Widgets are lightweight and immutable, meaning that when a widget changes, it creates a new widget. For example, if the text in a Text widget changes, the widget tree is recreated with the new text value.

Element Tree

The Element tree is a more efficient representation of the widget tree that exists at runtime. Each Element corresponds to a widget in the widget tree and stores references to its state. Unlike widgets, which are immutable, elements can have state, making them ideal for managing the UI’s changes during execution.

RenderObject Tree

The RenderObject tree is the most crucial part of the rendering pipeline. The RenderObject represents the visual elements that Flutter needs to draw on the screen. Every RenderObject has properties such as size, position, paint, and layout constraints that help it define how the widget should appear and behave. The RenderObject tree is responsible for rendering the app’s UI and handles tasks such as layout and painting.


Layout Phase: Determining Size and Position

The layout phase of the Flutter rendering pipeline determines the size and position of each widget on the screen. During this phase, each RenderObject must figure out its size based on its parent’s constraints and the available space in the layout.

The layout phase is hierarchical. It starts from the root of the tree and recursively processes each child. Here’s how the layout process works:

  1. Parent Constraints: The parent widget gives constraints to its child. For example, if you have a Row widget with two Text widgets inside, the Row widget determines how much space the child widgets can occupy.
  2. Size Determination: Each child widget (RenderObject) will calculate its size based on these constraints. For example, if the parent widget is a Column, it might impose constraints that tell its children to expand vertically, or if the parent is a Row, the children might be constrained to expand horizontally.
  3. Positioning: After each child widget determines its size, Flutter positions the widgets in the layout tree by calculating their offsets (x and y positions). This ensures that each widget is placed correctly on the screen relative to its parent.

In a typical Column widget, for example, each child is positioned vertically, with padding and spacing taken into account during the layout phase.

Column(
children: [
Padding(
padding: EdgeInsets.all(10),
child: Text("Hello, Flutter!"),
),
RaisedButton(
onPressed: () {},
child: Text("Press Me"),
),
],
)

Here, during the layout phase, the Column widget will position each child vertically, taking into account the padding for the first Text widget and the space needed for the RaisedButton.


Paint Phase: Rendering Visuals to the Screen

The paint phase is when the actual drawing happens. After the layout phase has calculated where each widget should be positioned, it’s time to paint the widgets. This phase utilizes the Canvas API, which is where Skia comes into play.

The Canvas is a central component of Flutter’s rendering system, and it provides methods to draw shapes, text, images, and more. Each RenderObject can paint itself on the canvas by calling specific methods.

For example, a simple Container widget might use a RenderBox to draw a rectangle, while a Text widget might use a text drawing method.

Example: Custom Painter for Drawing Shapes

Let’s look at a basic example where we use a CustomPainter to draw a shape (circle) on the screen.

class CirclePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}

In this example, the CirclePainter class extends CustomPainter. The paint method is where the actual drawing happens. Here, we are using Skia’s canvas.drawCircle method to draw a blue circle at the center of the given size. This is an example of how Flutter uses the Canvas API to interact with Skia to render graphics on the screen.


Compositing: Combining Layers for Efficient Rendering

Once the widgets are painted, Flutter enters the compositing phase, where it combines different layers of the UI into a single image to be displayed on the screen. Flutter’s compositing system ensures that only the parts of the screen that need to be updated are redrawn.

In this phase, Flutter uses a layered compositing model, where each widget’s painted output is stored in a layer. These layers are then composited into a single image that will be displayed on the screen. The advantage of this approach is that Flutter only re-renders the portions of the screen that have changed, rather than redrawing the entire screen.

For instance, if a button changes color on tap, only the button’s layer needs to be updated, not the entire screen. This results in significant performance gains, especially in complex applications with many UI elements.

Here’s an example of how layers can be used for custom painting with CustomPaint:

CustomPaint(
painter: CirclePainter(),
)

When Flutter draws the CirclePainter, the painted content is stored in a layer. If this layer needs to be updated (for instance, if the circle’s position or color changes), only this layer is recomposited. This makes Flutter apps extremely efficient when updating the UI.


Conclusion

Understanding the internals of Flutter’s architecture, its custom rendering pipeline, and the relationship between Dart and Skia is crucial for developers aiming to fully leverage Flutter’s capabilities. Flutter’s approach to UI rendering, with a custom pipeline based on Skia, enables it to deliver high performance and consistent results across platforms. This architecture not only makes Flutter fast and flexible but also empowers developers to create sophisticated UIs with full control over rendering.

By learning about the engine, the rendering process, and how Flutter draws widgets on screen, you can optimize your Flutter apps further, troubleshoot performance issues, and even build custom rendering solutions when needed.


At What the Flutter, we love working with Flutter apps to make them high-performing that delight users. Contact us today to discuss how we can optimize your Flutter app performance.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


References:

https://www.javatpoint.com/flutter-architecture#:~:text=In%20Flutter%2C%20the%20application%20is,complex%20user%20interface%20very%20easily.

Flutter architectural overview
A high-level overview of the architecture of Flutter, including the core principles and concepts that form its design.docs.flutter.dev

Flutter’s Architecture Explained
We explore the intricacies of Flutter’s architecture, divided into three primary layers: the Framework, the Engine, and…somniosoftware.com


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
 And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project hourly or full-time as per your requirement! For any flutter-related queries, you can connect with us on Facebook, GitHub, Twitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

2026 Ultimate Guide: Hide/Reveal Passwords with Riverpod in Fl…

0

This article will explore the Show/Hide Password Using Riverpod In Flutter. We will implement a demo program and I would like to show how to show/hide password in TextFormField by using the Riverpod state management in your flutter applications.

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.

For Hooks Riverpod:

hooks_riverpod | Flutter Package
Run this command: With Flutter: $ flutter pub add hooks_riverpod This will add a line like this to your package’s…pub.dev

Table Of Contents::

Introduction

Implementation

Code Implement

Code File

Conclusion



Introduction:

This demo video shows how to show/hide passwords in textformfield in flutter and how a show/hide password will work using the hooks_riverpod package in your flutter applications. We will show a user show/hide password on a textformfield by using riverpod and also use will once we click outside of the TextFormField keyboard will close. It will be shown on your device.

Demo Module ::


Implementation:

Step 1: Add the dependencies

Add dependencies to pubspec — yaml file.

dependencies:
flutter:
sdk: flutter
hooks_riverpod: ^latest version

Step 2: Add the assets

Add assets to pubspec — yaml file.

assets:
- assets/

Step 3: Import

import 'package:hooks_riverpod/hooks_riverpod.dart';

Step 4: Run flutter packages get in the root directory of your app.

How to implement code in dart file :

You need to implement it in your code respectively:

Create a new dart file called main.dart inside the lib folder.

We should adjust our main. dart file. We want to add ProviderScope to the root of our application that stores the state of the Providers.

void main() {
runApp(
const ProviderScope(child: MyApp()),
);
}

Then, we need to change StatelessWidget to ConsumerWidget. Once we do that, we must add WidgetRef inside of the build.

class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
final bool showPassState = ref.watch(showPassProvider);
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.cyan,
),
debugShowCheckedModeBanner: false,
home: const Splash(),
);
}
}

Now time to create our StateProvider.

This is the provider we will use for show and hide.

final showPassProvider = StateProvider<bool>((ref) => true);

Now we will create a new class HomeScreen with extended ConsumerWidget. We will return the GestureDetector widget. In this widget, we will add a Column widget. And ad two textformfield.

class HomeScreen extends ConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
final bool showPassState = ref.watch(showPassProvider);
return GestureDetector(
onTap: FocusManager.instance.primaryFocus?.unfocus,
child: Scaffold(
appBar: AppBar(
title: const Text(
"Flutter Show/Hide Password Riverpod Demo",
style: TextStyle(fontSize: 18,color: Colors.white),
),
backgroundColor: Colors.cyanAccent.withOpacity(.4),
automaticallyImplyLeading: false,
centerTitle: true,
),
body: Column(
children: [
Image.asset("assets/logo.png",height: 240,width: 280,),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
decoration: const InputDecoration(
prefixIcon: Icon(Icons.person),
labelText: "Username",
hintText: "Username",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
obscureText: showPassState,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: showPassState
? const Icon(Icons.visibility_off)
: const Icon(Icons.visibility),
onPressed: () => ref
.read(showPassProvider.notifier)
.update((state) => !state),
),
prefixIcon: const Icon(Icons.lock),
labelText: "Password",
hintText: "Password",
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
),
),
),
],
),
),
);
}
}

Since obscureText is true in the beginning. We will set showPassState in obscureText.

TextFormField(
// hide the password. when it is true.
obscureText: showPassState,
...

Then, at that point, we ought to update our state for symbol click. To do that We involved IconButton in our suffixIcon. That’s what to do, we ought to read and afterward call the update function on the notifier, and we need to change our Icon concerning our state.

IconButton(
icon: showPassState
? const Icon(Icons.visibility_off)
: const Icon(Icons.visibility),
onPressed: () => ref
.read(showPassProvider.notifier)
.update((state) => !state),
),

When we run the application, we ought to get the screen’s output like the underneath screen capture.

Final Output

Code File:

import 'package:flutter/material.dart';
import 'package:flutter_show_hide_password_riverpod_demo/splash_screen.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

final showPassProvider = StateProvider<bool>((ref) => true);

void main() {
runApp(
const ProviderScope(child: MyApp()),
);
}

class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
final bool showPassState = ref.watch(showPassProvider);
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.cyan,
),
debugShowCheckedModeBanner: false,
home: const Splash(),
);
}
}

class HomeScreen extends ConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
final bool showPassState = ref.watch(showPassProvider);
return GestureDetector(
onTap: FocusManager.instance.primaryFocus?.unfocus,
child: Scaffold(
appBar: AppBar(
title: const Text(
"Flutter Show/Hide Password Riverpod Demo",
style: TextStyle(fontSize: 18,color: Colors.white),
),
backgroundColor: Colors.cyanAccent.withOpacity(.4),
automaticallyImplyLeading: false,
centerTitle: true,
),
body: Column(
children: [
Image.asset("assets/logo.png",height: 240,width: 280,),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
decoration: const InputDecoration(
prefixIcon: Icon(Icons.person),
labelText: "Username",
hintText: "Username",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
obscureText: showPassState,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: showPassState
? const Icon(Icons.visibility_off)
: const Icon(Icons.visibility),
onPressed: () => ref
.read(showPassProvider.notifier)
.update((state) => !state),
),
prefixIcon: const Icon(Icons.lock),
labelText: "Password",
hintText: "Password",
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
),
),
),
],
),
),
);
}
}

Conclusion:

In the article, I have explained the Show/Hide Password Using Riverpod in a flutter; you can modify this code according to your choice. This was a small introduction to Show/Hide Password Using Riverpod On User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying the Show/Hide Password Using Riverpod in your flutter projectsWe will show you what the Introduction is. Make a demo program for working on show/hide password in TextFormField Using the hooks_riverpod package in your flutter applications. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on FacebookGitHubTwitter, and LinkedIn for any flutter-related queries.

We welcome feedback and hope you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Related: Basics of Riverpod | Flutter

Related: Dialog Using GetX in Flutter


Explore Memory Leaks In Flutter

0

In this article, we will Explore Memory Leaks In Flutter. We perceive how to execute a demo program. We will show you what is the reason, detect, and prevent memory leaks in your Flutter applications.

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table Of Contents::

What Is a Memory Leaks?

What Reasons For Memory Leaks In Flutter?

How To Detect Memory Leak In Flutter?

How To Prevent Memory Leaks In Flutter?

Conclusion



What Is a Memory Leaks?

Memory leaks in Flutter applications can happen when a program holds memory that is not generally required, making the application consume more memory than needed. One common reason is the presence of unused objects that stay in memory due to caching, improper disposal, or failure to remove listeners. This can prompt slower execution, crashes, and eventually, an unsuitable user experience.

Abusing streams, which are utilized to deal with asynchronous events in Flutter, can likewise prompt memory leaks on the off chance that stream subscriptions are not as expected dropped, making the stream keep running behind the background and consuming memory. Besides, stacking enormous pictures and videos without delivering them properly can add to memory leaks in Flutter applications.

What Reasons For Memory Leaks In Flutter?

Memory Leaks might happen for some reasons. Here are the absolute most normal reasons.

  • > Unused Objects — For this reason, unused objects that are unclosed streams are the circumstances when we make the stream yet neglect to close it. Streams can keep references to objects in memory even after they are not generally utilized causing memory leaks.
import 'dart:async';
void main() {
StreamController<String> _streamSub = StreamController<String>();
  _streamSub.add("Flutter, Devs!");
  _streamSub.stream.listen((data) {
print("stream data: $data");
});
}

Other, unused objects that are not taken out from memory can cause memory leaks. This is because the items consume space in memory, even though they are not generally needed. Unused objects can be made for different reasons, for example, caching, not appropriately disposing of objects, and not eliminating listeners when they are not generally required.

  • > Improper Use of Streams — For this reason, streams are utilized in Flutter to deal with asynchronous events, and improper utilization of streams can cause memory leaks. For instance, not appropriately canceling a stream subscription can make the stream keep running behind the background, consuming memory.
  • > Global Variables — For this reason, the garbage collector doesn’t work while putting away references to objects or widgets as global variables. So we need to deallocate that memory as it can likewise cause memory leaks.
  • > Large Images and Videos — For this reason, loading enormous images and videos can likewise cause memory leaks in Flutter. At the point when huge files are stacked, they take up a lot of memory, and on the off chance that they are not as expected delivered when they are not generally required, they can cause memory leaks.
  • >Widget Trees — For this reason, mistakenly putting the widgets on the widget tree, particularly while utilizing the stateful widget can likewise cause memory leaks in Flutter.

How To Detect Memory Leak In Flutter?

Recognizing memory leaks in your Flutter application can be a bit challenging yet it is conceivable. The following are a few strategies and tools that can distinguish and fix memory leaks in the Flutter Application.

  • > Flutter DevTools — Flutter DevTools is a performance and debugging tool for Dart programs and Flutter Applications. You can utilize Flutter DevTools to look at your application’s memory usage. You can get to the Flutter DevTools by running the following command:
flutter pub global run devtools
  • > Heap Snapshots — You can likewise take heap snapshots to look at the memory utilization and breaks at a particular point in time. Heap Snapshots can assist you with recognizing objects that are not being garbage collected as expected.
  • > Analyze Your Code — You should review your code and give close consideration to objects. Now check assuming that the objects are appropriately disposed of when they are not generally required. You should likewise check for the controller as they are one of the most well-known reasons for memory leaks in Flutter.

How To Prevent Memory Leaks In Flutter?

  • > Dispose of Objects — One of the best ways of forestalling memory spills in Ripple is to discard objects when they are not generally required. Discarding objects eliminates them from memory and opens up assets for different pieces of the application. To discard objects, utilize the arrange technique in the Stateful Gadget.
  • > Use Streams Properly — To prevent memory leaks caused by streams, consistently cancel the subscription when it is not generally required. This guarantees that the stream is done running behind the background, consuming memory.
  • > Use Images and Videos Efficiently — To prevent memory leaks influenced by enormous images and videos, utilize efficient techniques to load them. One such strategy is to utilize the flutter_cache_manager package, which assists with caching images and videos, reducing the amount of memory they consume.
  • Use Profiling Tools — Profiling tools, like the Flutter DevTools, can assist with recognizing memory leaks in your application. By analyzing the memory utilization of your application, you can pinpoint the areas that are causing memory leaks and do whatever it takes to fix them.
  • > Avoid Saving Flutter Object References in Background Threads — To prevent memory leaks, refrain from putting Flutter object references in background threads. All things being equal, utilize weak references or isolate communication mechanisms like message passing. Weak references enable objects to be garbage collected when they are no longer strongly referenced, preventing memory leaks. Isolate communication ensures that Flutter objects can be safely passed between isolates without retaining strong references, further guarding against memory leaks.

Conclusion:

In the article, I have explained the Memory Leaks In Flutter; you can modify this code according to your choice. This was a small introduction to the Memory Leaks In Flutter User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying the Memory Leaks In Flutter in your Flutter projectsMemory leaks are a typical issue in Flutter, however, they can be prevented by following best practices and utilizing the appropriate tools. By disposing of objects, utilizing streams appropriately, and efficiently loading images and videos, developers can prevent memory leaks and make high-performing applications that provide an ideal user experience.

Remember to use profiling tools to recognize memory leaks in your application, and do whatever it may take to fix them. With these tips, you can build Flutter applications that are quick, efficient, and reliable.

❤ ❤ Thanks for reading this article ❤❤

If I need to correct something? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on FacebookGitHubTwitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Related: Explore Exception Handling In Flutter


Best Practices for Riverpod Theming in Flutter 2026″ (59 chara…

0

Themes are a subject group frequently discussed while making applications. The most usually utilized term in regards to this point would be ‘dark theme’. You can frequently see individuals requesting how to deal with a dark theme in your application or any event, similar to requesting how to oversee various symbols for various themes.

Flutter has, however, greatly eased the process of customizing themes and their components. In this article, we will explore how to implement theming using Riverpod state management in your Flutter applications.

In this tutorial, I only show you how to change the theme according to system mode using the Switch button.

For Hooks Riverpod:

hooks_riverpod | Flutter Package
Run this command: With Flutter: $ flutter pub add hooks_riverpod This will add a line like this to your package’s…pub.dev

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table Of Contents::

Introduction

Implementation

Code Implement

Conclusion



Introduction:

This demo video shows how to implement theming theme in Flutter and shows how a theming will work using the hooks_riverpod package in your Flutter applications. We will show a user switches the button then the theme will be changed from light mode to dark mode/vice versa. It will be shown on your device.

Demo Module::


Implementation:

Step 1: Add the dependencies

Add dependencies to pubspec — yaml file.

dependencies:
flutter:
sdk: flutter
hooks_riverpod: ^latest version

Step 2: Add the assets

Add assets to pubspec — yaml file.

assets:
- assets/

Step 3: Import

import 'package:hooks_riverpod/hooks_riverpod.dart';

Step 4: Run flutter packages get in the root directory of your app.

How to implement code in dart file :

You need to implement it in your code respectively:

Create a new dart file called themes.dart inside the lib folder.

First, let’s create our darkTheme and Light Theme. To do that, we can create a new class called themes. dart.

import 'package:flutter/material.dart';

class Themes {
static final lightTheme = ThemeData(
brightness: Brightness.light, // For light theming
scaffoldBackgroundColor: Colors.grey.shade100,
appBarTheme: AppBarTheme(
backgroundColor: Colors.grey.shade100,
titleTextStyle: const TextStyle(color: Colors.black, fontSize: 12),
centerTitle: false),
// you can add more
);

static final darkTheme = ThemeData(
brightness: Brightness.dark, // For dark theming
scaffoldBackgroundColor: Colors.grey.shade900,
appBarTheme: const AppBarTheme(
backgroundColor: Color(0x00049fb6),
titleTextStyle: TextStyle(color: Colors.white, fontSize: 18),
centerTitle: false),
// you can add more
);

}

Create a new dart file called themes_provider.dart inside the lib folder.

Let’s create our provider file to do that we can create themes_provider.dart. In this example, we will use StateNotifier.

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

final themesProvider = StateNotifierProvider<ThemesProvider, ThemeMode?>((_) {
return ThemesProvider();
});

class ThemesProvider extends StateNotifier<ThemeMode?> {
ThemesProvider() : super(ThemeMode.system);
void changeTheme(bool isOn) {
state = isOn ? ThemeMode.dark : ThemeMode.light;
}
}

Create a new dart file called main.dart inside the lib folder.

Now, time to create our main. dart file. We should extend ConsumerWidget instead of StatelessWidget to use WidgetRef. Also, ensure you add the root of our app ProviderScope as shown below. Otherwise, you are not going to be able to use providers.

Presently, time to make our main. dart file. We ought to extend ConsumerWidget rather than StatelessWidget to utilize WidgetRef. Likewise, ensure you add the root of our application ProviderScope as displayed underneath. If not, you won’t have the option to utilize providers.

import 'package:flutter/material.dart';
import 'package:flutter_theming_riverpord_demo/splash_screen.dart';
import 'package:flutter_theming_riverpord_demo/themes.dart';
import 'package:flutter_theming_riverpord_demo/themes_provider.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

void main() {
runApp(
const ProviderScope(child: MyApp()),
);
}


class MyApp extends ConsumerWidget {
const MyApp({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final themeModeState = ref.watch(themesProvider);
return MaterialApp(
theme: Themes.lightTheme,
darkTheme: Themes.darkTheme,
themeMode:themeModeState,
debugShowCheckedModeBanner: false,
home: const Splash(),
);
}
}

Create a new dart file called home_screen.dart inside the lib folder.

Lastly, we need to create our HomeScreen(). To do that we create a home_screen.dart file. And as I said before you can create everything into maindart. We use Switch Widget to change

import 'package:flutter/material.dart';
import 'package:flutter_theming_riverpord_demo/themes_provider.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class HomeScreen extends ConsumerWidget {
const HomeScreen({
Key? key,
}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
final themeModeState = ref.watch(themesProvider);
return Scaffold(
appBar: AppBar(
title: const Text(
"Flutter Theming Riverpod Demo",
style: TextStyle(fontSize: 18),
),
centerTitle: true,
automaticallyImplyLeading: false,
backgroundColor: Colors.teal,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"assets/logo.png",
height: 200,
width: 300,
),
Text(
"Change Theme $themeModeState",
style: const TextStyle(fontSize: 18),
),
Consumer(
builder: (context, ref, child) {
return Switch(
value: themeModeState == ThemeMode.dark, //false or true
onChanged: (value) {
ref.read(themesProvider.notifier).changeTheme(value);
});
},
),
],
),
),
);
}
}

When we run the application, we ought to get the screen’s output like the underneath screen capture.

Final Output

Conclusion:

In the article, I have explained the Implement Theming Using Riverpod in a flutter; you can modify this code according to your choice. This was a small introduction to Implementing Theming Using Riverpord On User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying the Implement Theming Using Riverpod in your Flutter projectsWe will show you what the Introduction is. Make a demo program for working on implementing Theming Using the hooks_riverpod package in your Flutter applications. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on FacebookGitHubTwitter, and LinkedIn for any flutter-related queries.

We welcome feedback and hope you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Related: Show/Hide Password Using Riverpod In Flutter

Related: Basics of Riverpod | Flutter

Related: Dialog Using GetX in Flutter


Routing With Fluro In Flutter

Routing or navigating starting with one page and then onto the next page is fundamental for any application. All things considered, when it came to Flutter’s in-constructed routing system, it was quite simple to get a handle on, after a long tutorial and numerous uses in projects.

Then, I utilized Fluro at that point, and I was astounded. If you come from a front-end web development background, you would feel comfortable with how Fluro handles its routes.

This blog will explore the Routing With Fluro In Flutter. We perceive how to execute a demo program. We will learn how to route from one page to another page using the fluro package in your Flutter applications.

If you’re looking for the best flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.

fluro | Flutter Package
English | Português The brightest, hippest, coolest router for Flutter. Simple route navigation Function handlers (map…pub.dev

Table Of Contents::

Introduction

Implementation

Code Implement

Conclusion



Introduction:

The below demo video shows a two-page Home page and a Second page. On the Home page, we will add an elevated button and on the Second page, we will add Floating Action Buttons to navigate to the other page. It will be shown on your devices.

When moving from Home to Second, we require some extra information, such as data, so we pass that as part of the route parameters. So we’ll be learning how to do that through Fluro.

While moving from Home to Second, we require some additional information, like data, so we pass that as a component of the route parameters. So we’ll figure out how to do that through Fluro.

Demo Module::


Implementation:

Step 1: Add the dependencies

Add dependencies to pubspec — yaml file.

dependencies:
flutter:
sdk: flutter
fluro: ^2.0.4

Step 2: Import

import 'package:fluro/fluro.dart';

Step 3: Run flutter packages get in the root directory of your app.

How to implement code in dart file :

You need to implement it in your code respectively:

Create a new dart file called router.dart inside the lib folder.

import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import 'package:flutter_fluro_routing_demo/second_page.dart';
import 'package:flutter_fluro_routing_demo/home_page.dart';

class Routes {
static final router = FluroRouter();

static var firstScreen = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return const HomePage();
});

static var placeHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return SecondPage(data: params["data"][0]);
});

static dynamic defineRoutes() {
router.define("home", handler: firstScreen,transitionType: TransitionType.fadeIn);
router.define("second/:data", handler: placeHandler,transitionType: TransitionType.inFromLeft);
}
}

The first line inside the Routes class initializes a static instance of FluroRouter() from the Fluro library.

static final router = FluroRouter();

Then, we have a handler where we deal with highlighting which widget/part must be loaded while visiting a route. You can consider them controllers on the off chance that you are accompanying information from a past framework.

// Handler for Home Page
static var firstScreen = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return const HomePage();
});
// Handler for Second Page
static var placeHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return SecondPage(data: params["data"][0]);
});

params[‘data’][0] will return the data sent by the route path and will be sent to the SecondPage as an attribute.

Next, we have a defineRoutes() function, where we will define individual routes and their paths. This is also where we will define any parameters if any. Let’s take a close look at the router.define() function.

static dynamic defineRoutes() {
router.define("home", handler: firstScreen,transitionType: TransitionType.fadeIn);
router.define("second/:data", handler: placeHandler,transitionType: TransitionType.inFromLeft);
}

Create a new dart file called main.dart inside the lib folder.

Now, just pass this function in the initState and you’re good to go.

import 'package:flutter/material.dart';
import 'package:flutter_fluro_routing_demo/router.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

@override
void initState() {
super.initState();
Routes.defineRoutes();
}

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: 'home',
onGenerateRoute: Routes.router.generator,
);
}
}

Create a new dart file called home_page.dart inside the lib folder.

Navigator.pushReplacementNamed() is a capability that replaces the stack with anything that route you pass to it, so the user can’t return to the past screen by just squeezing the back button.

On the off chance that you would need such usefulness of returning a screen, you can utilize Navigator.pushNamed() with similar attributes.

import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Fluro Routing Demo'),
centerTitle: true,
backgroundColor: Colors.teal,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Home Page',style: TextStyle(fontSize: 20),),
const SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
var data = "FlutterDevs";
Navigator.pushReplacementNamed(context, 'second/$data');
},
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
primary: Colors.teal[300],
minimumSize: const Size(88, 36),
padding: const EdgeInsets.symmetric(horizontal: 16),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2)),
),
),
child: const Text('Press Button'),
),
],
),
),
);
}
}

Create a new dart file called second_page.dart inside the lib folder.

On the Second Page, it’s genuinely straightforward as we need to pass no parameters accordingly, so we simply need to call pushReplacementNamed() once more.

import 'package:flutter/material.dart';
class SecondPage extends StatelessWidget {
const SecondPage({Key? key, required this.data}) : super(key: key);

final String data;


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second page'),
centerTitle: true,
backgroundColor: Colors.teal,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset("assets/logo.png",width: 300,height: 250,),
Text('Welcome, $data',style: const TextStyle(fontSize: 20,fontWeight: FontWeight.w700),),
],
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.teal,
child: const Icon(Icons.settings_backup_restore),
onPressed: () {
Navigator.pushReplacementNamed(context, 'home');
},
),
);
}
}

When we run the application, we ought to get the screen’s output like the underneath screen capture.


Conclusion:

In the article, I have explained Routing With Fluro’s basic structure in a flutter; you can modify this code according to your choice. This was a small introduction to Routing With Fluro On User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying the Routing With Fluro in your Flutter projectsWe will show you what the Introduction is. Make a demo program for working Routing With Fluro and you’ve learned how to route from one page to another page using the fluro package in your Flutter applications. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on FacebookGitHubTwitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Related: Handling Navigation Stacks

Related: Bottom Navigation Bar using Provider | Flutter

Related: Flutter Deep Links – A unique way!


Upload, Download, and Delete Images in AWS Amplify Storage with S3 Bucket in Flutter 2024

0

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table Of Contents::

Introduction

What is AWS Amplify Storage?

S3 Core Concepts

Prerequisites

Configuration

Best Practices

Conclusion

References


Introduction

Flutter has become one of the most popular frameworks for building cross-platform applications, while AWS Amplify provides an easy and powerful way to add cloud capabilities to your applications. Among Amplify’s many features, Storage with S3 bucket is crucial for managing files like images, videos, and documents.

AWS Amplify storage module provides a simple mechanism for managing user content for your app in public, protected or private storage buckets. The storage category comes with built-in support for Amazon S3 (Simple Storage Service). In this blog, we’ll cover everything you need to integrate AWS Amplify Storage with S3 buckets into your Flutter app, step by step.


What is AWS Amplify Storage?

AWS Amplify is a cloud-based development framework by Amazon Web Services that simplifies building scalable, full-stack applications. The Amplify Storage category integrates with Amazon S3, allowing you to store and retrieve objects like images, audio, videos, and documents securely.


S3 Core Concepts

Amazon S3 stores data as objects within container buckets. An object consists of a file and optionally any metadata that describes that file. To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a file, you can set permissions on the object and any metadata.

Buckets are the containers for objects. You can have one or more buckets. For each bucket, you can control access to it (who can create, delete, and list objects in the bucket), view access logs for it and its objects, and choose the geographical region where Amazon S3 will store the bucket and its contents.


Prerequisites

Before diving in, ensure you have the following ready:

  1. AWS Account: Sign up and Setup your AWS account
  2. Node.js: Required to install the Amplify CLI.
  3. Flutter Setup: Ensure Flutter and Dart are installed on your machine.
  4. Amplify CLI: Install globally using npm install -g @aws-amplify/cli.

Configuration

Step 1: Initialize AWS Amplify

Install the Amplify CLI

Run the following command in your terminal:

npm install -g @aws-amplify/cli

This installs the Amplify Command Line Interface (CLI) globally.

Initialize the Amplify Project

Navigate to your Flutter project directory and run:

amplify init
  • Choose Flutter as the framework.
  • Configure your environment and backend details (e.g., name, environment, region, etc.).
  • When prompted, provide the details of your application.

This command sets up Amplify for your project and generates the necessary files.

Step 2: Add the Storage Category

Run the following command to add storage:

amplify add storage
  • Choose “Content (Images, audio, video, etc.)”.
  • Define the resource name (or accept the default).
? Please select from one of the below mentioned services:
> Content (Images, audio, video, etc.)
NoSQL Database
? Please provide a friendly name for your resource that will be used to label this category in the project:
> mystorage
? Please provide bucket name:
> mybucket
  • Set permissions for authenticated and unauthenticated users.
? Restrict access by?
> Auth/Guest Users
Individual Groups
Both
  • Enable Auth/Guest access only if necessary (e.g., public-facing apps).
? Who should have access:
❯ Auth users only
Auth and guest users

Then you’ll be prompted to set the access scopes for your authenticated and (if selected prior) unauthenticated users.

? What kind of access do you want for Authenticated users?
> ◉ create/update
◯ read
◯ delete
? What kind of access do you want for Guest users?
◯ create/update
> ◉ read
◯ delete
  • Push the changes to AWS:
amplify push

This command provisions an S3 bucket and configures it with the necessary policies.

Step 3: Add Dependencies to Your Flutter Project

In your Flutter app’s pubspec.yaml file, add the following dependencies:

dependencies:
amplify_flutter: ^latest
amplify_storage_s3: ^latest

Run flutter pub get to install the packages.

Step 4: Configure Amplify in Your App

To initialize the Amplify Auth and Storage categories, call Amplify.addPlugin() for each plugin or pass all the plugins in Amplify.addPlugins(). To complete initialization, call Amplify.configure().

Your code should look like this:

import 'package:flutter/material.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'amplifyconfiguration.dart';

Future<void> _configureAmplify() async {
try {
final auth = AmplifyAuthCognito();
final storage = AmplifyStorageS3();
await Amplify.addPlugins([auth, storage]);

// call Amplify.configure to use the initialized categories in your app
await Amplify.configure(amplifyconfig);
} on Exception catch (e) {
safePrint('An error occurred configuring Amplify: $e');
}
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await _configureAmplify();
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('AWS Amplify Storage with S3')),
body: Center(child: Text('Ready to upload files!')),
),
);
}
}

Step 5: Using StoragePath

You can use StoragePath to access, upload to, or download from to any path in your S3 bucket. The Amplify Gen 1 CLI automatically creates the following directories:

  • public/: Accessible by all users of your application
  • protected/<identityId>/: Readable by all users (you need to specify the identityID of the user who uploaded the file). Writable only by the creating user
  • private/<identityId>/: Readable and writable only by the creating user

If you are using Amplify Gen2 or an S3 bucket not created by Amplify, you can use StoragePath to access, upload to, or download from any directory in your bucket.

You can specify the path to your S3 resource by creating a StoragePath directly from a String, or by constructing the path with the user’s Cognito IdentityId.

A StoragePath must:

  1. Not start with a ‘/’ (leading slash)
  2. Not be empty

Create a StoragePath from String

When constructing a StoragePath from a String, the provided string will be the path.

// Resolves to "public/exampleFile.txt"
const StoragePath.fromString('public/exampleFile.txt');

Create a StoragePath with user’s IdentityId

You may want to construct a StoragePath that contains the Amplify Auth user’s IdentityId. We’ve created a helper function that injects the user’s IdentityId when a Storage API is called, since fetching an IdentityId from the Auth plugin is not synchronous.

// If the user's identityId was "123", 
// the StoragePath would resolve to "private/123/exampleFile.txt"
StoragePath.fromIdentityId(
(String identityId) => 'private/$identityId/exampleFile.txt',
};

Step 6: Implement Storage Functions

Upload a File to S3

To upload a Imagefile to S3 bucket, specify the file path and s3 bucket path where you want to store.

You can upload imageFile to a local directory using Amplify.Storage.uploadFile

To upload Image:

From Local File Path (All Platforms)

Future<void> uploadImage(File imageFile) async {
try {
final result = await Amplify.Storage.uploadFile(
localFile: AWSFile.fromPath('imageFile.path'),
path: StoragePath.fromString('public/'+'your-s3-bucket-imagefile-path'),
);
print('Upload successful: ${result.key}');
} catch (e) {
print('Upload failed: $e');
}
}

From File (Mobile & Desktop)

import 'dart:io' show File;

Future<void> uploadImage(File imageFile) async {
try {
final result = await Amplify.Storage.uploadFile(
localFile: AWSFilePlatform.fromFile(imageFile),
path: const StoragePath.fromString('public/imagefile.png'),
).result;
safePrint('Uploaded imagefile: ${result.uploadedItem.path}');
} on StorageException catch (e) {
safePrint(e.message);
}
}

You can call this method with a selected file from your device.

Download a File from S3

You can download file to a local directory using Amplify.Storage.downloadFile

To download a file:

Future<void> downloadImage(String key, String downloadPath) async {
try {
final result = await Amplify.Storage.downloadFile(
path: StoragePath.fromString('public/'+'your-s3-bucket-imagefile-path'),
localFile: AWSFile.fromPath(downloadPath),
).result;
AWSFile awsFile = result.localFile;
path = awsFile.path!; //use this path to show your imagefile in your UI

print('Download successful: ${result.file.path}');
} catch (e) {
print('Download failed: $e');
}
}

This method saves the image file locally to the specified path.

Generate a download URL

You can get a downloadable URL for the image file in storage by its path.

When creating a downloadable URL, you can choose to check if the image file exists by setting validateObjectExistence to true in S3GetUrlPluginOptions. If the file is inaccessible or does not exist, a StorageException is thrown. This allows you to check if an object exists during generating the presigned URL, which you can then use to download that object.

Future<void> getDownloadUrl() async {
try {
final result = await Amplify.Storage.getUrl(
path: const StoragePath.fromString('public/example.txt'),
options: const StorageGetUrlOptions(
pluginOptions: S3GetUrlPluginOptions(
validateObjectExistence: true,
expiresIn: Duration(days: 1),
),
),
).result;
safePrint('url: ${result.url}');
} on StorageException catch (e) {
safePrint(e.message);
}
}

List Files in the S3 Bucket

You can list all files uploaded under a given path.

  1. This will list all public image files(i.e. those with guest access level):
Future<void> listAllImageFiles() async {
try {
final result = await Amplify.Storage.list(
path: const StoragePath.fromString('public/'),
options: const StorageListOptions(
pluginOptions: S3ListPluginOptions.listAll(),
),
).result;
safePrint('Listed items: ${result.items}');
} on StorageException catch (e) {
safePrint(e.message);
}
}

2. This will list all image files located under path album that:

  • have private access level
  • are in the root of album/ (the result doesn’t include files under any sub path)

excludeSubPaths can be used to exclude nested paths. / is used by as the delimiter for nested paths. This can be customized with the delimiter option.

Future<void> listAlbum() async {
try {
String? nextToken;
bool hasNextPage;
do {
final result = await Amplify.Storage.list(
path: const StoragePath.fromString('public/album/'),
options: StorageListOptions(
pageSize: 50,
nextToken: nextToken,
pluginOptions: const S3ListPluginOptions(
excludeSubPaths: true,
delimiter: '/',
),
),
).result;
safePrint('Listed items: ${result.items}');
nextToken = result.nextToken;
hasNextPage = result.hasNextPage;
} while (hasNextPage);
} on StorageException catch (e) {
safePrint(e.message);
}
}

Delete a File from S3

To delete a file:

Future<void> deleteImage(String path) async {
try {
await Amplify.Storage.remove(path: StoragePath.fromString(path));
print('Image deleted successfully: $key');
} catch (e) {
print('Delete failed: $e');
}
}

Best Practices

  1. Secure Access: Use Cognito for managing user access.
  2. Optimize File Handling:
  • Use unique file keys to avoid overwrites.
  • Compress images or videos before uploading.

3. Error Handling: Always handle exceptions to improve user experience.

4. Monitor Costs: Keep track of your S3 bucket usage to avoid unexpected costs.


Conclusion

Integrating AWS Amplify Storage with S3 buckets into your Flutter app is straightforward and highly beneficial for managing files. By following this guide, you can upload, download, list, and delete files seamlessly. Amplify also provides robust security and scalability, making it an excellent choice for modern applications.

Explore further to customize storage configurations, add advanced features like presigned URLs, or integrate other Amplify categories to build a comprehensive cloud-enabled app.


At What the Flutter, we love working with Flutter apps to make them high-performing and delight users. Contact us today to discuss how we can optimize your Flutter app performance.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


References:

Storage – Flutter – AWS Amplify Gen 1 Documentation
Learn more about how you can manage user content for your app in public, protected or private storage buckets using…docs.amplify.aws

Storage – AWS Amplify Gen 2 Documentation
Set up and connect to storage. AWS Amplify Documentationdocs.amplify.aws


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on FacebookGitHubTwitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.


Enums In Flutter

0

As Flutter develops, resulting deliveries might be more about cleaning than new stages or significant changes. Alongside this development, however, is the surge of new developers coming to developers from different platforms.

With this, there is an undeniable correlation with make between Dart endlessly includes in the dialects they are now acquainted with. Dart is a lot of developing language, thus it misses the mark on highlights accessible in different dialects.

Enums are an element of Dart that developers have mentioned changes for quite a while — because of reasons seen ahead. Gradually, yet consistently, these have gone along. While still not a completely complete cycle, it has gone to where enums today are to some degree not the same as the enums of old.

In this blog, we will explore Enums In Flutter. We will focus on using the enums or extending them with other methods and packages to have the best development experience for your flutter applications.

If you’re looking for the best flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table Of Contents ::

What are enums?

Working with class extensions

Working with enum members

Conclusion



What are enums?

Enums (short for Listed Type, Identification, or several different things relying upon who you ask) at their core is an exceptional sort of class that permits the making of a bunch of constant values related to a specific type.

enum LoadingState {

stopped,

loading,

complete,

failed,

}

For instance, in the model above, stopped, loading, complete, and failed are related to the LoadingState enum. This class itself can’t and needn’t bother with to be started up. The values in the class consider obliging the sorts of states or activities accessible in a specific framework or cycle.

Notwithstanding, with an enum, even a straightforward switch the statement will gripe on the off chance that you don’t fill in every one of the cases:

var state = LoadingState.complete;

switch(state) {

case LoadingState.stopped:
// TODO: Handle this case.
break;
case LoadingState.loading:
// TODO: Handle this case.
break;
case LoadingState.complete:
// TODO: Handle this case.
break;
case LoadingState.failed:
// TODO: Handle this case.
break;

}

Enums likewise reduce the general cognitive above required while working with strings or even a basic steady execution.

Working with class extensions:

As referenced previously, enums are still classes. At the point when augmentations showed up in Dart, existing classes might have new capabilities added that act as though the strategies or values exist in the first class.

extension Demo on double {

double half() => this / 2;

double twice() => this * 2;

}

This was fascinating since this implied additional usefulness could be added to the current enum class, but remotely instead of while characterizing the actual class.

Suppose we needed to add a portrayal field to every one of the loading states in the example. We can make an expansion for this different from the current enum:

extension Description on LoadingState {

String getDescription() {
switch(this) {
case LoadingState.stopped:
return 'Loading is stopped.';
case LoadingState.loading:
return 'Loading is progressing.';
case LoadingState.complete:
return 'Loading is complete.';
case LoadingState.failed:
return 'Loading has failed.';
}
}

}

Any enum value can now have a portrayal utilizing the getDescription() technique.

Working with enum members:

The most recent delivery in Flutter and Dart went along, it conveyed numerous valuable elements. One of them was the ability to now add individuals straightforwardly to enums. This additionally implied adding constructors to set these members.

This is what this resembles by and by with a similar example:

enum LoadingState {

stopped('Loading is stopped.'),

loading('Loading is progressing.'),

complete('Loading is complete.'),

failed('Loading has failed.');

final String description;

const LoadingState(this.description);

}

Here, we move the portrayal field into the enum itself. We want to start up this field for each enum value. There can be more than one value characterized for the enum and named parameters can likewise be utilized.

Alongside these, techniques can be added like the expansion capabilities referenced previously:

enum LoadingState {

stopped('Loading is stopped.'),

loading('Loading is progressing.'),

complete('Loading is complete.'),

failed('Loading has failed.');

final String description;

const LoadingState(this.description);

bool isFinished() => this == complete || this == failed;

}

Conclusion:

In the article, I have explained the Enums System structure in a flutter; you can modify this code according to your choice. This was a small introduction to Enums System User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying the Enums in your flutter projects. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on FacebookGitHubTwitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Related: SMS Using Twilio In Flutter

Related: Using SharedPreferences in Flutter


Flutter for Multi-Channel Communication

0

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.

Table of Contents

Introduction

Features of Multi-Channel Communication

Platform Dependency Overview for Communication Channel

Detailed guide on implementing each communication channels within Flutter apps

Push Notifications with Firebase Cloud Messaging (FCM)

SMS Integration with Twilio

Email Integration with SMTP

In-App Messaging

Voice Calls with Agora

Limitations

Conclusion

References


In today’s app development landscape, communications between apps and users has become a critical feature. To ensure that users are constantly updated engaged and informed of theirs interests, modern mobile apps leverage various communication channels such as Push notifications SMS, email, In App Messaging and Voice calls. Flutter, with its cross-platform capabilities, enables seamless integration of these communication channels, making it an ideal choice for developers.

In this blog, we will explore how to implement multi-channel communication using Flutter. We’ll discuss the platform dependencies, key features, and provide a detailed step-by-step guide for integrating each type of communication. We’ll also look into limitations and best practices to optimize user experience.

Introduction

Multi-channel communication is essential and important for mobile apps, whether it’s for user verification, sending alerts, promotional messages, or notifications. By supporting multiple communication channels, you can cater to a broader audience and ensure that important information reaches users no matter where they are. Flutter, a powerful and popular cross-platform framework, provides excellent packages and tools for integrating these channels.


Features of Multi-Channel Communication

Each communication method comes with its unique features:

  • Push Notifications: These are short messages that pop up on the user’s device screen, even if the app is not open. They are used for real-time alerts, such as a new message, an update, or a special offer. Allows real-time interaction with users, even when the app is in the background or closed. You can send notifications based on user activity, app events, or marketing campaigns.
  • SMS: Provides direct and fast communication, especially useful for user authentication (2FA) and one-time passcodes. SMS messages are more likely to be read quickly compared to emails. This is a widely used text messaging service, often used for user authentication (like OTPs), alerts, or reminders.
  • Email: Suitable for longer-form messages, detailed information, transactional emails (password reset, registration confirmation), promotional content and customer support updates.. Email offers more flexibility in formatting and attachments compared to SMS and push notifications.
  • In-App Messaging: This allows businesses to communicate with users directly within the app, which is useful for delivering targeted messages or promotions.
  • Voice Calls: For urgent or personalized communication, a phone call may be the most effective way to reach a user.

By supporting these various channels, apps can ensure they stay connected with users wherever they are and offer a seamless communication experience across devices and platforms. This is important not only for user engagement but also for building trust and improving user retention


Platform Dependency Overview for Communication Channels

When developing mobile applications that use various communication channels such as Push Notifications, SMS, Email, In-App Messaging, and Voice Calls, it’s important to understand how each of these channels behaves on different platforms like Android and iOS. Below is a breakdown of the platform dependencies for these channels, including any specific challenges or restrictions that may arise.

1. Push Notifications

  • Compatibility: Both Android and iOS support push notifications.

Platform Dependencies:

  • Android: Push notifications work seamlessly using Firebase Cloud Messaging (FCM) or Push Notifications through Google Cloud Messaging (GCM). Android allows background and foreground notifications without many restrictions.
  • iOS: iOS also supports push notifications, but with certain restrictions on background tasks. iOS uses APNs (Apple Push Notification Service), and Firebase Cloud Messaging (FCM) is commonly used for integration. On iOS, the app needs user permission to show notifications, and there are additional considerations for notification handling when the app is in the background or terminated.
  • Common Solution: FCM (Firebase Cloud Messaging) is the most widely used service for push notifications on both platforms as it simplifies sending notifications to both iOS and Android apps.

2. SMS (Short Message Service)

  • Compatibility: SMS functionality is available on both Android and iOS, though there are slight differences in implementation.

Platform Dependencies:

  • Android: Android natively supports sending SMS via apps using SMSManager or using external services like Twilio. Android apps can send SMS directly without requiring extra permissions unless the app is sending messages in the background.
  • iOS: iOS has more restrictive SMS handling. Native SMS sending is limited, and apps cannot send SMS messages without user interaction. To send SMS messages, iOS apps use the MessageUI framework, which opens the SMS dialog for the user to confirm and send the message. Some SMS features may require third-party services like Twilio for better flexibility.
  • Common Solution: For sending SMS programmatically, third-party services like Twilio, Nexmo, or Plivo are often used, as they provide APIs that abstract away the platform dependencies, allowing apps to send SMS across both Android and iOS.

3. Email

  • Compatibility: Email integration is platform-independent, working seamlessly on both Android and iOS.

Platform Dependencies:

  • Android & iOS: Both platforms do not have direct APIs for sending email without using a third-party service. Developers typically use SMTP services or email APIs to send emails from the app.
  • Popular email API services include SendGrid, Mailgun, Amazon SES, and others, which can be accessed through simple HTTP requests, making email functionality highly platform-independent.
  • Common Solution: Since email services are abstracted through APIs, they work uniformly across both platforms. You will need to integrate with services like SendGrid, Mailgun, or Amazon SES to send emails from your app.

4. In-App Messaging

  • Compatibility: In-App Messaging works on both Android and iOS, but the implementation varies due to different platform requirements for notifications and background tasks.

Platform Dependencies:

  • Android: Android provides flexibility in creating custom in-app messages. You can display messages or notifications within the app interface using Firebase In-App Messaging, OneSignal, or custom solutions. Background message handling is more straightforward compared to iOS, and Android allows persistent or real-time messages in both foreground and background modes.
  • iOS: iOS has a more restrictive environment when handling background tasks. To implement in-app messaging in the background, you might need to rely on PushKit (for VoIP apps) or Firebase In-App Messaging for real-time, event-driven messages. Permissions and user interaction are crucial when displaying messages on iOS.
  • Common Solution: Firebase In-App Messaging is a popular choice for both Android and iOS because it abstracts many platform-specific details and provides a unified way of sending messages to users. Other options include OneSignal, Pusher, or custom in-app messaging solutions built on top of Firestore or Firebase Realtime Database.

5. Voice Calls

  • Compatibility: Voice call functionality is supported on both Android and iOS, but it requires careful integration of platform-specific technologies for handling real-time communication.

Platform Dependencies:

  • Android: Android supports voice call functionality through ConnectionService and CallManager for VoIP (Voice over IP) calls. For third-party services, Twilio, Agora, and WebRTC can be used to integrate voice calling capabilities in Android apps. Android also allows background services to manage active calls, but this is restricted in newer versions (Android 8 and above) to improve battery life.
  • iOS: iOS handles voice calls through CallKit for integrating with the native phone call interface. It also uses PushKit for receiving incoming VoIP calls in the background. iOS applications that need to handle voice calls often rely on services like Twilio, Agora, or WebRTC, which provide SDKs for both platforms. iOS requires additional permissions to access the microphone, make network requests, and handle push notifications for incoming calls.
  • Common Solution: Twilio, Agora, and WebRTC are the most popular third-party services used for voice call integration on both iOS and Android. These services provide SDKs that abstract much of the platform-specific handling, making it easier to implement voice calls on both platforms.

Detailed guide on implementing each communication channels within Flutter apps

Push Notifications with Firebase Cloud Messaging (FCM)

Push notifications allow apps to keep users engaged and informed about important updates, events, and offers. Firebase Cloud Messaging (FCM) is a widely used solution for sending push notifications.

Step-by-step guide to implementing FCM in Flutter:

Set up Firebase in your Flutter app: Follow the instructions in the Firebase setup guide to add Firebase to your project. Make sure to enable Cloud Messaging for your Firebase project.

Add dependencies:

dependencies:
firebase_core: ^2.0.0
firebase_messaging: ^13.0.0

Initialize Firebase in your app:

await Firebase.initializeApp();
FirebaseMessaging messaging = FirebaseMessaging.instance;

Request notification permissions (iOS):

NotificationSettings settings = await messaging.requestPermission();

Handle notifications:

NotificationSettings settings = await messaging.requestPermission();
  • Foreground: Handle push notifications when the app is open using the following code:
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Received message: ${message.notification?.title}');
});
  • Background: Handle notifications when the app is in the background:
Future<void> backgroundHandler(RemoteMessage message) async {
print('Handling a background message: ${message.notification?.title}');
}
FirebaseMessaging.onBackgroundMessage(backgroundHandler);

Get FCM Token

we can get the FCM token manually for testing purposes using the code below. To retrieve the current registration token for an app instance, call getToken() in the main() method. This method will ask the user for notification permissions if notification permission has not been granted. Otherwise, it returns a token or rejects if there’s any error.

final fcmToken = await FirebaseMessaging.instance.getToken();
log("FCMToken $fcmToken");

Send notifications from Firebase Console or backend: Notifications can be sent from the Firebase console using the Device FCM Token your or own server using Firebase Admin SDK.

SMS Integration with Twilio

SMS integration is often used for one-time password (OTP) authentication, order updates, or other notifications that need immediate attention. Twilio is a popular service for sending SMS messages.

Step-by-step guide to sending SMS using Twilio:

Sign up for Twilio: Get your Account SID and Auth Token from Twilio.

Add dependencies:

dependencies:
twilio_flutter: ^0.4.0

Send an SMS using Twilio:

import 'package:twilio_flutter/twilio_flutter.dart';
TwilioFlutter twilioFlutter = TwilioFlutter(
accountSid: 'your_account_sid',
authToken: 'your_auth_token',
twilioNumber: 'your_twilio_phone_number',
);
void sendSMS(String toPhoneNumber, String message) async {
await twilioFlutter.sendSMS(
to: toPhoneNumber,
body: message,
);
print('SMS sent to $toPhoneNumber');
}

Handle SMS in the app: For more advanced use cases like two-factor authentication (2FA), you can generate OTPs, send them via SMS, and validate the code when the user enters it.

Email Integration with SMTP

Email is a versatile and widely used communication channel. You can use email for user verification, password resets, and sending marketing content.

Step-by-step guide to sending emails using SMTP:

Add dependencies:

dependencies:
mailer: ^4.0.0

Configure SMTP settings: Configure your SMTP server (e.g., Gmail, SendGrid, Mailgun) and set up the message details.

Send email:

import 'package:mailer/mailer.dart';
import 'package:mailer/smtp_server.dart';
void sendEmail() async {
String username = 'your_email@example.com';
String password = 'your_email_password';
final smtpServer = gmail(username, password); // Or configure for SendGrid or other providers
  final message = Message()
..from = Address(username, 'Your Name')
..recipients.add('recipient@example.com')
..subject = 'Test email from Flutter'
..text = 'This is an email sent from a Flutter app.';
  try {
final sendReport = await send(message, smtpServer);
print('Message sent: $sendReport');
} on MailerException catch (e) {
print('Message not sent. Error: $e');
}
}

In-App Messaging

In-app messaging allows you to send messages to users while they are using the app. There are various solutions to implement in-app messaging, but we will focus on using Firebase In-App Messaging and OneSignal as popular options. These solutions support both Android and iOS and are easy to integrate into Flutter apps.

Firebase In-App Messaging

Firebase provides an in-app messaging service that can be used to send personalized, targeted messages to users while they’re interacting with your app.

Step 1: Set Up Firebase in Your Flutter App

Before using Firebase In-App Messaging, you need to set up Firebase in your Flutter app.

Add Firebase to Your Flutter App:

  • Follow the steps in the FlutterFire documentation to add Firebase to your Android and iOS projects.
  • Install the required packages in your pubspec.yaml:
dependencies:
firebase_core: ^2.5.0
firebase_in_app_messaging: ^0.5.0

Initialize Firebase:

  • In your main.dart file, initialize Firebase
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

Step 2: Integrating Firebase In-App Messaging

Enable Firebase In-App Messaging:

  • Go to the Firebase Console > In-App Messaging, and enable it.
  • Create in-app messages using the Firebase Console (you can create banners, modals, or image messages).

Receive Messages in Your App: Firebase will automatically display messages in your app based on the configurations you set in the Firebase Console. You can customize the behavior of these messages (e.g., triggering on app open, based on user events, etc.).

Customizing the Messages: To customize the handling of in-app messages, you can listen to Firebase in-app messaging events like this:

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
FirebaseInAppMessaging.instance.setAutomaticDataCollectionEnabled(true);
FirebaseInAppMessaging.instance.triggerEvent('app_open');

return MaterialApp(
title: 'In-App Messaging',
home: HomeScreen(),
);
}
}

Voice Calls with Agora

Voice calls can be integrated into a Flutter app using services like Twilio, Agora, or WebRTC. Below, we’ll focus on integrating Agora for real-time voice calling.

Agora Voice Calls

Agora provides APIs for real-time voice and video communication, making it easy to implement voice calls in a Flutter app.

Step 1: Set Up Agora

Create an Agora Account:

  • Go to Agora’s website and create an account.
  • Create a new project in the Agora Console to get your App ID.

Add Agora to Your Flutter App:

  • Add the necessary dependencies in your pubspec.yaml:
dependencies:
agora_rtc_engine: ^5.0.0

Configure Agora in Your App:

  • Import Agora package in your main.dart file:
class VoiceCallScreen extends StatefulWidget {
@override
_VoiceCallScreenState createState() => _VoiceCallScreenState();
}
class _VoiceCallScreenState extends State<VoiceCallScreen> {
static const _appId = 'YOUR_AGORA_APP_ID'; // Replace with your Agora App ID
late RtcEngine _engine;
  @override
void initState() {
super.initState();
_initializeAgora();
}
  Future<void> _initializeAgora() async {
_engine = await RtcEngine.create(_appId);
await _engine.enableAudio();
await _engine.joinChannel(null, 'testChannel', null, 0);
}
  @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Voice Call")),
body: Center(
child: ElevatedButton(
onPressed: () {
_endCall();
},
child: Text('End Call'),
),
),
);
}
  void _endCall() {
_engine.leaveChannel();
Navigator.pop(context);
}
  @override
void dispose() {
super.dispose();
_engine.release();
}
}

Step 2:Managing Voice Calls

  • Starting a Call: You can initiate a voice call by calling the joinChannel method after creating the Agora engine.
  • Ending a Call: Call leaveChannel when the user ends the call.
  • Handling Incoming Calls: You can handle incoming calls by integrating push notifications and using PushKit (for iOS) or Firebase Cloud Messaging (for Android) to notify users when a voice call is incoming.

Limitations

While multi-channel communication greatly enhances user engagement and allows apps to reach users through various touchpoints, it does come with certain limitations. Here’s a detailed breakdown of the limitations for each communication channel:

1. Push Notifications

iOS Limitations:

  • Background Activity Restrictions: iOS imposes strict limits on how apps can operate in the background, which can affect push notifications. For instance, the app may not be able to handle push notifications or background tasks efficiently if the app is not running in the foreground, especially with system restrictions on iOS 13 and later.
  • Notification Delivery Delays: Push notifications on iOS might be delayed due to the system prioritizing other activities like system updates or battery saving.

User Permission:

  • Opt-In Requirement: Users need to explicitly grant permission to receive push notifications, and many users choose not to enable notifications. This opt-in process can significantly reduce the engagement rates, particularly if the user is prompted with permission requests at an inconvenient time.
  • App Usage Impact: Poorly timed permission requests may lead to users denying notifications altogether, leading to reduced communication reach.

Other Limitations:

  • Frequency Limitation: Push notifications must be sent sparingly to avoid user fatigue. Excessive notifications can annoy users, leading them to disable notifications or uninstall the app.
  • Notification Blocking: Both Android and iOS users can block or mute notifications for specific apps, leading to decreased visibility of key messages.

2. SMS (Short Message Service)

Platform Restrictions:

  • iOS Restrictions: iOS places strict limits on SMS functionality, especially when sending messages programmatically. SMS sending is typically confined to user interactions, meaning apps cannot send SMS without user consent and interaction. Apps must invoke the MessageUI framework to open the SMS dialog, limiting the automation of SMS sending.

Cost:

  • Third-Party Service Fees: Sending SMS messages through services like Twilio or Nexmo can be costly, especially if sending large volumes of messages. The cost per SMS can add up quickly for large-scale apps, making SMS a less cost-effective option for apps with a broad user base or high message frequency.

Other Limitations:

  • Character Limit: SMS messages are limited to 160 characters, which may not be sufficient for delivering long-form content or detailed messages. This can lead to additional costs if messages need to be split into multiple parts.
  • Delivery Failures: SMS delivery can sometimes fail due to network issues, carrier restrictions, or invalid phone numbers. There is no guarantee that all messages will be delivered successfully.
  • Limited User Interaction: Unlike in-app messages or push notifications, SMS lacks interactivity and richer content, making it a more basic form of communication.

3. Email

Spam Filters:

  • Risk of Being Marked as Spam: Emails sent from apps or marketing services may end up in spam folders due to spam filters, reducing the chances of important information being seen by users. This is particularly problematic when users are not expecting emails or when the email contains certain flagged keywords.

SMTP Limitations:

  • Email Sending Limits: Many SMTP providers impose sending limits, such as daily or hourly quotas on the number of emails that can be sent. This can be an issue for large apps or services that need to send bulk emails. For example, services like SendGrid or Mailgun might limit the number of emails that can be sent on a free or basic plan.
  • Throttling: SMTP services may throttle email sending rates if an app sends too many emails in a short period, potentially delaying or blocking important messages.

Other Limitations:

  • Low Engagement Rate: Email open rates can be low, and users may ignore or unsubscribe from email lists if they receive too many irrelevant or unsolicited emails.
  • Content Rendering: Email rendering may vary across different email clients (e.g., Gmail, Outlook, Apple Mail), and certain formatting or media may not display correctly on all platforms. This could affect the user experience.

4. In-App Messaging

Platform-Specific Restrictions:

  • Background Limitations (iOS): In-app messages may not be visible if the app is running in the background or has been closed by the user. iOS’s strict background task restrictions can limit the ability to display time-sensitive messages when the app is not active.
  • User Interaction: Unlike push notifications, in-app messages require the user to have the app open to view them. This means users may miss important messages if they don’t actively open the app, leading to reduced engagement.

User Experience Impact:

  • Annoyance Factor: Overuse of in-app messaging can lead to poor user experience. If messages are displayed too frequently or in an intrusive manner, it can cause annoyance and may lead users to uninstall or disable notifications for the app.
  • Message Saturation: Over time, users might tune out in-app messages, particularly if they are not well-targeted or relevant, diminishing the effectiveness of this communication channel.

5. Voice Calls

Platform Limitations:

  • Background Restrictions (iOS and Android): Both iOS and Android impose background activity restrictions that can hinder voice call functionality. For example, on iOS, if a call comes in while the app is in the background, it might not trigger the call notification until the app is brought to the foreground. Android also has restrictions, particularly with background services, starting from Android 8 (Oreo).

Cost:

  • High Service Costs: Services like Twilio, Agora, or WebRTC often charge based on the minutes of call time or the number of active users, making it an expensive solution for apps that need to support large-scale voice communication. The cost can be prohibitive for apps that require frequent or long voice calls.

Technical Challenges:

  • Network Dependency: Voice call quality depends heavily on network conditions. Poor network connections can result in dropped calls or poor audio quality, which can affect user experience.
  • Integration Complexity: Integrating voice call functionality involves using third-party services and APIs, which can add complexity to the app’s development process. Handling the infrastructure for real-time communication (e.g., signaling, media transport) can be technically demanding.

Regulatory and Privacy Concerns:

  • Privacy and Security: Voice calls often require handling sensitive user data (like phone numbers), which brings regulatory and privacy concerns (such as GDPR or CCPA) that need to be addressed. This might include encryption of call data and secure handling of phone numbers.

User Experience:

  • Involuntary Interruptions: Voice calls can be seen as intrusive by some users, especially if they are unexpected or unwanted. If users are not ready or willing to engage in a call, they might feel annoyed or overwhelmed.

Conclusion

Flutter’s ability to integrate Push notifications, SMS, email, In-App Messaging and Voice Calls makes it a powerful tool for building engaging, communicative apps. Whether you are implementing user authentication, transactional emails, or real-time notifications, Flutter provides the necessary packages and APIs to ensure smooth integration of these communication channels.

However, developers should be aware of the platform limitations and costs associated with each channel. By following best practices, ensuring user consent, and leveraging the right tools, you can build a communication system that enhances user experience and boosts engagement.


At What the Flutter, we love working with Flutter apps to make them high-performing that delight users. Contact us today to discuss how we can optimize your Flutter app performance.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


References:

Sending SMS Messages with Dart and Twilio Programmable SMS
Computer programming tutorial demonstrating how to use the Dart programming language to send SMS messages using Twlio…www.twilio.com

Sending an SMTP email in Dart
I looked through the API documentation and language guide, but I did not see anything about sending emails in Dart. I…stackoverflow.com

Send and receive notifications for a Flutter app using Firebase Cloud Messaging
In this codelab, you use the FCM HTTP v1 API to send push notifications to an app running on multiple platforms. You…firebase.google.com


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on FacebookGitHubTwitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.


Optimizing Flutter Apps for Performance


If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.

Table of contents

Introduction

Understanding Flutter Performance

Factors affecting Flutter app performance

Measuring performance

Profiling performance

Why do we need to Optimize our application?

How can we optimize our flutter application?

Conclusion

References


Introduction

Optimizing Flutter apps is crucial for delivering smooth and responsive user experiences, especially on resource-limited mobile devices. As Flutter apps grow in complexity, factors like widget rebuilds, inefficient data handling, and heavy asset usage can slow performance. By focusing on efficient widget management, memory optimization, and streamlined network requests, you can significantly enhance app responsiveness and reduce memory usage. Let’s dive into some best practices and techniques to help optimize your Flutter app for peak performance.


Understanding Flutter Performance

In the context of Flutter apps, performance primarily revolves around two key metrics:

  • Rendering speed: This refers to how quickly Flutter can generate the pixels that make up your app’s UI on the screen. Ideally, Flutter should be able to render each frame in roughly 16 milliseconds (ms) to achieve a smooth 60 frames per second (FPS) experience. This ensures a seamless and responsive interaction for the user.
  • Frame rate (FPS): FPS refers to the number of times per second that the app’s UI is updated and redrawn on the screen. A higher FPS translates to a smoother and more fluid user experience. Conversely, a low FPS can lead to choppiness, lag, and a feeling of sluggishness.

Factors affecting Flutter app performance

Several factors can influence the performance of your Flutter app. Here’s a breakdown of some key contributors:

  • Widget tree complexity: Flutter builds your app’s UI using a hierarchy of widgets. A complex widget tree with many nested widgets can take longer to render, impacting performance.
  • Widget rebuild frequency: Flutter rebuilds the entire widget subtree whenever a change occurs in its state, even if the change only affects a small portion of the UI. This can be a performance bottleneck for frequently updated widgets or those deeply nested within the widget tree.
  • State management strategy: The way you manage your app’s state can significantly impact performance. Poor state management practices can trigger unnecessary widget rebuilds, leading to slowdowns.
  • UI complexity: Visually complex UIs with rich animations, heavy layouts, or large images can require more processing power to render, potentially affecting performance.
  • Device capabilities: The performance of your app will also be influenced by the user’s device. Devices with lower processing power, limited memory, or slower network connections will experience slower app performance.

While understanding the theoretical aspects of performance is valuable, it’s crucial to have practical tools at your disposal to measure and profile your Flutter app’s actual performance. Here’s how you can get started:

Measuring performance

Several built-in tools and techniques can help you assess your Flutter app’s performance:

  • Flutter DevTools: This suite of developer tools provides a wealth of information about your app’s performance. Within DevTools, the Performance view allows you to:
  • Analyze frame rate: Monitor the app’s FPS in real-time and identify any drops that might indicate performance issues.
  • Track build times: See how long it takes for widgets to rebuild during the build phase. This helps pinpoint potential bottlenecks related to complex widget trees or inefficient build methods.
  • Visualize the rendering pipeline: Gain insights into the different stages of the rendering pipeline and identify any bottlenecks that might be slowing down the process.
  • The Timeline Class: The Timeline methods add synchronous events to the timeline. When generating a timeline in Chrome’s tracing format, using Timeline generates “Complete” events. Timeline’s startSync and finishSync can be used explicitly, or implicitly by wrapping a closure in timeSync.
Timeline.startSync("Doing Something");
doSomething();
Timeline.finishSync();

Profiling performance

Profiling goes beyond just measuring performance metrics. It involves analyzing how your app utilizes resources like CPU, memory, and network bandwidth. This deeper analysis helps you pinpoint the root cause of performance issues.

  • Dart observatory: This tool provides detailed insights into your app’s memory usage and allocation. You can use it to identify potential memory leaks or excessive memory consumption that might be impacting performance.
  • Performance overlay: Flutter DevTools offer a Performance Overlay that can be displayed on top of your running app. This overlay provides real-time information about widget rebuilds, frame rate, and build times, allowing you to visualize performance issues directly within the app itself.
WidgetsApp(
showPerformanceOverlay: true,
// Other properties
);

Why do we need to Optimize our application?

In essence, optimization ensures the app provides a quality experience across devices, respects user resources, and stays competitive in an ever-growing market. Here are some top notch reasons for it:

1. Enhanced User Experience

  • Performance: Users expect smooth interactions and minimal load times. Optimization reduces lag, stuttering, and crashes, leading to a seamless user experience.
  • Responsiveness: Optimized apps respond faster to user inputs, making interactions feel natural and responsive, which is critical for user retention.

2. Efficient Resource Usage

  • Battery Life: Optimized apps consume less CPU, GPU, and memory, which can reduce battery drain — a big plus for mobile users.
  • Memory Management: By reducing memory usage and preventing memory leaks, you can avoid crashes and freezes on lower-end devices or when dealing with large data sets.

3. Reduced Data Usage

  • Efficient Network Calls: Optimization of network requests, including data compression and caching, minimizes data usage, making the app more friendly to users with limited or costly internet access.

4. Improved Device Compatibility

  • Support for Lower-End Devices: Optimized apps can run smoothly on a wider range of devices, including those with lower processing power and limited resources, expanding the app’s reach.

5. Higher App Store Ratings

  • User Satisfaction: Faster, smoother apps tend to receive better reviews. Higher ratings and positive feedback in app stores improve visibility and credibility, driving more downloads.

How can we optimize our flutter application?

There are variety of ways following which the app can be optimized.

Some of them are :

1. Optimize Widgets

  • Use const Widgets: Marking widgets as const helps Flutter avoid rebuilding them unnecessarily, as it knows the widget won’t change. Use const constructors on widgets as much as possible, since they allow Flutter to short-circuit most of the rebuild work. To be automatically reminded to use const when possible, enable the recommended lints from the flutter_lints package. For more information, check out the flutter_lints migration guide.
// Better: Declared as const so it won't rebuild
const Text("Hello");
// Avoid this if it doesn't change
Text("Hello");
  • Break Down Large Widgets: Avoid overly large single widgets with a large build() function. Split complex widgets into smaller widgets to reduce the amount of code Flutter has to re-render and improve readability.

Widget build(BuildContext context) {
return Column(
children: [
ProfileHeader(), // Small widget for header
ProfileDetails(), // Small widget for details
],
);
}
  • Avoid Unnecessary Rebuilds: Wrap widgets in setState only when necessary to avoid excessive re-rendering of unrelated parts of the widget tree. When setState() is called on a State object, all descendent widgets rebuild. Therefore, localize the setState() call to the part of the subtree whose UI actually needs to change. Avoid calling setState() high up in the tree if the change is contained to a small part of the tree.
  • Use RepaintBoundary to isolate parts of the widget tree: The RepaintBoundary widget helps in isolating parts of the widget tree, preventing unnecessary repaints. This approach can significantly reduce the workload on the rendering engine.
RepaintBoundary(
child: MyExpensiveWidget(),
);

2. Optimize complex layouts

  • Explore using SingleChildScrollView or GridView for layouts with long lists or grids, as they are optimized for performance.
  • Use ListView.builder: Using ListView.builder instead of ListView avoids rendering all items at once by creating them only when they’re visible.
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
);
  • Use GridView.builder: Using GridView.builder instead of GridViewavoids rendering all items at once by creating them only when they’re visible.
  • Implement lazy loading and pagination: Implement lazy loading and pagination to load data as needed, reducing initial load times and memory usage.
class PaginatedList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
if (index == items.length - 1) {
// Trigger pagination
}
return ListTile(
title: Text(items[index]),
);
},
);
}
}
  • Use Keys for List Items: Assigning unique keys to items in a list helps Flutter efficiently track and update only the changed items.
ListView(
children: [
ListTile(key: ValueKey('item1'), title: Text('Item 1')),
ListTile(key: ValueKey('item2'), title: Text('Item 2')),
],
);

3. Image Optimization

  • Compress Images: Before adding images to your assets, use compressed formats like JPEG or WebP, which are optimized for mobile.
  • Optimize image sizes and formats: Ensure images are optimized for size and format to reduce load times and improve performance.
  • Use cached_network_image for Image Caching: Use this package to store images locally, reducing the need for repeated network calls.
CachedNetworkImage(
imageUrl: 'https://example.com/image.png',
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
);
  • Consider SVGs for Simple Graphics: For icons and vector graphics, SVGs use less memory and load faster.
SvgPicture.asset("assets/icon.svg");

4. Memory Management

  • Dispose of Resources: Always dispose of controllers and streams in the dispose method to free up memory when the widget is removed.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final TextEditingController _controller = TextEditingController();
  @override
void dispose() {
_controller.dispose(); // Dispose of the controller
super.dispose();
}
}
  • Avoid Excessive Nesting: Excessively deep widget trees use more memory and make the code harder to maintain. Consider using functions or smaller widgets to simplify the structure.

5. Efficient Networking

  • Use Pagination: Only load a limited number of items at once, and load more as the user scrolls to the end of the list.

ListView.builder(
controller: _scrollController,
itemCount: items.length,
itemBuilder: (context, index) {
if (index == items.length - 1) {
_loadMoreData();
}
return ListTile(title: Text(items[index]));
},
);
  • Data Caching and Compression: Use the dio package with GZIP compression to reduce data transfer.
Dio dio = Dio();
dio.options.headers['Content-Encoding'] = 'gzip';

6. Async Operations

  • Use compute() for Heavy Computations: Offload tasks to a separate isolate to keep the UI responsive.
Future<int> _calculateSum() async {
return await compute(expensiveCalculation, 1000);
}
  • Debounce User Input: Limit rapid user input by implementing a debounce to reduce the number of API calls or heavy tasks.
Timer? _debounce;
void onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
// Call your API or update state here
});
}

7. Reduce App Size

  • Minimize Dependencies: Check pubspec.yaml to remove unused packages that add unnecessary weight to your app.
  • Enable Tree Shaking: Flutter’s tree-shaking feature automatically removes unused code in the release build.
  • Remove unused resources and code: Regularly audit your project to identify and remove any unused images, code, or assets that are bloating your app size. Tools like flutter analyze can help with this process.
  • Split APKs by Architecture: Use the --split-per-abi flag to generate APKs optimized for specific architectures, reducing the APK size.
flutter build apk --split-per-abi

8. Performance Profiling

  • Flutter DevTools: Use DevTools to monitor widget rebuilds, track memory usage, and diagnose CPU performance issues.
flutter pub global activate devtools
flutter pub global run devtools

In DevTools, you can visualize your widget rebuilds, analyze memory usage, and identify potential performance bottlenecks.

9. Minimize expensive operations

Some operations are more expensive than others, meaning that they consume more resources. Obviously, you want to only use these operations when necessary. How you design and implement your app’s UI can have a big impact on how efficiently it runs.

10. Control build() cost

Here are some things to keep in mind when designing your UI:

  • The traversal to rebuild all descendents stops when the same instance of the child widget as the previous frame is re-encountered. This technique is heavily used inside the framework for optimizing animations where the animation doesn’t affect the child subtree. See the TransitionBuilder pattern and the source code for SlideTransition, which uses this principle to avoid rebuilding its descendents when animating. (“Same instance” is evaluated using operator ==, but see the pitfalls section at the end of this page for advice on when to avoid overriding operator ==.)
  • To create reusable pieces of UIs, prefer using a StatelessWidget rather than a function.

11. Use saveLayer() thoughtfully

Some Flutter code uses saveLayer(), an expensive operation, to implement various visual effects in the UI. Even if your code doesn’t explicitly call saveLayer(), other widgets or packages that you use might call it behind the scenes. Perhaps your app is calling saveLayer() more than necessary; excessive calls to saveLayer() can cause jank.

12. Minimize use of opacity and clipping

Opacity is another expensive operation, as is clipping. Here are some tips you might find to be useful:

  • Use the Opacity widget only when necessary. See the Transparent image section in the Opacity API page for an example of applying opacity directly to an image, which is faster than using the Opacity widget.
  • Instead of wrapping simple shapes or text in an Opacity widget, it’s usually faster to just draw them with a semitransparent color. (Though this only works if there are no overlapping bits in the to-be-drawn shape.)
  • To implement fading in an image, consider using the FadeInImage widget, which applies a gradual opacity using the GPU’s fragment shader. For more information, check out the Opacity docs.
  • Clipping doesn’t call saveLayer() (unless explicitly requested with Clip.antiAliasWithSaveLayer), so these operations aren’t as expensive as Opacity, but clipping is still costly, so use with caution. By default, clipping is disabled (Clip.none), so you must explicitly enable it when needed.
  • To create a rectangle with rounded corners, instead of applying a clipping rectangle, consider using the borderRadius property offered by many of the widget classes.

13. Leverage asynchronous programming

Asynchronous programming is essential for maintaining a responsive UI.

  • Use async/await effectively: Using async and await allows your app to perform non-blocking operations, keeping the UI responsive.
Future<void> fetchData() async {
final data = await apiService.getData();
// Process data
}
  • Avoid blocking the main thread: Ensure that heavy computations and long-running tasks are performed off the main thread to prevent UI freezes.
compute(expensiveFunction, data);

14. Optimize network calls

Efficient network handling is crucial for app performance.

  • Use efficient APIs and data formats: Optimize your APIs and use efficient data formats like JSON to reduce payload sizes and improve response times.
  • Implement caching strategies: Implementing caching mechanisms can reduce the number of network requests and improve performance.
class CacheService {
final _cache = <String, dynamic>{};
void cacheData(String key, dynamic data) {
_cache[key] = data;
}
dynamic getData(String key) {
return _cache[key];
}
}
  • Reduce the number of network requests: Batching requests and minimizing unnecessary network calls can significantly enhance performance.

Conclusion

By following these best practices and exploring advanced techniques, you can significantly improve the performance of your Flutter app. Ultimately, these optimizations not only enhance app performance but also broaden device compatibility, improve user retention, and lead to higher app store ratings. With these strategies, your Flutter app can achieve both technical excellence and user satisfaction. Remember, performance optimization is an ongoing process. As your app evolves, revisit these strategies and adapt them to your specific needs.


At What the Flutter, we love working with Flutter apps to make them high-performing that delight users. Contact us today to discuss how we can optimize your Flutter app performance.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


References:

Performance best practices
How to ensure that your Flutter app is performant.docs.flutter.dev

How to Improve Flutter App Performance: Best Practices
Discover the best practices and optimization techniques to enhance your Flutter app performance for smooth and…blog.flutter.wtf


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on FacebookGitHubTwitter, and LinkedIn for any flutter-related queries.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.