From e31f284c229b2f048d5343d852b3dc4ca045c239 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Wed, 24 Aug 2022 16:02:07 +0200 Subject: [PATCH] feat: collapse blocks with same id --- example/lib/main.dart | 115 ++++++++++++++++++++++++++++++++++++++--- lib/src/timetable.dart | 115 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 218 insertions(+), 12 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 3848196..34cdb4c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -13,6 +13,7 @@ class TimetableDemo extends StatefulWidget { } class _TimetableDemoState extends State { + bool _grouped = false; final ScrollController _scrollController = ScrollController(); final List blocks = [ TimeBlock( @@ -35,6 +36,72 @@ class _TimetableDemoState extends State { 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 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,13 +115,47 @@ class _TimetableDemoState extends State { return Scaffold( body: Padding( padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: Timetable( - startHour: 3, - endHour: 22, - timeBlocks: blocks, - scrollController: _scrollController, - tablePaddingStart: 0, - mergeBlocks: true, + 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, + ), + ], + ], + ), ), ), ); diff --git a/lib/src/timetable.dart b/lib/src/timetable.dart index f66c56c..ce4f535 100644 --- a/lib/src/timetable.dart +++ b/lib/src/timetable.dart @@ -81,6 +81,7 @@ class _TimetableState extends State { @override Widget build(BuildContext context) { + var blocks = _collapseBlocks(widget.timeBlocks); return SingleChildScrollView( physics: widget.scrollPhysics ?? const BouncingScrollPhysics(), controller: _scrollController, @@ -106,7 +107,7 @@ class _TimetableState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!widget.mergeBlocks && !widget.collapseBlocks) ...[ - for (var block in widget.timeBlocks) ...[ + for (var block in blocks) ...[ Block( start: block.start, end: block.end, @@ -118,11 +119,11 @@ class _TimetableState extends State { ), ], ] else if (widget.mergeBlocks) ...[ - for (var blocks - in _mergeBlocksInColumns(widget.timeBlocks)) ...[ + for (var mergedBlocks + in _mergeBlocksInColumns(blocks)) ...[ Stack( children: [ - for (var block in blocks) ...[ + for (var block in mergedBlocks) ...[ Block( start: block.start, end: block.end, @@ -135,7 +136,25 @@ class _TimetableState extends State { ], ], ), - ] + ], + ] 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 { ); } + /// Copmbine blocks that have the same id and the same time. + List _collapseBlocks(List blocks) { + var newBlocks = []; + var groupedBlocks = >[]; + // 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> _groupBlocksById(List blocks) { + var groupedBlocks = >[]; + var defaultGroup = []; + 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> _mergeBlocksInColumns(List blocks) { var mergedBlocks = >[]; // try to put blocks in the same column if the time doesn´t collide