1. SeparatedRow
As name suggests, this widget separates children with a separator widget. It is useful when you need to separate children with a divider or any other widget.
The implementation:
import 'dart:math';
import 'package:flutter/material.dart';
class SeparatedRow extends StatelessWidget {
const SeparatedRow({
required this.children,
required this.separator,
this.mainAxisAlignment = MainAxisAlignment.start,
this.crossAxisAlignment = CrossAxisAlignment.center,
this.mainAxisSize = MainAxisSize.min,
super.key,
});
final List<Widget> children;
final Widget separator;
final MainAxisAlignment mainAxisAlignment;
final CrossAxisAlignment crossAxisAlignment;
final MainAxisSize mainAxisSize;
@override
Widget build(BuildContext context) {
final itemCount = max(0, children.length * 2 - 1);
return Row(
mainAxisSize: mainAxisSize,
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
children: [for (int i = 0; i < itemCount; i += 1) i].map((index) {
final itemIndex = index ~/ 2;
return index.isEven ? children[itemIndex] : separator;
}).toList(growable: false),
);
}
}
2. SeparatedColumn
You guessed it right, this widget is similar to SeparatedRow
but for columns.
The implementation:
import 'dart:math';
import 'package:flutter/material.dart';
class SeparatedColumn extends StatelessWidget {
const SeparatedColumn({
required this.children,
required this.separator,
this.mainAxisAlignment = MainAxisAlignment.start,
this.crossAxisAlignment = CrossAxisAlignment.center,
this.mainAxisSize = MainAxisSize.min,
super.key,
});
final List<Widget> children;
final Widget separator;
final MainAxisAlignment mainAxisAlignment;
final CrossAxisAlignment crossAxisAlignment;
final MainAxisSize mainAxisSize;
@override
Widget build(BuildContext context) {
final itemCount = max(0, children.length * 2 - 1);
return Column(
mainAxisSize: mainAxisSize,
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
children: [for (int i = 0; i < itemCount; i += 1) i].map((index) {
final itemIndex = index ~/ 2;
return index.isEven ? children[itemIndex] : separator;
}).toList(growable: false),
);
}
}
3. TableGrid
This widget is useful when you need to create a grid with a specific number of columns but without specific aspect ratio.
The implementation:
import 'package:flutter/material.dart';
class TableGrid extends StatelessWidget {
const TableGrid({
required this.children,
this.crossAxisCount = 3,
this.mainAxisSpacing = 4,
this.crossAxisSpacing = 4,
this.mainAxisAlignment = MainAxisAlignment.start,
super.key,
});
final List<Widget> children;
final int crossAxisCount;
final double mainAxisSpacing;
final double crossAxisSpacing;
final MainAxisAlignment mainAxisAlignment;
@override
Widget build(BuildContext context) {
var rowIndex = -1;
var itemIndex = -1;
final rowCount = (children.length / crossAxisCount).ceil();
int columnCount() {
final hasIncompleteRow = children.length % crossAxisCount != 0;
final isLastRow = rowIndex == (children.length / crossAxisCount).floor();
if (hasIncompleteRow && isLastRow) {
return children.length % crossAxisCount;
}
return crossAxisCount;
}
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.filled(rowCount, 0).map((_) {
++rowIndex;
final semanticRowList = Iterable<int>.generate(columnCount() * 2 - 1);
return Padding(
padding: EdgeInsets.only(top: rowIndex > 0 ? crossAxisSpacing : 0),
child: Row(
mainAxisAlignment: mainAxisAlignment,
children: semanticRowList.map<Widget>((semanticRowIndex) {
return semanticRowIndex.isOdd
? SizedBox(width: mainAxisSpacing)
: children[++itemIndex];
}).toList(),
),
);
}).toList(growable: false),
);
}
}
4. AnotherStateManagementWidget
Just kidding, we already have enough of those. 😄
5. Clickable
GestureDetector is a great widget for handling user interactions, but it doesn’t provide much much to the user.
For example it doesn’t chage the cursor on Web, or does’t provide splash effect on mobile.
So here is the solution, Clickable
widget.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Clickable extends StatelessWidget {
const Clickable({
required this.child,
required this.onTap,
this.margin = EdgeInsets.zero,
this.padding = EdgeInsets.zero,
this.borderRadius,
this.decoration,
super.key,
});
final EdgeInsetsGeometry margin;
final EdgeInsetsGeometry padding;
final Widget child;
final BorderRadius? borderRadius;
final VoidCallback? onTap;
final BoxDecoration? decoration;
@override
Widget build(BuildContext context) {
return ExcludeFocus(
child: MaterialButton(
padding: EdgeInsets.zero,
onPressed: onTap != null
? () {
HapticFeedback.selectionClick();
onTap!.call();
}
: null,
splashColor: Colors.white.withOpacity(.05),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: borderRadius != null ? RoundedRectangleBorder(borderRadius: borderRadius!) : null,
minWidth: 0,
child: child,
),
);
}
}