feat(portable_text): per-subtree configuration via PortableTextTheme - #57
Open
Ortes wants to merge 2 commits into
Open
feat(portable_text): per-subtree configuration via PortableTextTheme#57Ortes wants to merge 2 commits into
Ortes wants to merge 2 commits into
Conversation
Add PortableTextConfig.of(context) + copyWith and a PortableTextTheme InheritedWidget (plus PortableTextStyleOverride) so a subtree can override the rendering config without mutating the global shared instance. Render paths resolve the config from context, falling back to shared. Mark deserialization stays on the context-free shared registry.
Creating a fresh PortableTextConfig on every build made PortableTextTheme.updateShouldNotify (identity comparison) always return true, forcing all descendant Portable Text to rebuild on any ancestor rebuild. Cache the derived config and recompute only when the inherited config or the override map changes, keeping it identity-stable. Also document that copyWith(markDefs:) affects rendering only, not parse-time deserializer selection.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for rendering different Portable Text subtrees with different configurations, instead of every
PortableTextwidget sharing the single globalPortableTextConfig.shared. Mirrors Flutter'sThemepattern and is fully backward-compatible.Motivation
PortableTextConfig.sharedis a process-wide singleton, so every Portable Text surface in an app must share one visual configuration. Apps that render Portable Text in multiple contexts (e.g. a full article body vs. compact card descriptions) can't vary styling per surface without mutating the global config.What's added
PortableTextConfig.of(context)— resolves the nearest config from the widget tree, falling back tosharedwhen none is present (likeTheme.of/ThemeData.fallback).PortableTextConfig.copyWith(...)— returns a derived config. Map fields (styles,blocks,blockContainers,markDefs) are merged (provided keys win, the rest are kept); scalar fields are replaced. This intentionally differs fromThemeData.copyWith(which replaces whole fields) because these maps are additive registries.PortableTextTheme— anInheritedWidgetthat supplies aPortableTextConfigto its subtree.PortableTextStyleOverride— an ergonomic helper: pass per-styleTextStyle → TextStyledeltas (e.g.{'normal': (s) => s.copyWith(fontSize: s.fontSize! - 2)}); it wraps the inherited builders internally, so callers express only the delta and there's no recursion.PortableTextConfigso additional configs can be created.The render paths (
PortableTextBlock,defaultListBuilder,defaultBulletRenderer) now resolve their config viaPortableTextConfig.of(context)instead of reading.shareddirectly.Backward compatibility
Non-breaking. With no
PortableTextThemeancestor,of(context)returnsshared, so existing apps render identically.Note on mark deserialization
_markDefsFromJsonruns at JSON-parse time with noBuildContext, so it intentionally keeps reading the context-freeshared.markDefsregistry to choose custom mark deserializers. Mark styling is still resolved per subtree at render time. This is documented inline.