buildWidget method
Compose Flutter widget with child widgets already built
Subclasses should override this method. This method provides a general description of the layout of this math node. The child nodes are built in prior. This method is only responsible for the placement of those child widgets accroding to the layout & other interactions.
Please ensure children works in the same order as updateChildren, computeChildOptions, and buildWidget.
Implementation
@override
BuildResult buildWidget(
MathOptions options, List<BuildResult?> childBuildResults) {
// Checking of character box is done automatically by the passing of
// BuildResult, so we don't need to check it here.
final baseResult = childBuildResults[0]!;
final skew = isShifty ? baseResult.skew : 0.0;
Widget accentWidget;
if (!isStretchy) {
Widget accentSymbolWidget;
// Following comment are selected from KaTeX:
//
// Before version 0.9, \vec used the combining font glyph U+20D7.
// But browsers, especially Safari, are not consistent in how they
// render combining characters when not preceded by a character.
// So now we use an SVG.
// If Safari reforms, we should consider reverting to the glyph.
if (label == '\u2192') {
// We need non-null baseline. Because ShiftBaseline cannot deal with a
// baseline distance of null due to Flutter rendering pipeline design.
accentSymbolWidget = staticSvg('vec', options, needBaseline: true);
} else {
final accentRenderConfig = accentRenderConfigs[label];
if (accentRenderConfig == null || accentRenderConfig.overChar == null) {
accentSymbolWidget = Container();
} else {
accentSymbolWidget = makeBaseSymbol(
symbol: accentRenderConfig.overChar!,
variantForm: false,
atomType: AtomType.ord,
mode: Mode.text,
options: options,
).widget;
}
}
// Non stretchy accent can not contribute to overall width, thus they must
// fit exactly with the width even if it means overflow.
accentWidget = LayoutBuilder(
builder: (context, constraints) => ResetDimension(
depth: 0.0, // Cut off xHeight
width: constraints.minWidth, // Ensure width
child: ShiftBaseline(
// \tilde is submerged below baseline in KaTeX fonts
relativePos: 1.0,
// Shift baseline up by xHeight
offset: -options.fontMetrics.xHeight.cssEm.toLpUnder(options),
child: accentSymbolWidget,
),
),
);
} else {
// Strechy accent
accentWidget = LayoutBuilder(
builder: (context, constraints) {
// \overline needs a special case, as KaTeX does.
if (label == '\u00AF') {
final defaultRuleThickness = options
.fontMetrics.defaultRuleThickness.cssEm
.toLpUnder(options);
return Padding(
padding: EdgeInsets.only(bottom: 3 * defaultRuleThickness),
child: Container(
width: constraints.minWidth,
height: defaultRuleThickness, // TODO minRuleThickness
color: options.color,
),
);
} else {
final accentRenderConfig = accentRenderConfigs[label];
if (accentRenderConfig == null ||
accentRenderConfig.overImageName == null) {
return Container();
}
var svgWidget = strechySvgSpan(
accentRenderConfig.overImageName!,
constraints.minWidth,
options,
);
// \horizBrace also needs a special case, as KaTeX does.
if (label == '\u23de') {
return Padding(
padding: EdgeInsets.only(bottom: 0.1.cssEm.toLpUnder(options)),
child: svgWidget,
);
} else {
return svgWidget;
}
}
},
);
}
return BuildResult(
options: options,
italic: baseResult.italic,
skew: baseResult.skew,
widget: VList(
baselineReferenceWidgetIndex: 1,
children: <Widget>[
VListElement(
customCrossSize: (width) =>
BoxConstraints(minWidth: width - 2 * skew),
hShift: skew,
child: accentWidget,
),
// Set min height
MinDimension(
minHeight: options.fontMetrics.xHeight.cssEm.toLpUnder(options),
topPadding: 0,
child: baseResult.widget,
),
],
),
);
}