我正在使用 Flutter 构建一个聊天应用程序。
在我的消息气泡中,我会显示消息文本、日期和一个图标,以显示消息是否已被阅读。消息文本显示不佳。它会在一两个词之后转到下一行,而不是填充气泡的整个宽度。
Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(top: 8.0, bottom: 8.0, left: 80.0, right: 10),
decoration: BoxDecoration(
color: primaryColor.withOpacity(.1),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd().add_jm()
.format(documentSnapshot.data["time"].toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 5),
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
)
],
),
答案 0 :(得分:2)
目前,您使用 Row
小部件将消息文本显示为第一个子项,然后将日期和阅读图标显示为第二个子项:
相反,您应该使用 Column
小部件。
和朱利安来回走后
class ChatEntry {
final String text;
final DateTime date;
final bool read;
final bool sent;
ChatEntry({
this.text,
this.date,
this.read,
this.sent,
});
}
class Bubble extends StatelessWidget {
final ChatEntry entry;
const Bubble({Key key, this.entry}) : super(key: key);
@override
Widget build(BuildContext context) {
return Align(
alignment: entry.sent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
padding: kBubblePadding,
decoration: BoxDecoration(
color: (entry.sent ? kSentColor : kReceivedColor)
.withOpacity(entry.read ? kReadOpacity : 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kBorderRadius),
topRight: Radius.circular(kBorderRadius),
bottomRight: Radius.circular(entry.sent ? 0.0 : kBorderRadius),
bottomLeft: Radius.circular(entry.sent ? kBorderRadius : 0.0),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
entry.sent ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(entry.text, style: kBubbleTextStyle),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
DateFormat('MMMd – kk:mm').format(entry.date),
style: TextStyle(fontSize: kBubbleMetaFontSize),
),
if (entry.read) ...[
const SizedBox(width: 5),
Icon(Icons.done, size: kBubbleMetaFontSize)
]
],
),
],
),
),
);
}
}
class Conversation extends StatelessWidget {
final List<ChatEntry> entries;
const Conversation({Key key, this.entries}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: entries
.map(
(entry) => Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(entry: entry),
),
)
.toList(),
);
}
}
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Chat Demo',
home: Scaffold(
body: SingleChildScrollView(
child: Conversation(entries: getChatEntries()),
),
),
),
);
}
连同随机数据生成。
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:faker/faker.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Chat Demo',
home: Scaffold(
body: SingleChildScrollView(
child: Conversation(entries: getChatEntries()),
),
),
),
);
}
class Conversation extends StatelessWidget {
final List<ChatEntry> entries;
const Conversation({Key key, this.entries}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: entries
.map(
(entry) => Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(entry: entry),
),
)
.toList(),
);
}
}
class Bubble extends StatelessWidget {
final ChatEntry entry;
const Bubble({Key key, this.entry}) : super(key: key);
@override
Widget build(BuildContext context) {
return Align(
alignment: entry.sent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
padding: kBubblePadding,
decoration: BoxDecoration(
color: (entry.sent ? kSentColor : kReceivedColor)
.withOpacity(entry.read ? kReadOpacity : 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kBorderRadius),
topRight: Radius.circular(kBorderRadius),
bottomRight: Radius.circular(entry.sent ? 0.0 : kBorderRadius),
bottomLeft: Radius.circular(entry.sent ? kBorderRadius : 0.0),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
entry.sent ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(entry.text, style: kBubbleTextStyle),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
DateFormat('MMMd – kk:mm').format(entry.date),
style: TextStyle(fontSize: kBubbleMetaFontSize),
),
if (entry.read) ...[
const SizedBox(width: 5),
Icon(Icons.done, size: kBubbleMetaFontSize)
]
],
),
],
),
),
);
}
}
// DOMAIN
class ChatEntry {
final String text;
final DateTime date;
final bool read;
final bool sent;
ChatEntry({
this.text,
this.date,
this.read,
this.sent,
});
}
// CONFIG
const kSentColor = Color(0xff03bd85);
const kReceivedColor = Color(0xff0251d6);
const kReadOpacity = .3;
const kBorderRadius = 15.0;
const kBubblePadding = const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10.0,
);
const kBubbleTextStyle = const TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
);
const kBubbleMetaFontSize = 11.0;
// RANDOM DATA
final Random random = Random.secure();
final faker = new Faker();
List<ChatEntry> getChatEntries() {
final nbMessages = random.nextInt(17) + 3;
final lastRead = random.nextInt(nbMessages);
DateTime date = DateTime.now();
return List.generate(
nbMessages,
(index) {
date = date.subtract(Duration(minutes: random.nextInt(30)));
return ChatEntry(
text: faker.lorem
.words(2 + random.nextInt(random.nextBool() ? 3 : 15))
.join(' '),
date: date,
read: index >= lastRead,
sent: random.nextBool(),
);
},
).reversed.toList();
}