feat: collapse blocks with same id

This commit is contained in:
Freek van de Ven 2022-08-24 16:02:07 +02:00
parent 5a54309a5d
commit e31f284c22
2 changed files with 218 additions and 12 deletions

View file

@ -13,6 +13,7 @@ class TimetableDemo extends StatefulWidget {
}
class _TimetableDemoState extends State<TimetableDemo> {
bool _grouped = false;
final ScrollController _scrollController = ScrollController();
final List<TimeBlock> blocks = [
TimeBlock(
@ -35,6 +36,72 @@ class _TimetableDemoState extends State<TimetableDemo> {
end: TimeOfDay(hour: 7, minute: 0),
child: Container(color: Colors.blue, height: 300, width: 200),
),
TimeBlock(
start: TimeOfDay(hour: 18, minute: 0),
end: TimeOfDay(hour: 18, minute: 15),
child: Text('High Tea'),
id: 10,
),
TimeBlock(
start: TimeOfDay(hour: 18, minute: 0),
end: TimeOfDay(hour: 18, minute: 15),
child: Text('High Tea'),
id: 10,
),
TimeBlock(
start: TimeOfDay(hour: 18, minute: 0),
end: TimeOfDay(hour: 18, minute: 15),
child: Text('High Tea'),
id: 10,
),
];
final List<TimeBlock> groupedBlocks = [
TimeBlock(
start: TimeOfDay(hour: 14, minute: 0),
end: TimeOfDay(hour: 15, minute: 0),
id: 0,
),
TimeBlock(
start: TimeOfDay(hour: 8, minute: 0),
end: TimeOfDay(hour: 9, minute: 0),
id: 1,
),
TimeBlock(
start: TimeOfDay(hour: 9, minute: 15),
end: TimeOfDay(hour: 10, minute: 0),
id: 1,
),
TimeBlock(
start: TimeOfDay(hour: 10, minute: 15),
end: TimeOfDay(hour: 11, minute: 0),
child: Container(color: Colors.purple, height: 300, width: 50),
id: 2,
),
TimeBlock(
start: TimeOfDay(hour: 6, minute: 15),
end: TimeOfDay(hour: 7, minute: 0),
child: Container(color: Colors.blue, height: 300, width: 200),
id: 2,
),
TimeBlock(
start: TimeOfDay(hour: 18, minute: 0),
end: TimeOfDay(hour: 18, minute: 15),
child: Text('High Tea'),
id: 10,
),
TimeBlock(
start: TimeOfDay(hour: 18, minute: 0),
end: TimeOfDay(hour: 18, minute: 15),
child: Text('High Tea'),
id: 10,
),
TimeBlock(
start: TimeOfDay(hour: 18, minute: 0),
end: TimeOfDay(hour: 18, minute: 15),
child: Text('High Tea'),
id: 10,
),
];
@override
@ -48,14 +115,48 @@ class _TimetableDemoState extends State<TimetableDemo> {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Timetable(
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
children: [
// toggle between grouped and ungrouped blocks
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Grouped'),
Switch(
value: _grouped,
onChanged: (value) {
setState(() {
_grouped = value;
});
},
),
],
),
if (_grouped) ...[
Timetable(
startHour: 3,
endHour: 22,
timeBlocks: groupedBlocks,
scrollController: _scrollController,
tablePaddingStart: 0,
collapseBlocks: true,
)
] else ...[
Timetable(
startHour: 3,
endHour: 22,
timeBlocks: blocks,
scrollController: _scrollController,
tablePaddingStart: 0,
collapseBlocks: true,
mergeBlocks: true,
),
],
],
),
),
),
);
}

View file

