Flutter Design Patterns: 4 — Composite

An overview of the Composite design pattern and its implementation in Dart and Flutter

In the last article, I have analysed the Template Method design pattern. This time I would like to represent the pattern which is pretty simple to understand (comparing to the other design patterns) and is related to the implementation of the Flutter framework itself — the Composite design pattern.


Table of Contents

  • Analysis
  • Implementation
  • Other articles in this series
  • Your contribution

What is the Composite design pattern?

Cat-box composition (source)

The Composite is one of the structural design patterns. Its intention in the GoF book is described as:

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

To understand the Composite design pattern, you should be familiar with the tree data structure.

Tree data structure (source)

In simple words, the tree data structure consists of nodes (elements) and edges (relations between nodes). Each node could have multiple children nodes, but each child could have only one parent node. The root node is the base of the tree, which has no parent node. The leaf is a tree node, which does not have any children. In the Composite design pattern context, we use two types of nodes — leaf (a component, which has no child components) and composite (a component, which contains one or more child components). Basically, any hierarchical data could be represented and stored as a tree structure. The main problem — how to implement this kind of structure in code? A very inflexible way is to define leaf and composite objects differently, treat the composite object as a container for the leaf objects by specifying a specific logic, interface for it. This leads to the issue when a client should treat leaf and composite objects differently, hence making the code very complex especially when the data structure is constructed dynamically. That is one of the main reasons why the Composite design pattern is used — to define an abstract class (interface works as well) that represents both leaf and composite objects uniformly hence letting clients treat every element of the tree structure in the same manner.

Doesn’t it sound familiar? “In Flutter, everything is a widget!”, “Flutter widget tree”, no? The Flutter framework builds the UI of the application as a Widget tree, allows you to put widgets inside other widgets or their containers (widgets, which contain the children property, e.g. Column, Row, ListView, etc.). That’s pretty much a Composite design pattern, well, on steroids and with some additional Flutter magic…


Analysis

Structure of the Composite design pattern (source)
  • Component — declares the interface for objects in the composition. This interface allows the client to treat leaf and composite objects uniformly.
  • Leaf — represents leaf objects in the composition. This object does not have sub-elements (child components), defines behaviour for primitive objects in the composition and does most of the real work since they don’t have anyone to delegate the work to.
  • Composite — stores sub-elements (children) and implements child-related operations in the Composite interface. Differently from the leaf component, composite object delegates the work to its child elements, processes intermediate results ant then returns the final result to the client.
  • Client — uses the Component interface to interact with objects in the composite structure. This allows the client to work with simple and complex elements of the tree in the same way.

Applicability


Implementation

Besides, we want to show the size of each file or directory. It is easy to show it for a concrete file, but the directory size depends on the items inside it and should be calculated. To implement this, the Composite design pattern is a great option!

Class diagram

Class Diagram — Implementation of the Composite design pattern

IFile defines a common interface for both File (leaf) and Directory (composite) classes:

  • getSize() — returns size of the file;
  • render() — renders the component’s UI.

File class implements the getSize() and render() methods, additionally contains title, size and icon properties. Directory implements the same required methods, but it also contains title, isInitiallyExpanded and files list, containing the IFile objects, defines addFile() method, which allows adding IFile objects to the directory (files list). AudioFile, ImageFile, TextFile and VideoFile classes extend the File class to specify a concrete type of the file.

IFile

File

Concrete classes extending File

Directory

FileSizeConverter

Example

CompositeExample widget contains the buildMediaDirectory() method which builds the file structure for the example. This method illustrates the Composite design pattern — even though the components are of a different type, they could be handled in the same manner since the implemented interface of IFile is the same for all components. This allows us to put Directory objects inside other directories, mix them along with concrete File objects hence building the tree structure of IFile components.

The final result of the Composite design pattern’s implementation looks like this:

As you can see in the example, the file size is shown for each file directly and for directories, it is calculated by adding up each file size inside the directory.

All of the code changes for the Composite design pattern and its example implementation could be found here.



Your contribution

Flutter Community

Articles and Stories from the Flutter Community

Mangirdas Kazlauskas

Written by

Software Engineer | Flutter Enthusiast https://www.linkedin.com/in/mangirdas-kazlauskas/

Flutter Community

Articles and Stories from the Flutter Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade