mirror of
https://github.com/Tencent/Hippy.git
synced 2026-10-27 12:17:15 +08:00
680 lines
19 KiB
Dart
680 lines
19 KiB
Dart
//
|
|
// Tencent is pleased to support the open source community by making
|
|
// Hippy available.
|
|
//
|
|
// Copyright (C) 2022 THL A29 Limited, a Tencent company.
|
|
// All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:voltron_renderer/style.dart';
|
|
|
|
import '../common.dart';
|
|
import '../controller.dart';
|
|
import '../util.dart';
|
|
|
|
const int kDefaultTextShadowColor = 0x55000000;
|
|
const String kEllipsis = "\u2026";
|
|
const int kMaxLineCount = 100000;
|
|
|
|
final TextMethodProvider _sTextMethodProvider = TextMethodProvider();
|
|
|
|
class TextVirtualNode extends VirtualNode {
|
|
static const _kTag = "TextVirtualNode";
|
|
|
|
TextVirtualNode(rootId, id, pid, index, renderContext)
|
|
: super(rootId, id, pid, index, renderContext) {
|
|
if (parent != null) {
|
|
_tapGestureRecognizer = TapGestureRecognizer();
|
|
_tapGestureRecognizer?.onTapDown = (detail) {
|
|
_longClickTimer = Timer(const Duration(milliseconds: 500), () {
|
|
nativeGestureDispatcher.handleLongClick();
|
|
});
|
|
nativeGestureDispatcher.handlePressIn();
|
|
};
|
|
_tapGestureRecognizer?.onTapUp = (e) {
|
|
var longClickTimer = _longClickTimer;
|
|
if (longClickTimer != null && longClickTimer.isActive) {
|
|
nativeGestureDispatcher.handleClick();
|
|
longClickTimer.cancel();
|
|
}
|
|
nativeGestureDispatcher.handlePressOut();
|
|
};
|
|
}
|
|
}
|
|
|
|
// 文本转换后的span,组合各种css样式
|
|
TextSpan? span;
|
|
double lastLayoutWidth = 0.0;
|
|
|
|
// 原文本
|
|
String? _sourceText;
|
|
|
|
// 文本基础属性
|
|
int? _numberOfLines;
|
|
double _fontSize = NodeProps.kDefaultFontSizeSp;
|
|
double? _lineHeight;
|
|
double? _letterSpacing;
|
|
int _color = Colors.black.value;
|
|
String? _fontFamily;
|
|
TextAlign _textAlign = TextAlign.start;
|
|
FontStyle _fontStyle = FontStyle.normal;
|
|
FontWeight? _fontWeight;
|
|
TextOverflow _textOverflow = TextOverflow.ellipsis;
|
|
double customTextScale = 1.0;
|
|
|
|
// 文本阴影属性
|
|
double _textShadowOffsetDx = 0;
|
|
double _textShadowOffsetDy = 0;
|
|
double _textShadowRadius = 1;
|
|
int _textShadowColor = kDefaultTextShadowColor;
|
|
|
|
// 下划线
|
|
bool _isUnderlineTextDecorationSet = false;
|
|
|
|
// 删除线
|
|
bool _isLineThroughTextDecorationSet = false;
|
|
|
|
// 下划线样式
|
|
TextDecorationStyle _textDecorationStyle = TextDecorationStyle.solid;
|
|
|
|
// 下划线颜色
|
|
int _textDecorationColor = Colors.black.value;
|
|
|
|
// 系统文本缩放尺寸
|
|
bool _enableScale = false;
|
|
|
|
bool get enableScale => _enableScale;
|
|
|
|
List<PlaceholderDimensions> _placeholderDimensions = [];
|
|
|
|
String get _text {
|
|
return _sourceText ?? '';
|
|
}
|
|
|
|
TapGestureRecognizer? _tapGestureRecognizer;
|
|
|
|
Timer? _longClickTimer;
|
|
|
|
static int _parseArgument(String weight) {
|
|
var doubleWeight = double.tryParse(weight);
|
|
if (doubleWeight != null) {
|
|
return doubleWeight.toInt();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kFontStyle)
|
|
// ignore: use_setters_to_change_properties
|
|
void fontStyle(String fontStyleString) {
|
|
var fontStyle = parseFontStyle(fontStyleString);
|
|
if (fontStyle != _fontStyle) {
|
|
_fontStyle = fontStyle;
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
static FontStyle parseFontStyle(String fontStyleStr) {
|
|
var fontStyle = FontStyle.normal;
|
|
if ("italic" == fontStyleStr) {
|
|
fontStyle = FontStyle.italic;
|
|
} else if ("normal" == fontStyleStr) {
|
|
fontStyle = FontStyle.normal;
|
|
}
|
|
|
|
return fontStyle;
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kLetterSpacing)
|
|
// ignore: use_setters_to_change_properties
|
|
void letterSpacing(double letterSpace) {
|
|
if (letterSpace != -1.0) {
|
|
_letterSpacing = letterSpace;
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kColor)
|
|
// ignore: use_setters_to_change_properties
|
|
void color(int color) {
|
|
_color = color;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kFontSize)
|
|
// ignore: use_setters_to_change_properties
|
|
void fontSize(double fontSize) {
|
|
_fontSize = fontSize;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kFontFamily)
|
|
// ignore: use_setters_to_change_properties
|
|
void fontFamily(String fontFamily) {
|
|
_fontFamily = fontFamily;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kFontWeight)
|
|
void fontWeight(String weight) {
|
|
var fontWeight = parseFontWeight(weight);
|
|
|
|
if (fontWeight != _fontWeight) {
|
|
_fontWeight = fontWeight;
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
static FontWeight parseFontWeight(String weight) {
|
|
var fontWeight = FontWeight.normal;
|
|
if ("bold" == weight) {
|
|
fontWeight = FontWeight.bold;
|
|
} else if ("normal" == weight) {
|
|
fontWeight = FontWeight.normal;
|
|
} else {
|
|
var fontWeightNumeric = _parseArgument(weight);
|
|
if (fontWeightNumeric != -1) {
|
|
if (fontWeightNumeric <= 100) {
|
|
fontWeight = FontWeight.w100;
|
|
} else if (fontWeightNumeric <= 200) {
|
|
fontWeight = FontWeight.w200;
|
|
} else if (fontWeightNumeric <= 300) {
|
|
fontWeight = FontWeight.w300;
|
|
} else if (fontWeightNumeric <= 400) {
|
|
fontWeight = FontWeight.w400;
|
|
} else if (fontWeightNumeric <= 500) {
|
|
fontWeight = FontWeight.w500;
|
|
} else if (fontWeightNumeric <= 600) {
|
|
fontWeight = FontWeight.w600;
|
|
} else if (fontWeightNumeric <= 700) {
|
|
fontWeight = FontWeight.w700;
|
|
} else if (fontWeightNumeric <= 800) {
|
|
fontWeight = FontWeight.w800;
|
|
} else {
|
|
fontWeight = FontWeight.w900;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fontWeight;
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kTextDecorationLine)
|
|
void textDecorationLine(String textDecorationLineString) {
|
|
_isUnderlineTextDecorationSet = false;
|
|
_isLineThroughTextDecorationSet = false;
|
|
for (var textDecorationLineSubString
|
|
in textDecorationLineString.split(" ")) {
|
|
if ("underline" == textDecorationLineSubString) {
|
|
_isUnderlineTextDecorationSet = true;
|
|
} else if ("line-through" == textDecorationLineSubString) {
|
|
_isLineThroughTextDecorationSet = true;
|
|
}
|
|
}
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kTextDecorationStyle)
|
|
void textDecorationStyle(String textDecorationStyleString) {
|
|
final propertyMap = {
|
|
'solid': TextDecorationStyle.solid,
|
|
'dashed': TextDecorationStyle.dashed,
|
|
'dotted': TextDecorationStyle.dotted,
|
|
'double': TextDecorationStyle.double,
|
|
'wavy': TextDecorationStyle.wavy,
|
|
};
|
|
|
|
_textDecorationStyle =
|
|
propertyMap[textDecorationStyleString] ?? TextDecorationStyle.solid;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kTextDecorationColor)
|
|
void textDecorationColor(int textDecorationColor) {
|
|
_textDecorationColor = textDecorationColor;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kPropShadowOffset)
|
|
void textShadowOffset(VoltronMap offsetMap) {
|
|
_textShadowOffsetDx =
|
|
offsetMap.get<double>(NodeProps.kPropShadowOffsetWidth) ??
|
|
offsetMap.get<int>(NodeProps.kPropShadowOffsetWidth)?.toDouble() ??
|
|
0.0;
|
|
_textShadowOffsetDy =
|
|
offsetMap.get<double>(NodeProps.kPropShadowOffsetHeight) ??
|
|
offsetMap.get<int>(NodeProps.kPropShadowOffsetHeight)?.toDouble() ??
|
|
0.0;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kPropShadowRadius)
|
|
void textShadowRadius(double textShadowRadius) {
|
|
if (textShadowRadius != _textShadowRadius) {
|
|
_textShadowRadius = textShadowRadius;
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kPropShadowColor)
|
|
void setTextShadowColor(int textShadowColor) {
|
|
if (textShadowColor != _textShadowColor) {
|
|
_textShadowColor = textShadowColor;
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kLineHeight)
|
|
void lineHeight(int lineHeight) {
|
|
_lineHeight = (lineHeight == -1.0 ? null : lineHeight)?.toDouble();
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kTextAlign)
|
|
void setTextAlign(String textAlign) {
|
|
_textAlign = parseTextAlign(textAlign);
|
|
markDirty();
|
|
}
|
|
|
|
static TextAlign parseTextAlign(String textAlign) {
|
|
var align = TextAlign.start;
|
|
if ("auto" == (textAlign)) {
|
|
align = TextAlign.start;
|
|
} else if ("left" == (textAlign)) {
|
|
align = TextAlign.left;
|
|
} else if ("right" == (textAlign)) {
|
|
align = TextAlign.right;
|
|
} else if ("center" == (textAlign)) {
|
|
align = TextAlign.center;
|
|
} else if ("justify" == (textAlign)) {
|
|
align = TextAlign.justify;
|
|
} else {
|
|
LogUtils.e(_kTag, "Invalid textAlign: $textAlign");
|
|
}
|
|
return align;
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kText)
|
|
// ignore: use_setters_to_change_properties
|
|
void text(String text) {
|
|
_sourceText = text;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kEllipsizeMode)
|
|
void setEllipsizeMode(String ellipsizeMode) {
|
|
var textOverflow = _textOverflow;
|
|
if (ellipsizeMode == 'tail') {
|
|
textOverflow = TextOverflow.ellipsis;
|
|
} else if (ellipsizeMode == 'clip') {
|
|
textOverflow = TextOverflow.clip;
|
|
}
|
|
if (textOverflow != _textOverflow) {
|
|
_textOverflow = textOverflow;
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kPropEnableScale)
|
|
set enableScale(bool flag) {
|
|
_enableScale = flag;
|
|
markDirty();
|
|
}
|
|
|
|
@ControllerProps(NodeProps.kNumberOfLines)
|
|
void setNumberOfLines(int numberOfLines) {
|
|
_numberOfLines = numberOfLines <= 0 ? 0 : numberOfLines;
|
|
markDirty();
|
|
}
|
|
|
|
TextSpan createSpan({bool useChild = true}) {
|
|
var curFontSize = _fontSize;
|
|
|
|
var shadowList = <Shadow>[];
|
|
if (_textShadowOffsetDx != 0 || _textShadowOffsetDy != 0) {
|
|
shadowList.add(
|
|
Shadow(
|
|
blurRadius: _textShadowRadius,
|
|
color: Color(_textShadowColor),
|
|
offset: Offset(_textShadowOffsetDx, _textShadowOffsetDy),
|
|
),
|
|
);
|
|
}
|
|
|
|
var childrenSpan = <InlineSpan>[];
|
|
if (useChild) {
|
|
_placeholderDimensions = [];
|
|
for (var i = 0; i < childCount; i++) {
|
|
var node = getChildAt(i);
|
|
if (node != null) {
|
|
if (node is TextVirtualNode) {
|
|
var styleNode = node;
|
|
childrenSpan.add(styleNode.createSpan(useChild: useChild));
|
|
} else if (node is ImageVirtualNode) {
|
|
_placeholderDimensions.add(
|
|
PlaceholderDimensions(
|
|
size: Size(node.mWidth, node.mHeight),
|
|
alignment: node.verticalAlignment,
|
|
),
|
|
);
|
|
childrenSpan.add(node.createSpan());
|
|
} else {
|
|
// throw StateError("${node.name} is not support in Text");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var textDecoration = TextDecoration.none;
|
|
if (_isUnderlineTextDecorationSet) {
|
|
textDecoration = TextDecoration.underline;
|
|
}
|
|
if (_isLineThroughTextDecorationSet) {
|
|
if (textDecoration != TextDecoration.none) {
|
|
textDecoration = TextDecoration.combine(
|
|
[
|
|
TextDecoration.underline,
|
|
TextDecoration.lineThrough,
|
|
],
|
|
);
|
|
} else {
|
|
textDecoration = TextDecoration.lineThrough;
|
|
}
|
|
}
|
|
|
|
if (!isEmpty(_text) || childrenSpan.isNotEmpty) {
|
|
return TextSpan(
|
|
text: _text,
|
|
style: TextStyle(
|
|
leadingDistribution: TextLeadingDistribution.even,
|
|
color: Color(_color),
|
|
letterSpacing: _letterSpacing,
|
|
fontSize: curFontSize,
|
|
fontStyle: _fontStyle,
|
|
fontWeight: _fontWeight,
|
|
fontFamily: _fontFamily,
|
|
shadows: shadowList,
|
|
height: _lineHeightFactor(),
|
|
decoration: textDecoration,
|
|
decorationStyle: _textDecorationStyle,
|
|
decorationColor: Color(_textDecorationColor),
|
|
),
|
|
children: childrenSpan,
|
|
recognizer: nativeGestureDispatcher.needListener()
|
|
? _tapGestureRecognizer
|
|
: null,
|
|
);
|
|
}
|
|
return const TextSpan(text: "");
|
|
}
|
|
|
|
TextData createData(double width, FlexMeasureMode widthMode) {
|
|
if (span == null || dirty) {
|
|
span = createSpan(useChild: true);
|
|
dirty = false;
|
|
}
|
|
lastLayoutWidth = width;
|
|
return TextData(
|
|
_numberOfLines ?? kMaxLineCount,
|
|
span!,
|
|
_textAlign,
|
|
_generateTextScale(),
|
|
_textOverflow,
|
|
);
|
|
}
|
|
|
|
TextData createLayout() {
|
|
return createData(lastLayoutWidth, FlexMeasureMode.exactly);
|
|
}
|
|
|
|
TextPainter createPainter(double width, FlexMeasureMode widthMode) {
|
|
var unconstrainedWidth =
|
|
widthMode == FlexMeasureMode.undefined || width < 0;
|
|
var maxWidth = unconstrainedWidth ? double.infinity : width;
|
|
if (span == null || dirty) {
|
|
span = createSpan(useChild: true);
|
|
dirty = false;
|
|
}
|
|
var painter = TextPainter(
|
|
maxLines: _numberOfLines ?? kMaxLineCount,
|
|
text: span,
|
|
textDirection: TextDirection.ltr,
|
|
textAlign: _textAlign,
|
|
ellipsis: kEllipsis,
|
|
textScaleFactor: _generateTextScale(),
|
|
);
|
|
painter.setPlaceholderDimensions(_placeholderDimensions);
|
|
painter.layout(maxWidth: maxWidth);
|
|
return painter;
|
|
}
|
|
|
|
double? _lineHeightFactor() {
|
|
var lineHeight = _lineHeight;
|
|
if (lineHeight == null) {
|
|
return lineHeight;
|
|
}
|
|
return lineHeight / _fontSize;
|
|
}
|
|
|
|
double _generateTextScale() {
|
|
var textScaleFactor = 1.0;
|
|
if (_enableScale) {
|
|
textScaleFactor = customTextScale;
|
|
}
|
|
|
|
return textScaleFactor;
|
|
}
|
|
|
|
@override
|
|
MethodPropProvider get provider => _sTextMethodProvider;
|
|
|
|
@override
|
|
void onDelete() {
|
|
_longClickTimer?.cancel();
|
|
_tapGestureRecognizer?.dispose();
|
|
}
|
|
}
|
|
|
|
class TextMethodProvider extends StyleMethodPropProvider {
|
|
TextMethodProvider() {
|
|
pushMethodProp(
|
|
NodeProps.kFontStyle,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.fontStyle(value);
|
|
}
|
|
}, "normal"),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kLetterSpacing,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is double) {
|
|
consumer.letterSpacing(value);
|
|
}
|
|
}, -1.0),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kColor,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is int) {
|
|
consumer.color(value);
|
|
}
|
|
}, Colors.transparent.value),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kFontSize,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is double) {
|
|
consumer.fontSize(value);
|
|
}
|
|
}, NodeProps.kDefaultFontSizeSp),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kNumberOfLines,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is int) {
|
|
consumer.setNumberOfLines(value);
|
|
}
|
|
}, kMaxLineCount),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kFontFamily,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.fontFamily(value);
|
|
}
|
|
}, ""),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kFontWeight,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.fontWeight(value);
|
|
}
|
|
}, ""),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kTextDecorationLine,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.textDecorationLine(value);
|
|
}
|
|
}, ""),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kTextDecorationStyle,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.textDecorationStyle(value);
|
|
}
|
|
}, ""),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kTextDecorationColor,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is int) {
|
|
consumer.textDecorationColor(value);
|
|
}
|
|
}, Colors.black.value),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kPropShadowOffset,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is VoltronMap) {
|
|
consumer.textShadowOffset(value);
|
|
}
|
|
}, null),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kPropShadowRadius,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is double) {
|
|
consumer.textShadowRadius(value);
|
|
}
|
|
}, 0),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kPropShadowColor,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is int) {
|
|
consumer.setTextShadowColor(value);
|
|
}
|
|
}, Colors.transparent.value),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kLineHeight,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is int) {
|
|
consumer.lineHeight(value);
|
|
}
|
|
}, -1),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kTextAlign,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.setTextAlign(value);
|
|
}
|
|
}, "left"),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kText,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.text(value);
|
|
}
|
|
}, ""),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kEllipsizeMode,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is String) {
|
|
consumer.setEllipsizeMode(value);
|
|
}
|
|
}, ""),
|
|
);
|
|
pushMethodProp(
|
|
NodeProps.kPropEnableScale,
|
|
StyleMethodProp((consumer, value) {
|
|
if (consumer is TextVirtualNode && value is bool) {
|
|
consumer.enableScale = value;
|
|
}
|
|
}, false),
|
|
);
|
|
}
|
|
}
|
|
|
|
class TextData {
|
|
final int maxLines;
|
|
final InlineSpan text;
|
|
final TextAlign textAlign;
|
|
final String ellipsis = kEllipsis;
|
|
final double textScaleFactor;
|
|
final TextOverflow textOverflow;
|
|
|
|
TextData(
|
|
this.maxLines,
|
|
this.text,
|
|
this.textAlign,
|
|
this.textScaleFactor,
|
|
this.textOverflow,
|
|
);
|
|
|
|
@override
|
|
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
|
bool operator ==(Object other) {
|
|
return other is TextData &&
|
|
maxLines == other.maxLines &&
|
|
text == other.text &&
|
|
textAlign == other.textAlign &&
|
|
ellipsis == other.ellipsis &&
|
|
textScaleFactor == other.textScaleFactor &&
|
|
textOverflow == other.textOverflow;
|
|
}
|
|
|
|
@override
|
|
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
|
int get hashCode =>
|
|
maxLines.hashCode |
|
|
text.hashCode |
|
|
textAlign.hashCode |
|
|
ellipsis.hashCode |
|
|
textScaleFactor.hashCode |
|
|
textOverflow.hashCode;
|
|
}
|