@ -81,6 +81,7 @@ class _TimetableState extends State<Timetable> {
@override
Widget build(BuildContext context) {
var blocks = _collapseBlocks(widget.timeBlocks);
return SingleChildScrollView(
physics: widget.scrollPhysics ?? const BouncingScrollPhysics(),
controller: _scrollController,
@ -106,22 +107,6 @@ class _TimetableState extends State<Timetable> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!widget.mergeBlocks && !widget.collapseBlocks) ...[
for (var block in widget.timeBlocks) ...[
Block(
start: block.start,
end: block.end,
startHour: widget.startHour,
hourHeight: widget.hourHeight,
blockWidth: widget.blockWidth,
blockColor: widget.blockColor,
child: block.child,
),
],
] else if (widget.mergeBlocks) ...[
for (var blocks
in _mergeBlocksInColumns(widget.timeBlocks)) ...[
Stack(
children: [
for (var block in blocks) ...[
Block(
start: block.start,
@ -133,9 +118,43 @@ class _TimetableState extends State<Timetable> {
child: block.child,
),
],
] else if (widget.mergeBlocks) ...[
for (var mergedBlocks
in _mergeBlocksInColumns(blocks)) ...[
Stack(
children: [
for (var block in mergedBlocks) ...[
Block(
start: block.start,
end: block.end,
startHour: widget.startHour,
hourHeight: widget.hourHeight,
blockWidth: widget.blockWidth,
blockColor: widget.blockColor,
child: block.child,
),
],
],
),
]
],
] else if (widget.collapseBlocks) ...[
for (var groupedBlocks in _groupBlocksById(blocks)) ...[
Stack(
children: [
for (var block in groupedBlocks) ...[
Block(
start: block.start,
end: block.end,
startHour: widget.startHour,
hourHeight: widget.hourHeight,
blockWidth: widget.blockWidth,
blockColor: widget.blockColor,
child: block.child,
),
],
],
),
],
],
SizedBox(
width: widget.tablePaddingEnd,
@ -152,6 +171,92 @@ class _TimetableState extends State<Timetable> {
);
}
/// Copmbine blocks that have the same id and the same time.
List<TimeBlock> _collapseBlocks(List<TimeBlock> blocks) {
var newBlocks = <TimeBlock>[];
var groupedBlocks = <List<TimeBlock>>[];
// order blocks by id and collides with another block
for (var block in blocks) {
// check if the block is already in one of the grouped blocks
var found = false;
for (var groupedBlock in groupedBlocks) {
if (groupedBlock.first.id == block.id &&
groupedBlock.first.start == block.start &&
groupedBlock.first.end == block.end) {
groupedBlock.add(block);
found = true;
break;
}
}
if (!found) {
if (blocks
.where(
(b) =>
b != block &&
b.id == block.id &&
b.start == block.start &&
b.end == block.end,
)
.isNotEmpty) {
groupedBlocks.add([block]);
} else {
newBlocks.add(block);
}
}
}
// 8.10 8.40 8.55
//
for (var block in groupedBlocks) {
// combine the blocks into one block
// calculate the endtime of the combined block
var startMinute = block.first.start.minute + block.first.start.hour * 60;
var endMinute = block.first.end.minute + block.first.end.hour * 60;
var durationMinute = (endMinute - startMinute) * block.length;
var endTime = TimeOfDay(
hour: (startMinute + durationMinute) ~/ 60,
minute: (startMinute + durationMinute) % 60,
);
var newBlock = TimeBlock(
start: block.first.start,
end: endTime,
child: Column(
children: [
for (var b in block) ...[b.child ?? Container()],
],
),
);
newBlocks.add(newBlock);
}
return newBlocks;
}
List<List<TimeBlock>> _groupBlocksById(List<TimeBlock> blocks) {
var groupedBlocks = <List<TimeBlock>>[];
var defaultGroup = <TimeBlock>[];
for (var block in blocks) {
var found = false;
if (block.id == 0) {
defaultGroup.add(block);
} else {
for (var groupedBlock in groupedBlocks) {
if (groupedBlock.first.id == block.id) {
groupedBlock.add(block);
found = true;
break;
}
}
if (!found) {
groupedBlocks.add([block]);
}
}
}
for (var block in defaultGroup) {
groupedBlocks.add([block]);
}
return groupedBlocks;
}
List<List<TimeBlock>> _mergeBlocksInColumns(List<TimeBlock> blocks) {
var mergedBlocks = <List<TimeBlock>>[];
// try to put blocks in the same column if the time doesn´t collide