Flutter architectural overview | Flutter

Flutter architectural overview | Flutter

Understanding the Core Principles and Concepts of Flutter’s Design

Flutter is a powerful cross-platform framework that allows developers to create high-performance, visually stunning mobile applications using a single codebase. Its unique architectural approach sets it apart from traditional UI frameworks, offering significant benefits in terms of development efficiency, code reusability, and overall performance. In this in-depth article, we’ll dive into the core principles and concepts that form the design of the Flutter framework, providing seasoned IT professionals with a comprehensive understanding of its inner workings.

The Layered Architecture of Flutter

At its core, Flutter is designed as an extensible, layered system. It exists as a series of independent libraries, each relying on the underlying layer. This modular design ensures that no single layer has privileged access to the layer below, and every part of the framework is designed to be optional and replaceable.

From the perspective of the underlying operating system, Flutter applications are packaged in the same way as any other native application. A platform-specific embedder provides an entrypoint, coordinates with the operating system for access to services like rendering surfaces, accessibility, and input, and manages the message event loop. The embedder is written in a language that is appropriate for the target platform, such as Java and C++ for Android, Objective-C/Objective-C++ for iOS and macOS, and C++ for Windows and Linux.

The Flutter engine, written mostly in C++, sits at the core of the framework and provides the low-level implementation of Flutter’s core API, including graphics, text layout, file and network I/O, accessibility support, plugin architecture, and a Dart runtime and compile toolchain. The engine is exposed to the Flutter framework through the dart:ui library, which wraps the underlying C++ code in Dart classes and exposes the lowest-level primitives.

At the top of the stack is the Flutter framework, a modern, reactive framework written in the Dart language. It includes a rich set of platform, layout, and foundational libraries, composed of a series of layers. These layers work together to provide developers with a comprehensive set of tools and abstractions for building cross-platform applications.

The Reactive Paradigm of UI Development

One of the defining characteristics of Flutter is its adoption of a reactive, declarative approach to UI development. Unlike traditional UI frameworks, where the developer must manually update the interface in response to changes in application state, Flutter’s model decouples the user interface from its underlying state.

In a Flutter app, widgets (the building blocks of the UI) are represented by immutable classes that are used to configure a tree of objects. These widgets are used to manage a separate tree of objects for layout, which is then used to manage a separate tree of objects for compositing. Flutter’s core mechanisms are responsible for efficiently walking the modified parts of these trees, converting them into lower-level trees of objects, and propagating changes across the tree.

The build() method of a widget is a function that converts the current state of the application into a UI representation. This method is designed to be fast and free of side effects, allowing the framework to call it as often as needed (potentially once per rendered frame) to update the UI in response to changes in state.

Widgets and Composition

Widgets are the fundamental building blocks of a Flutter app’s user interface. Each widget is an immutable declaration of part of the user interface, and widgets form a hierarchy based on composition, with each widget nesting inside its parent.

Flutter’s UI is constructed by composing many small, single-purpose widgets that combine to produce powerful effects. The framework uses the same core concept (a Widget) to represent drawing to the screen, layout, user interactivity, state management, theming, animations, and navigation, resulting in a large vocabulary of specialized widgets.

This emphasis on composition allows developers to create custom widgets by combining and configuring existing ones in novel ways, rather than subclassing or inheriting from a set of predefined UI components.

Stateful and Stateless Widgets

Flutter introduces two major classes of widgets: stateful and stateless widgets. Stateless widgets are those that have no mutable state; they don’t have any properties that change over time. Stateful widgets, on the other hand, are those whose unique characteristics need to change based on user interaction or other factors.

Stateful widgets store their mutable state in a separate State object. Whenever the state of a stateful widget changes, the setState() method must be called to signal the framework to update the user interface by calling the State object’s build() method again.

State Management and Inheritance

As widget trees grow deeper, passing state information up and down the hierarchy can become cumbersome. Flutter provides the InheritedWidget to address this problem, allowing widgets to easily access shared state from a common ancestor in the widget tree.

InheritedWidget is used extensively within the framework itself, such as for managing the application’s visual theme, which includes properties like color and typography that are pervasive throughout the application.

For more advanced state management needs, Flutter developers often turn to utility packages like provider, which provide a wrapper around InheritedWidget to further simplify the process of managing and sharing state.

The Rendering Pipeline

Flutter’s rendering pipeline is the series of steps it takes to convert a hierarchy of widgets into the actual pixels painted onto a screen. Unlike traditional cross-platform frameworks that rely on abstraction layers over native UI libraries, Flutter bypasses these layers in favor of its own widget set.

The key principle behind Flutter’s rendering pipeline is “simple is fast.” Flutter has a straightforward pipeline for data flow, with each step playing a critical role in the efficient translation of widgets into a rendered scene.

During the build phase, Flutter translates the widgets expressed in code into a corresponding element tree, with one element for every widget. The element tree is persistent from frame to frame, allowing Flutter to act as if the widget hierarchy is fully disposable while caching its underlying representation.

The layout phase involves walking the render tree in a depth-first traversal and passing down size constraints from parent to child. Each child object must respect the constraints given to it by its parent, and at the end of this process, every object has a defined size within its parent’s constraints and is ready to be painted.

Finally, when the platform demands a new frame to be rendered, the RenderView object at the root of the render tree triggers an update of the scene, which is then passed to the GPU for rendering.

Platform Integration and Interoperability

While Flutter’s UI is drawn entirely by the framework, there are still times when developers need to interact with platform-specific APIs or embed native controls within a Flutter app. Flutter provides several mechanisms to enable this interoperability.

For platform-specific code and APIs, Flutter uses platform channels, which allow for communication between Dart code and platform-specific code written in languages like Kotlin, Swift, or C++. Platform channels handle the serialization and deserialization of data between the two environments.

Flutter also supports embedding native controls, such as Android views or iOS UIViews, within a Flutter app using platform view widgets (AndroidView and UiKitView). These widgets act as an intermediary between the Flutter content and the underlying platform, allowing for the integration of complex, platform-specific UI components.

Flutter on the Web

While the core architectural concepts of Flutter apply to all platforms it supports, the web version of Flutter has some unique characteristics. Instead of using a C++-based engine, the web version of Flutter compiles the Flutter framework (written in Dart) to JavaScript, using either an HTML-based or WebGL-based rendering approach.

The web version of Flutter also does not require a Dart runtime, as the Dart code is compiled to optimized JavaScript for deployment. During development, Flutter web uses dartdevc, a compiler that supports incremental compilation and hot restart, while the production build uses the highly optimized dart2js compiler.

Conclusion

Flutter’s architectural design emphasizes simplicity, modularity, and a focus on efficient rendering and state management. By bypassing traditional abstraction layers and providing a reactive, declarative approach to UI development, Flutter empowers developers to create high-performance, visually stunning applications that feel natural on multiple platforms.

Through its layered architecture, powerful widget composition, and advanced state management capabilities, Flutter offers a compelling alternative to traditional cross-platform frameworks. As an experienced IT professional, understanding the core principles and concepts behind Flutter’s design will equip you with the knowledge to make informed decisions, architect robust Flutter applications, and deliver exceptional user experiences across a wide range of devices and platforms.

Facebook
Pinterest
Twitter
LinkedIn

Newsletter

Signup our newsletter to get update information, news, insight or promotions.

Latest Post