mirror of
https://github.com/Iconica-Development/flutter_timetable.git
synced 2025-05-18 19:43:43 +02:00
feat: collapse blocks with same id
This commit is contained in:
parent
5a54309a5d
commit
e31f284c22
2 changed files with 218 additions and 12 deletions
|
@ -13,6 +13,7 @@ class TimetableDemo extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TimetableDemoState extends State<TimetableDemo> {
|
class _TimetableDemoState extends State<TimetableDemo> {
|
||||||
|
bool _grouped = false;
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
final List<TimeBlock> blocks = [
|
final List<TimeBlock> blocks = [
|
||||||
TimeBlock(
|
TimeBlock(
|
||||||
|
@ -35,6 +36,72 @@ class _TimetableDemoState extends State<TimetableDemo> {
|
||||||
end: TimeOfDay(hour: 7, minute: 0),
|
end: TimeOfDay(hour: 7, minute: 0),
|
||||||
child: Container(color: Colors.blue, height: 300, width: 200),
|
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
|
@override
|
||||||
|
@ -48,13 +115,47 @@ class _TimetableDemoState extends State<TimetableDemo> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||||
child: Timetable(
|
child: SingleChildScrollView(
|
||||||
startHour: 3,
|
controller: _scrollController,
|
||||||
endHour: 22,
|
child: Column(
|
||||||
timeBlocks: blocks,
|
children: [
|
||||||
scrollController: _scrollController,
|
// toggle between grouped and ungrouped blocks
|
||||||
tablePaddingStart: 0,
|
Row(
|
||||||
mergeBlocks: true,
|
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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -81,6 +81,7 @@ class _TimetableState extends State<Timetable> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var blocks = _collapseBlocks(widget.timeBlocks);
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
physics: widget.scrollPhysics ?? const BouncingScrollPhysics(),
|
physics: widget.scrollPhysics ?? const BouncingScrollPhysics(),
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
|
@ -106,7 +107,7 @@ class _TimetableState extends State<Timetable> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (!widget.mergeBlocks && !widget.collapseBlocks) ...[
|
if (!widget.mergeBlocks && !widget.collapseBlocks) ...[
|
||||||
for (var block in widget.timeBlocks) ...[
|
for (var block in blocks) ...[
|
||||||
Block(
|
Block(
|
||||||
start: block.start,
|
start: block.start,
|
||||||
end: block.end,
|
end: block.end,
|
||||||
|
@ -118,11 +119,11 @@ class _TimetableState extends State<Timetable> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
] else if (widget.mergeBlocks) ...[
|
] else if (widget.mergeBlocks) ...[
|
||||||
for (var blocks
|
for (var mergedBlocks
|
||||||
in _mergeBlocksInColumns(widget.timeBlocks)) ...[
|
in _mergeBlocksInColumns(blocks)) ...[
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
for (var block in blocks) ...[
|
for (var block in mergedBlocks) ...[
|
||||||
Block(
|
Block(
|
||||||
start: block.start,
|
start: block.start,
|
||||||
end: block.end,
|
end: block.end,
|
||||||
|
@ -135,7 +136,25 @@ class _TimetableState extends State<Timetable> {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
],
|
||||||
|
] 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(
|
SizedBox(
|
||||||
width: widget.tablePaddingEnd,
|
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) {
|
List<List<TimeBlock>> _mergeBlocksInColumns(List<TimeBlock> blocks) {
|
||||||
var mergedBlocks = <List<TimeBlock>>[];
|
var mergedBlocks = <List<TimeBlock>>[];
|
||||||
// try to put blocks in the same column if the time doesn´t collide
|
// try to put blocks in the same column if the time doesn´t collide
|
||||||
|
|
Loading…
Reference in a new issue