右对齐消息时间戳与 flutter 消息文本字段的尾随右侧
Right aligning message timestamp with trailing right-hand-side of flutter message text field
正如我在之前的 post 主题 () I'm a bit of a perfectionist. Unfortunately my flutter layout-fu isn't as strong as my ambition. I'm creating a messaging app, and I'm working on adding a timestamp to the message box. My code so far (thanks also to this answer: ) 中提到的那样:
创建一行的代码:
Widget getAppUserMessageRow() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: AppState.i.chatItemUserLeftInset),
Flexible(
fit: FlexFit.loose,
child: message.cm.messageType.getMessageWidget(message),
),
],
);
}
创建消息框本身的代码:
Widget build(BuildContext context) {
bool isFromAppUser = message.cm.isFromAppUser(AppState.i.activeUserId);
return Container(
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerticalInset),
child:
Container(
decoration: BoxDecoration(
color: isFromAppUser ? AppState.i.chatItemUserMessageBackgroundColour : Colors.white,
//.withOpacity(!message.isFromAppUser ? 0.1 : 0.8),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : 0),
topRight: Radius.circular(isFromAppUser ? 0 : AppState.i.chatItemMessageBorderRadius),
bottomRight: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageCurvedBorderRadius : AppState.i.chatItemMessageBorderRadius),
bottomLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : AppState.i.chatItemMessageCurvedBorderRadius),
),
boxShadow: [
BoxShadow(
color: AppState.i.chatItemMessageBoxShadowColour,
spreadRadius: AppState.i.chatItemMessageBoxShadowSpreadRadius,
blurRadius: AppState.i.chatItemMessageBoxShadowBlurRadius,
offset: AppState.i.chatItemMessageBoxShadowOffset, // changes position of shadow
),
],
),
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerInset,
horizontal: AppState.i.chatItemMessageHorInset),
child: Stack(
children: [
Text(
// WTF? This calculates enough space for the message receipt time to wrap
message.cm.messageText + (isFromAppUser ? ' \u202F' : ' \u202F'),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTextFontSize,
color:
isFromAppUser ? AppState.i.chatItemMessageUserTextFontColour : AppState.i.chatItemMessageOtherUserTextFontColour,
),
textWidthBasis: TextWidthBasis.longestLine,
),
// This Positioned item creates the message timestamp in the area gained by adding the spaces above, assuming the spaces forced
// The text field into adding an extra line.
Positioned(
width: 60,
height: 15,
right: 0,
bottom: -2,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
DateFormat.Hm().format(message.cm.messageDisplayTime(AppState.i.activeUserId)),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTimeFontSize,
color: isFromAppUser ? AppState.i.chatItemMessageUserTimeFontColour : AppState.i.chatItemMessageOtherUserTimeFontColour,
),
textAlign: TextAlign.right,
),
!isFromAppUser ? Container() :
Padding(
padding: EdgeInsets.only(left: AppState.i.chatItemMessageReceiptLeftInset),
child: message.cm.getReadReceiptIcon()
)
],
),
),
],
),
)
);
}
这里有一些好的和坏的例子 - 查看坏例子的右侧填充。时间戳应该与上面的文字更右对齐:
还有更多:
添加 spaces + 一个不可打印的字符来为日期时间戳 + 已读回执组合腾出空间是一个天才的把戏,但也有缺点。当文本接近将被时间戳占用的 space 时,它会推出右对齐。所以在某些情况下,根据上述情况,盒子会消耗不必要的 space.
那么,是否可以解决这个问题,或者是否可以使用不同的布局来实现我正在寻找的效果?我不能使用太过计算的东西,因为现在滚动长列表时性能相当不错。
为了使这个更具体一些,一些 'fixed' 个例子。
我想你可以这样试试:
- Calculate/Simulate 文本并检查消息是否与时间戳重叠。
- 如果重叠,添加
\n
来换行。否则,return原来的
你需要以下东西:
LayoutBuilder
: 文本控件上方的 BoxConstraint
TextPainter
:模拟布局(还需要TextStyle
、textWidthBasis
否则可能会影响布局结果)
代码如下:
// assume 'message' is a String need to be displayed
...
LayoutBuilder(builder: (context, constraints) {
final reserve = ' \u202F';
final originalPainter = TextPainter(
text: TextSpan(text: text, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final reservePainter = TextPainter(
text: TextSpan(text: text + reserve, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final changeLine = reservePainter.width > originalPainter.width + 0.001 || reservePainter.height > originalPainter.height + 0.001;
return Text(
changeLine ? text+'\n' : text,
...
)
}
正如我在之前的 post 主题 (
创建一行的代码:
Widget getAppUserMessageRow() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: AppState.i.chatItemUserLeftInset),
Flexible(
fit: FlexFit.loose,
child: message.cm.messageType.getMessageWidget(message),
),
],
);
}
创建消息框本身的代码:
Widget build(BuildContext context) {
bool isFromAppUser = message.cm.isFromAppUser(AppState.i.activeUserId);
return Container(
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerticalInset),
child:
Container(
decoration: BoxDecoration(
color: isFromAppUser ? AppState.i.chatItemUserMessageBackgroundColour : Colors.white,
//.withOpacity(!message.isFromAppUser ? 0.1 : 0.8),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : 0),
topRight: Radius.circular(isFromAppUser ? 0 : AppState.i.chatItemMessageBorderRadius),
bottomRight: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageCurvedBorderRadius : AppState.i.chatItemMessageBorderRadius),
bottomLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : AppState.i.chatItemMessageCurvedBorderRadius),
),
boxShadow: [
BoxShadow(
color: AppState.i.chatItemMessageBoxShadowColour,
spreadRadius: AppState.i.chatItemMessageBoxShadowSpreadRadius,
blurRadius: AppState.i.chatItemMessageBoxShadowBlurRadius,
offset: AppState.i.chatItemMessageBoxShadowOffset, // changes position of shadow
),
],
),
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerInset,
horizontal: AppState.i.chatItemMessageHorInset),
child: Stack(
children: [
Text(
// WTF? This calculates enough space for the message receipt time to wrap
message.cm.messageText + (isFromAppUser ? ' \u202F' : ' \u202F'),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTextFontSize,
color:
isFromAppUser ? AppState.i.chatItemMessageUserTextFontColour : AppState.i.chatItemMessageOtherUserTextFontColour,
),
textWidthBasis: TextWidthBasis.longestLine,
),
// This Positioned item creates the message timestamp in the area gained by adding the spaces above, assuming the spaces forced
// The text field into adding an extra line.
Positioned(
width: 60,
height: 15,
right: 0,
bottom: -2,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
DateFormat.Hm().format(message.cm.messageDisplayTime(AppState.i.activeUserId)),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTimeFontSize,
color: isFromAppUser ? AppState.i.chatItemMessageUserTimeFontColour : AppState.i.chatItemMessageOtherUserTimeFontColour,
),
textAlign: TextAlign.right,
),
!isFromAppUser ? Container() :
Padding(
padding: EdgeInsets.only(left: AppState.i.chatItemMessageReceiptLeftInset),
child: message.cm.getReadReceiptIcon()
)
],
),
),
],
),
)
);
}
这里有一些好的和坏的例子 - 查看坏例子的右侧填充。时间戳应该与上面的文字更右对齐:
还有更多:
添加 spaces + 一个不可打印的字符来为日期时间戳 + 已读回执组合腾出空间是一个天才的把戏,但也有缺点。当文本接近将被时间戳占用的 space 时,它会推出右对齐。所以在某些情况下,根据上述情况,盒子会消耗不必要的 space.
那么,是否可以解决这个问题,或者是否可以使用不同的布局来实现我正在寻找的效果?我不能使用太过计算的东西,因为现在滚动长列表时性能相当不错。
为了使这个更具体一些,一些 'fixed' 个例子。
我想你可以这样试试:
- Calculate/Simulate 文本并检查消息是否与时间戳重叠。
- 如果重叠,添加
\n
来换行。否则,return原来的
你需要以下东西:
LayoutBuilder
: 文本控件上方的 BoxConstraintTextPainter
:模拟布局(还需要TextStyle
、textWidthBasis
否则可能会影响布局结果)
代码如下:
// assume 'message' is a String need to be displayed
...
LayoutBuilder(builder: (context, constraints) {
final reserve = ' \u202F';
final originalPainter = TextPainter(
text: TextSpan(text: text, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final reservePainter = TextPainter(
text: TextSpan(text: text + reserve, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final changeLine = reservePainter.width > originalPainter.width + 0.001 || reservePainter.height > originalPainter.height + 0.001;
return Text(
changeLine ? text+'\n' : text,
...
)
}