I am trying to create a card with text on the left hand side and a button which is always on the far right.
However, if the button text is too large to fit on one row, I would like it to wrap down to the next row. I have tried my best and managed to made it wrap to the next row but to the start of the next row.
To achieve this, I used a Wrap
and alignment: WrapAlignment.spaceBetween
to align the text and button to the far left and right on the first row. I also used CrossAxisAlignment.stretch
in the Column surrounding the Wrap widget so that it spans the whole row in the card.
The relevant code for the '8:30am', 'Not Started' and 'Start/Mark as Complete' row is shown below. The statusButton
is a OutlinedButton.icon
with no alignment attributes set
Wrap( //Wrap - place in row if space, otherwise wrap
direction: Axis.horizontal,
alignment: WrapAlignment
.spaceBetween,
runSpacing: 8,
children: [
FittedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment
.start, //align both rows to the left
children: [
Row(
children: [
const Icon(
Icons.timer,
size: 24,
color: Color(0xFF4C4C4C),
),
const SizedBox(
width: 8,
),
Text(
task.completeBy,
style: textStyle,
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
const Icon(
Icons.remove_circle_outline,
size: 24,
color: Color(0xFF4C4C4C),
),
const SizedBox(
width: 8,
),
Text(
task.getCompletionStatusText(),
style: textStyle,
),
],
),
],
),
),
statusButton
],
),
However, what I really want is for it to wrap to the end of the next row (Note: I only want it to go to the next row if the button text is too long, so the first 'Start' button picture is still correct). I have tried various alignments and wrap stuff but can't find out how to do this, any help would be much appreciated. Thank you
Sadly it is not possible to align the runs of a Wrap
individually.
You could either create your own a bit more complicated Widget, or use a workaround like the following which conditionally either uses the Button directly, or wraps it with an Align widget (which forces the button on its own run and aligns it to the right).
In my example i use the button Size combined with the current available width for the calculation. But of course if your calculations depend on the text, then you have to adjust that a bit.
I used your code snippet with some modifications like forcing the Wrap
to expand with the SizedBox
and getting the current constraints with the LayoutBuilder
.
For debugging, you can also use the debugPaintSizeEnabled = true;
to see the sizes of the render boxes.
Just run the example and change the button size from 300 to 100, so that it gets wrapped on the first run instead.
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
class YourWrapperWidget extends StatelessWidget {
final double buttonSize;
const YourWrapperWidget({super.key, required this.buttonSize});
@override
Widget build(BuildContext context) {
debugPaintSizeEnabled = true; // for debugging
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
final bool buttonOnNextLine = constraints.maxWidth - buttonSize < 200;
return SizedBox(
width: double.infinity,
child: Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
runSpacing: 8,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.timer,
size: 24,
color: Color(0xFF4C4C4C),
),
const SizedBox(
width: 8,
),
Text("completed by"),
],
),
const SizedBox(
height: 8,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.remove_circle_outline,
size: 24,
color: Color(0xFF4C4C4C),
),
const SizedBox(
width: 8,
),
Text("completed status"),
],
),
],
),
if (!buttonOnNextLine) ElevatedButton(onPressed: () {}, child: SizedBox(width: buttonSize)),
if (buttonOnNextLine)
Align(
alignment: Alignment.centerRight,
child: ElevatedButton(onPressed: () {}, child: SizedBox(width: buttonSize)),
),
],
),
);
});
}
}
void main() => runApp(
MaterialApp(
home: Scaffold(
backgroundColor: Colors.green,
appBar: AppBar(),
body: const Builder(builder: _build),
),
),
);
Widget _build(BuildContext context) {
return YourWrapperWidget(buttonSize: 300);
}
Oh and the 200
of the line final bool buttonOnNextLine = constraints.maxWidth - buttonSize < 200;
should be the width of your left aligned widgets at the start.