I am trying to create a page where I display a pyramid of buttons, representing pins. This will allow the user to tap each button to show which pins in bowling they knocked down. I am having trouble figuring out how to format it so the buttons look like a set of pins.
This is what I have so far. Its almost correct but the third row still has issues. However, even though this is close. It won't work for me. When you click a button, it turns invisible and therefore disappears. Because I used spaceEvenly and SpaceBetween to space the buttons, when one disappears, they space differently. Here is my code for the widget
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Enter Pins'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
_new_button(6),_new_button(7),_new_button(8),_new_button(9)
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
_new_button(3),_new_button(4),_new_button(5)
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
_new_button(1),_new_button(2)
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
_new_button(0)
]),
],
),
),
floatingActionButton:
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
Align(
alignment: Alignment.bottomLeft,
child: FloatingActionButton.extended(
onPressed: () => _submit_turn(context),
heroTag: 'btn1',
label: const Text('Submit'),
tooltip: 'Submit Pins',
),
),
Align(
alignment: Alignment.bottomLeft,
child: FloatingActionButton(
onPressed: () => Navigator.pop(context),
heroTag: 'btn2',
tooltip: 'Cancel Game',
child: const Icon(Icons.delete, color: Colors.red),
),
)
])
// This trailing comma makes auto-formatting nicer for build methods.
);
}
and for the method _new_button()
_new_button(int index){
return Visibility(
visible: _enable[index],
child: FloatingActionButton (
onPressed: () => _edit_pin(index),
heroTag: 'pin' index.toString(),
tooltip: index.toString(),
child: Icon(Icons.circle, color: _pins_colors[index]),
)
);
}
Is there anyway to fix this and perhaps do it in a better way?
Thanks in advance for the help!
CodePudding user response:
for such irregular layouts the best way is to use CustomMultiChildLayout
with PyramidDelegate
layout delegate - if you want your "pins" to be more invisible remove border
(and/or color
) parameters from BoxDecoration
class Pyramid extends StatefulWidget {
const Pyramid({Key? key}) : super(key: key);
@override
State<Pyramid> createState() => _PyramidState();
}
class _PyramidState extends State<Pyramid> {
// 10 = 4 3 2 1
final colors = List.generate(10, (i) => HSVColor.fromAHSV(1, 100 i * 25, 1, 0.5).toColor());
final visible = List.filled(10, true);
@override
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: PyramidDelegate(4),
children: [
for(var i = 0; i < 10; i )
LayoutId(
id: i,
child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: visible[i]? colors[i] : Colors.black.withOpacity(0.05),
border: Border.all(color: colors[i].withOpacity(0.25), width: visible[i]? 0 : 2),
),
child: GestureDetector(
onTap: () {
print('$i pressed');
setState(() => visible[i] = !visible[i]);
},
),
)
)
],
);
}
}
class PyramidDelegate extends MultiChildLayoutDelegate {
final int sideLength;
PyramidDelegate(this.sideLength);
@override
void performLayout(ui.Size size) {
final constraints = BoxConstraints.tight(const Size.square(64));
var childId = 0;
final rect = (Offset.zero & size).deflate(32);
final a = rect.topLeft;
final b = rect.topRight;
final c = Offset(rect.center.dx, rect.top rect.width * 0.5 * sqrt(3));
for (var row = sideLength; row > 0; row--) {
final t = 1 - (row - 1) / (sideLength - 1);
final a1 = Offset.lerp(a, c, t);
final b1 = Offset.lerp(b, c, t);
for (var column = row; column > 0; column--) {
final childSize = layoutChild(childId, constraints);
final offset = row == 1? c : Offset.lerp(a1, b1, 1 - (column - 1) / (row - 1))!;
positionChild(childId, offset - childSize.center(Offset.zero));
childId ;
}
}
}
@override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => false;
}
CodePudding user response:
You can use Opacity
widget to handle visibility in this case. For click event and tooltip, you can also include condition.
_new_button(int index) {
return Opacity(
opacity: _enable[index] ? 1 : 0,
child: FloatingActionButton(
onPressed: !_enable[index]
? null
: () {
setState(() {
_enable[index] = false;
});
},
//........