flutter_sanity_portable_text 1.0.0-beta.11 flutter_sanity_portable_text: ^1.0.0-beta.11 copied to clipboard
Flutter renderer for the Sanity.io Portable Text format. It includes support for the standard blocks, spans, annotations, including the ability to provide custom blocks, annotations and styles.
Flutter Widget for rendering Sanity's Portable Text #
Renders the Portable Text format in Flutter.
Features #
- ✅ Renders all standard styles and marks
- ✅ Renders multiple blocks of text
- ✅ Includes supports for custom blocks
- ✅ Supports custom styles and marks including gestures
Usage #
The below samples show the various ways of using the PortableText
widget.
With a simple TextBlockItem #
import 'package:flutter/material.dart';
import 'package:flutter_sanity_portable_text/flutter_sanity_portable_text.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,
),
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: PortableText(
blocks: [
TextBlockItem(
children: [
Span(
text: 'Sanity Portable Text',
),
],
style: 'h1',
),
],
),
),
),
),
);
}
}
With multiple blocks and different styles #
import 'package:flutter/material.dart';
import 'package:flutter_sanity_portable_text/flutter_sanity_portable_text.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,
),
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: PortableText(
blocks: [
TextBlockItem(
children: [
Span(
text: 'Sanity Portable Text',
),
],
style: 'h1',
),
// Let's try a blockquote now
TextBlockItem(
children: [
Span(
text:
'"The best way to predict the future is to invent it."',
),
Span(
text: '\n- Steve Jobs',
),
],
style: 'blockquote',
),
TextBlockItem(
children: [
Span(
text:
'Supports all standard marks and styles, including support for:',
),
],
),
_listItem('Bulleted text', ListItemType.bullet),
_listItem('Numbered text', ListItemType.number),
_listItem('Square bullet text', ListItemType.square),
TextBlockItem(
children: [
Span(text: 'Strong text, ', marks: ['strong']),
Span(text: 'Emphasized text, ', marks: ['em']),
Span(text: 'Underlined text, ', marks: ['underline']),
Span(
text: 'Strike through text, ',
marks: ['strike-through']),
Span(
text: 'All combined',
marks: ['strong', 'em', 'underline', 'strike-through']),
Span(text: '.'),
],
),
_textBlock('And not to forget...the unsung'),
for (final index in [1, 2, 3, 4, 5, 6])
_textBlock('H$index', style: 'h$index'),
],
),
),
),
),
);
}
TextBlockItem _textBlock(String text, {String? style}) {
return TextBlockItem(
children: [
Span(text: text),
],
style: style ?? 'normal',
);
}
TextBlockItem _listItem(String text, ListItemType type) {
return TextBlockItem(
children: [
Span(text: text),
],
listItem: type,
);
}
}
With a custom block #
import 'package:flutter/material.dart';
import 'package:flutter_sanity_portable_text/flutter_sanity_portable_text.dart';
// A custom block item that will be registered for rendering
final class CustomBlockItem implements PortableBlockItem {
CustomBlockItem({
required this.text,
required this.foregroundColor,
required this.backgroundColor,
});
@override
String get blockType => 'custom';
final String text;
final Color foregroundColor;
final Color backgroundColor;
}
void main() {
// Registering a custom block
PortableTextConfig.shared.blocks['custom'] = (context, item) {
final theme = Theme.of(context);
final custom = item as CustomBlockItem;
final style =
theme.textTheme.bodyMedium?.apply(color: custom.foregroundColor);
return Container(
decoration: BoxDecoration(
color: custom.backgroundColor,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blueAccent, width: 2),
boxShadow: const [
BoxShadow(
offset: Offset(0, 4),
blurRadius: 2,
color: Colors.black26,
)
]),
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(8),
child: Text(custom.text, style: style),
);
};
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,
),
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: PortableText(
blocks: [
TextBlockItem(
children: [
Span(
text: 'Sanity Portable Text',
),
],
style: 'h1',
),
CustomBlockItem(
text: 'We can also do custom blocks!',
foregroundColor: Colors.white,
backgroundColor: Colors.primaries[4]),
],
),
),
),
),
);
}
}
Using an unregistered block shows an error #
import 'package:flutter/material.dart';
import 'package:flutter_sanity_portable_text/flutter_sanity_portable_text.dart';
// A custom block item that will not be registered for rendering
final class UnregisteredBlockItem implements PortableBlockItem {
@override
String get blockType => 'unregistered';
}
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,
),
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: PortableText(
blocks: [
TextBlockItem(
children: [
Span(
text: 'Sanity Portable Text',
),
],
style: 'h1',
),
// this will show an error on the PortableWidget
UnregisteredBlockItem(),
],
),
),
),
),
);
}
}
Exploring further #
There are several other features which have been excluded from the examples
above. You can look at the properties of PortableConfig
for more customization
opportunities, including changing the base styles.