diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml deleted file mode 100644 index 3c4b54b..0000000 --- a/bitbucket-pipelines.yml +++ /dev/null @@ -1,44 +0,0 @@ -image: cirrusci/flutter - -pipelines: - pull-requests: - feature/*: - - step: - caches: - - gradle - - gradlewrapper - - flutter - name: Run analyzer & test - script: - - flutter pub get - - flutter format -o none --set-exit-if-changed . - - flutter analyze - - develop: - - step: - caches: - - gradle - - gradlewrapper - - flutter - name: Run analyzer & test - script: - - flutter pub get - - flutter format -o none --set-exit-if-changed . - - flutter analyze - - hotfix/*: - - step: - caches: - - gradle - - gradlewrapper - - flutter - name: Run analyzer & test - script: - - flutter pub get - - flutter format -o none --set-exit-if-changed . - - flutter analyze - -definitions: - caches: - gradlewrapper: ~/.gradle/wrapper - flutter: ~/.pub-cache diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 0530d9c..89acbc8 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -124,7 +124,7 @@ linter: prefer_asserts_with_message: true prefer_collection_literals: true prefer_conditional_assignment: true - prefer_const_constructors: true + prefer_const_constructors: false prefer_const_constructors_in_immutables: true prefer_const_declarations: false prefer_const_literals_to_create_immutables: false diff --git a/example/lib/main.dart b/example/lib/main.dart index bcbc2d4..b2a2737 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,14 +2,59 @@ import 'package:flutter/material.dart'; import 'package:timetable/timetable.dart'; void main() { - runApp(const MaterialApp(home: TimetableDemo())); + runApp(MaterialApp(home: TimetableDemo())); } -class TimetableDemo extends StatelessWidget { +class TimetableDemo extends StatefulWidget { const TimetableDemo({Key? key}) : super(key: key); + @override + State createState() => _TimetableDemoState(); +} + +class _TimetableDemoState extends State { + final ScrollController _scrollController = ScrollController(); + final List blocks = [ + TimeBlock( + start: TimeOfDay(hour: 8, minute: 0), + end: TimeOfDay(hour: 9, minute: 0), + child: null, + ), + TimeBlock( + start: TimeOfDay(hour: 9, minute: 15), + end: TimeOfDay(hour: 10, minute: 0), + child: null, + ), + TimeBlock( + start: TimeOfDay(hour: 10, minute: 15), + end: TimeOfDay(hour: 11, minute: 0), + child: Container(color: Colors.purple, height: 300, width: 50), + ), + TimeBlock( + start: TimeOfDay(hour: 6, minute: 15), + end: TimeOfDay(hour: 7, minute: 0), + child: Container(color: Colors.blue, height: 300, width: 200), + ), + ]; + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return const Scaffold(body: Timetable()); + return Scaffold( + body: Padding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Timetable( + startHour: 3, + endHour: 22, + timeBlocks: blocks, + scrollController: _scrollController, + ), + ), + ); } } diff --git a/lib/src/block.dart b/lib/src/block.dart new file mode 100644 index 0000000..509fa1a --- /dev/null +++ b/lib/src/block.dart @@ -0,0 +1,58 @@ +part of timetable; + +class Block extends StatelessWidget { + const Block({ + required this.start, + required this.end, + required this.startHour, + required this.blockWidth, + this.linePadding = 8, + this.child, + Key? key, + }) : super(key: key); + + /// The start time of the block in 24 hour format + final TimeOfDay start; + + /// The end time of the block in 24 hour format + final TimeOfDay end; + + /// Widget that will be displayed within the block. + final Widget? child; + + /// The start hour of the timetable. + final int startHour; + + /// The width of the block if there is no child + final double blockWidth; + + /// The padding between the lines and the numbers + final double linePadding; + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only( + left: 10, + top: + (((start.hour - startHour) * 60) + start.minute) * sizePerMinute() + + linePadding, + ), + height: (((end.hour - start.hour) * 60) + end.minute - start.minute) * + sizePerMinute(), + child: child ?? + Container( + height: + (((end.hour - start.hour) * 60) + end.minute - start.minute) * + sizePerMinute(), + width: blockWidth, + color: Colors.red.withOpacity(0.5), + ), + ); + } + + double sizePerMinute() { + // TODO(anyone): calculate this based on the size of the screen + return 80 / 60; + } +} diff --git a/lib/src/table.dart b/lib/src/table.dart index 5e95732..76ba919 100644 --- a/lib/src/table.dart +++ b/lib/src/table.dart @@ -22,7 +22,7 @@ class Table extends StatelessWidget { Row( children: [ Text( - '${(i).toString().padLeft(2, '0')}:00', + '${i.toString().padLeft(2, '0')}:00', ), const SizedBox( width: 5, diff --git a/lib/src/time_block.dart b/lib/src/time_block.dart new file mode 100644 index 0000000..bda25d0 --- /dev/null +++ b/lib/src/time_block.dart @@ -0,0 +1,25 @@ +part of timetable; + +class TimeBlock { + TimeBlock({ + required this.start, + required this.end, + this.child, + }) : assert( + start.hour <= end.hour || + (start.hour == end.hour && start.minute < end.minute), + 'start time must be less than end time', + ); + + /// The start time of the block in 24 hour format + /// The start time should be before the end time + final TimeOfDay start; + + /// The end time of the block in 24 hour format + /// The end time should be after the start time + final TimeOfDay end; + + /// The child widget that will be displayed within the block. + /// The size of the parent widget should dictate the size of the child. + final Widget? child; +} diff --git a/lib/src/timetable.dart b/lib/src/timetable.dart index e152e94..5d0e9a8 100644 --- a/lib/src/timetable.dart +++ b/lib/src/timetable.dart @@ -2,9 +2,11 @@ part of timetable; class Timetable extends StatefulWidget { const Timetable({ + this.timeBlocks = const [], this.scrollController, this.startHour = 0, this.endHour = 24, + this.blockWidth = 50, Key? key, }) : super(key: key); @@ -14,6 +16,12 @@ class Timetable extends StatefulWidget { /// Hour at which the timetable ends. final int endHour; + /// The time blocks that will be displayed in the timetable. + final List timeBlocks; + + /// The width of the block if there is no child + final double blockWidth; + /// The scroll controller to control the scrolling of the timetable. final ScrollController? scrollController; @@ -49,6 +57,32 @@ class _TimetableState extends State { startHour: widget.startHour, endHour: widget.endHour, ), + Container( + margin: const EdgeInsets.only(left: 45), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (var block in widget.timeBlocks) ...[ + Block( + start: block.start, + end: block.end, + startHour: widget.startHour, + blockWidth: widget.blockWidth, + child: block.child, + ) + ], + SizedBox( + width: 15, + height: 80 * (widget.endHour - widget.startHour) + 40, + ), + ], + ), + ), + ), + ), ], ), ); diff --git a/lib/timetable.dart b/lib/timetable.dart index 8df4de3..7dd7609 100644 --- a/lib/timetable.dart +++ b/lib/timetable.dart @@ -4,3 +4,5 @@ import 'package:flutter/material.dart'; part 'src/timetable.dart'; part 'src/table.dart'; +part 'src/time_block.dart'; +part 'src/block.dart';