Bringing CSS "float" to Flutter text rendering
When displaying text in your Flutter app, you may notice that Flutter's text rendering capabilities are lacking some features commonly used in web pages, such as CSS float
, which is used to place an image or other element on the left or right side of the text, allowing the text to wrap around it.
For example, let's say we're starting with some rich text, and we want to add a drop-cap for the first letter, and float a couple images in the text.
To accomplish this, we could resort to using a package such as webview_flutter
to display the text in a platform native web view using HTML and CSS, but that comes with its own complications and limitations — and after all, we are writing a Flutter app, not a web app — it would be better if the text and floated items could be rendered with widgets!
FloatColumn to the rescue
We can accomplish our goal with the help of the float_column
package, whose only dependency is Flutter, so it works seamlessly on all Flutter platforms: Android, iOS, Linux, macOS, Web, and Windows.
This is the code we're starting with — basically, just some rich text:
Text.rich(
TextSpan(
children: [
TextSpan(text: '“This is what you shall do...'),
...
],
),
),
First, let's add the drop cap
Import the
float_column
package.Wrap the
Text.rich
in aFloatColumn
widget.Add a
WidgetSpan
andFloatable
for the drop cap widget.The
float: FCFloat.start
causes the childDropCap
widget to float at the start of the text so the text can wrap around it.Remove the '“T' from the TextSpan.
// 1. Import the `float_column` package.
import 'package:float_column/float_column.dart';
// 2. Wrap the `Text.rich` in a `FloatColumn` widget.
FloatColumn(
children: [
Text.rich(
TextSpan(
children: [
// 3. Add a `WidgetSpan` and `Floatable` for the drop cap widget.
WidgetSpan(
child: Floatable(
float: FCFloat.start,
child: DropCap('“T', height: 3),
),
// 4. Remove the '“T' from the TextSpan.
TextSpan(text: 'his is what you shall do...'),
],
),
),
],
)
Which gives us this:
Next, let's add the floating images
This involves adding a WidgetSpan
and Floatable
for each image widget.
For the first image, we're setting float: FCFloat.end
. Because the current text direction is left-to-right (LTR), the image is floated to the right side of the text. If the current text direction was RTL it would float to the left side. The possible FCFloat
values are none
, left
, right
, start
, and end
.
We're also setting clear: FCClear.both
for both images. Like the CSS clear
property, this makes sure the floating widget is placed below floating widgets on both sides. The possible FCClear
values are none
, left
, right
, start
, end
, and both
.
The clearMinSpacing
property provides a way to add or subtract additional spacing in relation to the cleared position. A positive value moves the widget down, and a negative value moves it up.
import 'package:float_column/float_column.dart';
FloatColumn(
children: [
Text.rich(
TextSpan(
children: [
WidgetSpan(
child: Floatable(
float: FCFloat.start,
child: DropCap('“T', height: 3),
),
// Add a `WidgetSpan` and `Floatable` for each floating image:
WidgetSpan(
child: Floatable(
float: FCFloat.end,
clear: FCClear.both,
clearMinSpacing: 16,
maxWidthPercentage: 0.33,
padding: EdgeInsetsDirectional.only(start: 8),
child: Img(name: 'walt.jpg', title: 'Walt Whitman'),
),
),
WidgetSpan(
child: Floatable(
float: FCFloat.start,
clear: FCClear.both,
clearMinSpacing: 60,
maxWidthPercentage: 0.33,
padding: EdgeInsetsDirectional.only(end: 12),
child: Img(name: 'jeremy.jpg', title: 'Photo by...'),
),
),
TextSpan(text: 'his is what you shall do...'),
],
),
),
],
)
Which accomplishes our goal:
Try it out with Flutter Web:
For the complete source code for this example (with some tweaks so it looks good on screens of any size), see:
github.com/ronjb/float_column/example/lib/pages/basic_ltr.dart
And for info about the float_column
package see: