Flutter (69): Introduction to custom component methods

Time: Column:Mobile & Frontend views:275

Customizing Components in Flutter

When the existing components provided by Flutter cannot meet our needs, or when we need to encapsulate some common components for code sharing, we need to create custom components. In Flutter, there are three ways to customize components: through composition of other components, custom painting, and implementing RenderObject. This section will introduce the characteristics of each method, with more details provided in subsequent chapters.

69.1 Composing Multiple Widgets

This method involves assembling multiple components to create a new one. For example, the Container widget we introduced earlier is a composite component made up of DecoratedBox, ConstrainedBox, Transform, Padding, Align, and other components.

In Flutter, the concept of composition is very important. Flutter provides many foundational components, and our UI development essentially consists of combining these components as needed to achieve various layouts.

69.2 Custom Painting with CustomPaint

If we encounter a situation where existing components cannot achieve the required UI, we can use custom painting. For instance, if we need a circular progress bar with a color gradient, we can't use the CircularProgressIndicator because it does not support applying a gradient to the progress bar when displaying precise progress (its valueColor property only changes the indicator's color during the rotation animation). In this case, the best approach is to create a custom component to draw the desired appearance using CustomPaint and Canvas provided by Flutter.

69.3 Custom Painting with RenderObject

Components in Flutter that have a UI appearance, such as Text and Image, are rendered by corresponding RenderObject classes (which we will discuss in detail in the "Core Principles of Flutter" chapter). For example, Text is rendered by RenderParagraph, while Image is rendered by RenderImage. RenderObject is an abstract class that defines an abstract method called paint(...):

void paint(PaintingContext context, Offset offset)

PaintingContext represents the drawing context of the component, and you can obtain a Canvas through PaintingContext.canvas. The drawing logic is primarily implemented using the Canvas API. Subclasses need to override this method to implement their own drawing logic, such as how RenderParagraph implements text drawing and RenderImage implements image drawing.

It's important to note that ultimately, RenderObject also uses the Canvas API for drawing. So what is the difference between implementing RenderObject and custom painting with CustomPaint and Canvas? The answer is simple: CustomPaint is a convenience proxy class for developers. It directly inherits from SingleChildRenderObjectWidget and connects the Canvas and Painter (which developers need to implement, as discussed in later chapters) through the paint method of RenderCustomPaint to achieve the final drawing.

69.4 Summary

“Composition” is the simplest method for customizing components, and in any scenario requiring a custom component, we should first consider whether it can be achieved through composition. The methods of custom painting using CustomPaint and RenderObject are fundamentally the same, as both require developers to manually call the Canvas API to draw the UI. The advantages are powerful flexibility, allowing for the implementation of any UI appearance; the downside is that developers must understand the details of the Canvas API and implement the drawing logic themselves. In the following sections of this chapter, we will provide detailed examples of custom UI methods.