mirror of
https://github.com/Iconica-Development/flutter_timetable.git
synced 2025-05-18 19:43:43 +02:00
feat: added timeblock rendering
This commit is contained in:
parent
06609a7537
commit
ff5aeb3bf8
8 changed files with 169 additions and 49 deletions
|
@ -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
|
|
|
@ -124,7 +124,7 @@ linter:
|
||||||
prefer_asserts_with_message: true
|
prefer_asserts_with_message: true
|
||||||
prefer_collection_literals: true
|
prefer_collection_literals: true
|
||||||
prefer_conditional_assignment: true
|
prefer_conditional_assignment: true
|
||||||
prefer_const_constructors: true
|
prefer_const_constructors: false
|
||||||
prefer_const_constructors_in_immutables: true
|
prefer_const_constructors_in_immutables: true
|
||||||
prefer_const_declarations: false
|
prefer_const_declarations: false
|
||||||
prefer_const_literals_to_create_immutables: false
|
prefer_const_literals_to_create_immutables: false
|
||||||
|
|
|
@ -2,14 +2,59 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:timetable/timetable.dart';
|
import 'package:timetable/timetable.dart';
|
||||||
|
|
||||||
void main() {
|
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);
|
const TimetableDemo({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TimetableDemo> createState() => _TimetableDemoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TimetableDemoState extends State<TimetableDemo> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
final List<TimeBlock> 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
|
@override
|
||||||
Widget build(BuildContext context) {
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
58
lib/src/block.dart
Normal file
58
lib/src/block.dart
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ class Table extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${(i).toString().padLeft(2, '0')}:00',
|
'${i.toString().padLeft(2, '0')}:00',
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 5,
|
width: 5,
|
||||||
|
|
25
lib/src/time_block.dart
Normal file
25
lib/src/time_block.dart
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -2,9 +2,11 @@ part of timetable;
|
||||||
|
|
||||||
class Timetable extends StatefulWidget {
|
class Timetable extends StatefulWidget {
|
||||||
const Timetable({
|
const Timetable({
|
||||||
|
this.timeBlocks = const [],
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.startHour = 0,
|
this.startHour = 0,
|
||||||
this.endHour = 24,
|
this.endHour = 24,
|
||||||
|
this.blockWidth = 50,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -14,6 +16,12 @@ class Timetable extends StatefulWidget {
|
||||||
/// Hour at which the timetable ends.
|
/// Hour at which the timetable ends.
|
||||||
final int endHour;
|
final int endHour;
|
||||||
|
|
||||||
|
/// The time blocks that will be displayed in the timetable.
|
||||||
|
final List<TimeBlock> timeBlocks;
|
||||||
|
|
||||||
|
/// The width of the block if there is no child
|
||||||
|
final double blockWidth;
|
||||||
|
|
||||||
/// The scroll controller to control the scrolling of the timetable.
|
/// The scroll controller to control the scrolling of the timetable.
|
||||||
final ScrollController? scrollController;
|
final ScrollController? scrollController;
|
||||||
|
|
||||||
|
@ -49,6 +57,32 @@ class _TimetableState extends State<Timetable> {
|
||||||
startHour: widget.startHour,
|
startHour: widget.startHour,
|
||||||
endHour: widget.endHour,
|
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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,3 +4,5 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
part 'src/timetable.dart';
|
part 'src/timetable.dart';
|
||||||
part 'src/table.dart';
|
part 'src/table.dart';
|
||||||
|
part 'src/time_block.dart';
|
||||||
|
part 'src/block.dart';
|
||||||
|
|
Loading…
Reference in a new issue