I use DataTable
in Flutter.
DataCell has long Text and cell's height is not adjusted.
DataTable is positioned in SingleScrollView.
So I do many things..
I used Wrap
outside Text, give softWrap: true
to Text,
And I also gave minHeight: double.infinity
to DataTable..etc
But nothing resolves my issue.
DataCell(
Align(
alignment: Alignment.centerLeft,
child: rowData.secret
? Text(
"비밀 글",
style: TextStyle(
color: Colors.grey.shade300,
),
)
: rowData.todayDiary.length > 20
? Text(
description,
textAlign:
TextAlign.start,
style: const TextStyle(
fontSize: Sizes.size13,
),
)
: Text(
rowData.todayDiary,
textAlign:
TextAlign.start,
style: const TextStyle(
fontSize: Sizes.size13,
),
),
),
onTap: () {
setState(() {
expandUpdate = true;
if (isExpanded) {
expandMap.remove(index);
} else {
expandMap[index] = true;
}
});
},
),
Only when I use Wrap widget outside of Text it shows all Text unless hiding it.
However height itself of DataCell is not expanded at all.
Since I use SingleScrollView, I can't calculate height manually by MediaQuery
Please help me..
my full data table code is below
Center(
child: SizedBox(
width: size.width - 400,
child: LayoutBuilder(
builder: (context, constraints) {
final totalWidth = constraints.maxWidth - 360;
final dateWidth = totalWidth * 0.2;
final diaryWidth = totalWidth * 0.5;
final moodWidth = totalWidth * 0.2;
final secretWidth = totalWidth * 0.1;
return DataTable(
columns: [
DataColumn(
label: SizedBox(
width: dateWidth,
child: const Text(
"날짜",
textAlign: TextAlign.start,
),
),
),
DataColumn(
label: SizedBox(
width: diaryWidth,
child: const Text(
"일기",
textAlign: TextAlign.start,
),
),
),
DataColumn(
label: SizedBox(
width: moodWidth,
child: const Text(
"감정",
textAlign: TextAlign.start,
),
),
),
DataColumn(
label: SizedBox(
width: secretWidth,
child: const Text(
"비밀",
textAlign: TextAlign.center,
),
),
),
],
rows: List.generate(
diaryModelList.length,
(index) {
final rowData = diaryModelList[index];
final isExpanded =
expandMap.containsKey(index) &&
expandMap[index] == true;
final description = isExpanded
? rowData.todayDiary
: rowData.todayDiary.length > 21
? rowData.todayDiary.substring(0, 21)
: rowData.todayDiary;
return DataRow(
cells: [
DataCell(
Align(
alignment: Alignment.centerLeft,
child: Text(
convertTimettampToString(
rowData.timestamp,
),
style: const TextStyle(
fontSize: Sizes.size13,
),
),
),
),
// DataCell(
// Align(
// alignment: Alignment.centerLeft,
// child: Text(
// rowData.todayDiary,
// style: const TextStyle(
// fontSize: Sizes.size13,
// ),
// softWrap: true,
// ),
// ),
// ),
DataCell(
Align(
alignment: Alignment.centerLeft,
child: rowData.secret
? Text(
"비밀 글",
style: TextStyle(
color: Colors.grey.shade300,
),
)
: rowData.todayDiary.length > 20
? Text(
description,
textAlign:
TextAlign.start,
style: const TextStyle(
fontSize: Sizes.size13,
),
)
: Text(
rowData.todayDiary,
textAlign:
TextAlign.start,
style: const TextStyle(
fontSize: Sizes.size13,
),
),
),
onTap: () {
setState(() {
expandUpdate = true;
if (isExpanded) {
expandMap.remove(index);
} else {
expandMap[index] = true;
}
});
},
),
DataCell(
Align(
alignment: Alignment.centerLeft,
child: Text(
rowData.todayMood.description!,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: Sizes.size13,
),
),
),
),
DataCell(
Align(
alignment: Alignment.center,
child: rowData.secret
? const Icon(Icons.check)
: null,
),
),
],
);
},
),
);
},
),
),
),
I'm not too familiar with DataTable
, but from looking at its source code, there doesn't seem to be a way to have dynamic table row heights. Here is an excerpt from the DataTable._buildDataCell
source (see comments):
label = Container(
padding: padding,
/// Since this Container is constrained to [effectiveDataRowMaxHeight], it cannot
/// be dynamically sized.
constraints: BoxConstraints(
minHeight: effectiveDataRowMinHeight,
maxHeight: effectiveDataRowMaxHeight
),
alignment: numeric ? Alignment.centerRight : AlignmentDirectional.centerStart,
child: DefaultTextStyle(
style: effectiveDataTextStyle.copyWith(
color: placeholder ? effectiveDataTextStyle.color!.withOpacity(0.6) : null,
),
child: DropdownButtonHideUnderline(child: label),
),
);
Here is the output I was able to achieve (code below):
You might have to create your own widget to handle your scenario. I will provide you with a starting point example below.
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
/// Your model data.
final elements = getElements();
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
/// This is the list of elements. We 'shrinkWrap' it to allow the
/// [ListView] to take up the minimum amount of space.
child: ListView.builder(
shrinkWrap: true,
itemCount: elements.length,
itemBuilder: (context, index) {
final element = elements[index];
return Column(
mainAxisSize: MainAxisSize.min,
children: [
if (index == 0) const Divider(),
Row(
children: [
/// You can calculate or set a maximum width using
/// [ConstrainedBox]; I use [Expanded] for simplicity.
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Text(element.title),
),
),
const SizedBox(width: 24.0),
/// Flex 3 to 1 ratio for the description.
Expanded(
flex: 3,
child: Expandable(
header: const Align(
alignment: Alignment.centerLeft,
child: Text("Head"),
),
body: Text(element.description),
),
),
],
),
const Divider(),
],
);
},
),
),
);
}
}
Expandable:
class Expandable extends StatefulWidget {
const Expandable({
super.key,
this.duration = DurationConstants.medium,
this.curve = Curves.fastEaseInToSlowEaseOut,
this.initiallyExpanded = true,
this.backgroundColor,
this.margin = EdgeInsets.zero,
this.padding = EdgeInsets.zero,
this.header,
required this.body,
});
final Duration duration;
final Curve curve;
final Color? backgroundColor;
final EdgeInsets margin;
final EdgeInsets padding;
final bool initiallyExpanded;
final Widget? header;
final Widget body;
@override
State<Expandable> createState() => ExpandableState();
}
class ExpandableState extends State<Expandable>
with SingleTickerProviderStateMixin {
late bool _isExpanded;
late final AnimationController _animationController;
late final CurvedAnimation _curvedAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.duration,
);
_curvedAnimation = CurvedAnimation(
parent: _animationController,
curve: widget.curve,
reverseCurve: widget.curve.flipped,
);
if (_isExpanded = widget.initiallyExpanded) {
_animationController.value = 1.0;
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
margin: widget.margin,
padding: widget.padding,
decoration: BoxDecoration(
color: widget.backgroundColor,
borderRadius: const BorderRadius.all(Radius.circular(6.0)),
),
clipBehavior: Clip.antiAlias,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// Header.
if (widget.header != null)
GestureDetector(
onTap: toggle,
behavior: HitTestBehavior.opaque,
child: SizedBox(
height: 56.0,
child: Stack(
children: [
Positioned(
top: .0,
bottom: .0,
left: .0,
right: .0,
child: widget.header!,
),
Positioned(
top: .0,
bottom: .0,
right: .0,
child: AnimatedBuilder(
animation: _curvedAnimation,
builder: (_, child) => Transform.rotate(
angle: _curvedAnimation.value * math.pi,
child: child,
),
child: Icon(
Icons.keyboard_arrow_down,
color: theme.colorScheme.primary,
),
),
),
],
),
),
),
/// Body.
SizeTransition(
sizeFactor: _curvedAnimation,
child: AnimatedSize(
duration: DurationConstants.short,
child: widget.body,
),
),
],
),
);
}
Future<void> expand() async {
if (_isExpanded) {
return;
}
setState(() => _isExpanded = true);
return _animationController.forward();
}
Future<void> collapse() async {
if (_isExpanded) {
setState(() => _isExpanded = false);
return _animationController.reverse();
}
}
Future<void> toggle() {
if (_isExpanded) {
return collapse();
} else {
return expand();
}
}
}
Possibly helpful widgets: