0 purchases
task scheduler
Task Scheduler Widget #
The Task Scheduler Widget is a Flutter component designed to display a schedule with customizable time slots and tasks. It provides functionalities to schedule tasks, rearrange them, and visualize them efficiently.
Demo #
https://youngcet.github.io/taskscheduler.github.io/#/
Features: #
Schedule View: Display a customizable schedule view for managing tasks and appointments.
Resource Headers: Ability to define resource headers to categorize tasks. Each header can contain custom widgets.
Schedule Configuration:
Define the start and end time of the schedule.
Customize the time interval for the timeline display.
Support for both 12-hour and 24-hour time formats.
Empty Slot Handling: Trigger custom actions when users click on empty slots in the schedule.
Drag and Drop Functionality:
Allow users to drag and drop tasks within the schedule.
Define callbacks to handle dropped tasks, including updating the schedule view.
Entry Management:
Add, and edit entries within the schedule.
Customize entry properties such as color, duration, draggable and resizable behavior.
Block off specific periods on a schedule for a resource.
Entry Interaction:
Define callbacks for handling interactions with entries, such as onTap events.
Display custom content within each entry, including text and widgets.
Entry Resizing:
Allow users to resize entries to adjust their duration.
Define callbacks to handle resizing events, including update and completion events.
Prevent overlapping of entries during resizing with customizable logic.
Web #
[
Mobile #
[
Getting started #
To use this package, add it to your pubspec.yaml file:
dependencies:
task_scheduler: ^latest_version
copied to clipboard
Replace 'latest_version' with the lastest version, e.g. task_scheduler: ^0.0.1
Usage #
To use the widget,
Import the task scheduler library.
import 'package:task_scheduler/task_scheduler.dart';
// declare the task scheduler and schedule view
late TaskScheduler taskScheduler;
late TaskScheduleView taskScheduleView;
copied to clipboard
Create an instance of the TaskScheduleView and pass the TaskScheduler.
// define the resource headers
List<ScheduleResourceHeader> headers = [
ScheduleResourceHeader(
id: '1', // resource id
position: 0, // resource position, this is the order of the resource. 0 = first, 1 = 2nd, and so forth
// resource widget, to add title, descriptions etc.
title: 'Yung', // resource title
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Yung',
style:
const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
const SizedBox(
height: 3,
),
],
),
),
ScheduleResourceHeader(
id: '2', // resource id
position: 1, // resource position
// resource widget
title: 'Cedric', // resource title
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Cedric',
style:
const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
const SizedBox(
height: 3,
),
],
),
),
ScheduleResourceHeader(
id: '3', // resource id
position: 2, // resource position
// resource widget
title: 'Moss', // resource title
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Moss',
style:
const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
const SizedBox(
height: 3,
),
],
),
),
ScheduleResourceHeader(
id: '4', // resource id
position: 3, // resource position
// resource widget
title: 'Matt', // resource title
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Matt',
style:
const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
const SizedBox(
height: 3,
),
],
),
)
];
// Instatiate the TaskScheduleView and pass the TaskScheduler
taskScheduleView = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: ScheduleTimeline(hour: 8), // start time
scheduleEndTime: ScheduleTimeline(hour: 17), // end time
onEmptySlotPressed: (Map<String, dynamic> data) {}, // Callback function when an empty slot is pressed
onDragAccept: (Map<String, dynamic> data) {}, // Callback function when an entry is dropped from dragging
headers: headers, // resource headers
entries: [], // set entries to empty, do not add entries here
timeFormat: SchedulerTimeSettings(minuteInterval: 30) // minute intervals on the timeline, default 60
));
copied to clipboard
Load the ScheduleView.
// to load the schedule view, first add entries/tasks
List<ScheduleEntry> entries = [
ScheduleEntry(
color: Colors.blue, // entry background color
id: '123', // entry id
// resource to assign the entry to
resource: ResourceScheduleEntry(
index: 0, // resource position
hour: 9, // entry start hour
minutes: 60), // entry start minute
duration: 60, // entry duration
// entry options
options: TaskSchedulerSettings(
isTaskDraggable: true // if set to true, the entry is draggable, default false
),
onTap: () {}, // Callback function when an entry is pressed
// custom widget
child: const Text('Booked'),
),
ScheduleEntry(
color: Colors.green,
id: '1234',
resource: ResourceScheduleEntry(
index: 0,
hour: 8,
minutes: 30
),
duration: 30,
options: TaskSchedulerSettings(
isTaskDraggable: true
),
onTap: () {},
child: const Text('Booked'),
),
];
// load the view and pass the entries
taskScheduler = taskScheduleView.loadScheduleView(entries: entries);
copied to clipboard
Render the view.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: taskScheduler
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
copied to clipboard
ScreenShots
Hiding the line that shows the current time #
TaskScheduleView view = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: ...
...
showCurrentTimeLine: false, // false to hide it, true to show it (default is true)
...
));
copied to clipboard
Empty Slot Click Event Handling #
To create new tasks when users click empty slots in the schedule, define a function that handles this action and assign it to the onEmptySlotPressed callback of the TaskScheduler.
TaskScheduleView view = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: ...
...
onEmptySlotPressed: handleEmptySlotTap, // pass function here
...
));
// Define a function to handle creating a new task when an empty slot is clicked
// This function receives 'data' containing details about the clicked slot,
// such as resource ID, resource information etc.
void handleEmptySlotTap(Map<String, dynamic> data) {
// implement the logic here to handle empty slot tap
print('onTap: $data');
}
copied to clipboard
Adding Entries to Your Schedule #
While Step 3 showed how to add entries when the schedule loads, this section demonstrates how to add new entries by clicking a button.
In this example, clicking an empty slot in the schedule opens a dialog for creating a new entry. Here, the user can enter a title, set the duration of the task, and click "Create" to add it to the schedule.
// function to handle click event on empty slots
void handleEmptySlotTap(Map<String, dynamic> data) {
_createNewEntry(context, data);
}
void _createNewEntry(BuildContext context, Map<String, dynamic> data) {
List<String> resources = ['Cedric', 'Yung', 'Moss', 'Matt'];
int resourceIndex = data['resource']['index']; // resource position
String resourceName = resources[data['resource']['index']]; // resource name
// get time slot of the clicked empty slot
String hour = (data['resource']['hour'] < 10) ? '0${data['resource']['hour']}' : data['resource']['hour'].toString();
String minutes = (data['resource']['minutes'] < 10) ? '0${data['resource']['minutes']}' : data['resource']['minutes'].toString();
// text controllers for the dialog box
TextEditingController titleController = TextEditingController();
TextEditingController durationController = TextEditingController();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Create New Entry'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
keyboardType: TextInputType.text,
controller: titleController,
decoration: InputDecoration(hintText: "Enter title"),
),
SizedBox(height: 15),
TextField(
keyboardType: TextInputType.number,
controller: durationController,
decoration: InputDecoration(hintText: "Enter duration"),
),
SizedBox(height: 15),
Text(
"Time: $hour:$minutes\nWith: $resourceName", style: TextStyle(fontSize: 14),
),
],
),
actions: <Widget>[
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('Create'),
onPressed: () {
String title = titleController.text;
String duration = durationController.text;
Navigator.of(context).pop();
if (title.isNotEmpty && duration.isNotEmpty){
// create a new task scheduler
TaskScheduler newTaskScheduler = TaskScheduler(
scheduleStartTime: taskScheduler.scheduleStartTime,
scheduleEndTime: taskScheduler.scheduleEndTime,
onEmptySlotPressed: handleEmptySlotTap, // this is the function defined above
onDragAccept: handleDrop,
entries: taskScheduler.entries,
headers: taskScheduler.headers,
timeFormat: taskScheduler.timeFormat,
);
List<Color> colors = [
Colors.purple,
Colors.blue,
Colors.green,
Colors.orange,
Colors.lime,
Colors.cyan,
Colors.grey
];
// define the new entry
ScheduleEntry newEntry = ScheduleEntry(
color: colors[Random().nextInt(colors.length)],
id: generateId(5),
resource: ResourceScheduleEntry(
index: resourceIndex,
hour: int.parse(hour),
minutes: int.parse(minutes),
),
duration: int.parse(duration),
options: TaskSchedulerSettings(
isTaskDraggable: true, // false to disable drag
),
onTap: () {
print('clicked');
// implement onTap logic
},
child: Text(title, style: TextStyle(fontSize: 14)),
);
// TaskScheduleView provides a function to check if a slot is available,
// i.e the new entry slot does not overlap with an existing entry
if (taskScheduleView.isResourceSlotAvailable(newEntry)){
// slot is available, add entry to the new TaskScheduler
newTaskScheduler.entries?.add(newEntry);
}else{
// slot not available
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Error: Slot not Available."),
backgroundColor: Colors.red,
behavior: SnackBarBehavior.floating
));
}
// re-build the task scheduler
setState(() {
taskScheduler = newTaskScheduler;
});
}
},
),
],
);
},
);
}
// generate random string
String generateId(int length) {
const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
Random random = Random();
return List.generate(length, (index) => charset[random.nextInt(charset.length)]).join();
}
copied to clipboard
Blocking Off Times #
Block off specific periods on a schedule for a resource.
You can specify entries to block with the BlockedEntry object.
BlockedEntry(
title: 'Lunch break', // entry title (optional)
resource: ResourceScheduleEntry(
index: 2, // the resource to add entries against resources, i.e. 0 = 1st resource, 1 = 2nd etc
hour: 8, // start hour to block
minutes: 0, // start minutes to blobk
),
duration: 60 // duration
),
copied to clipboard
Define the BlockedEntry objects and add them to a list.
List<BlockedEntry> blockedEntries = [
BlockedEntry(
resource: ResourceScheduleEntry(
index: 2,
hour: 8,
minutes: 0,
),
duration: 60
),
BlockedEntry(
resource: ResourceScheduleEntry(
index: 2,
hour: 11,
minutes: 0,
),
duration: 120
),
BlockedEntry(
resource: ResourceScheduleEntry(
index: 0,
hour: 9,
minutes: 0,
),
duration: 60
),
BlockedEntry(
resource: ResourceScheduleEntry(
index: 1,
hour: 10,
minutes: 30,
),
duration: 60
)
];
copied to clipboard
Pass the list to the blockedEntries property of TaskScheduler.
taskScheduleView = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: ScheduleTimeline(hour: 8),
...
blockedEntries: blockedEntries, // add here
...
));
copied to clipboard
TimeFormat Property #
Customize your schedule's timeline with the SchedulerTimeSettings options.
TaskScheduler(
scheduleStartTime: ScheduleTimeline(hour: 8),
scheduleEndTime: ScheduleTimeline(hour: 17), // always define the end time in a 24 hour format and set the `use24HourFormat=false` to use the 12 hour format
...
timeFormat: SchedulerTimeSettings(
minuteInterval: 30, // set the minute interval, default is 60 (supported intervals [10, 15, 20, 30, 60])
use24HourFormat: true, // set wether the timeline should use 12/24 hour format, default is 24 hour format
includePeriod: true, // set to include period on 12 hour format
includeMinutes: false, // set to add or remove minutes from the clock (this only applies to a 12 hour format with the minute intervals set to 60)
showHoursOnly: true // show/hide minutes on the timeline
),
...
)
copied to clipboard
Working with Drag & Drop #
By default, entries can be dropped onto the grid. When adding a new entry, you can define whether it's draggable by setting isTaskDraggable to true within the entry's options parameter.
ScheduleEntry(...
...
options: TaskSchedulerSettings(
isTaskDraggable: true // set to true to enable drag
),
copied to clipboard
Once set to true, define a function to handle what happens when an entry is dropped and pass it to the TaskScheduler's onDragAccept parameter.
TaskScheduler(
scheduleStartTime: ScheduleTimeline(hour: 8),
....
onDragAccept: handleDrop // I have passed the function handleDrop
...
));
// declare the onDragAccept callback function that takes 1 argument 'data'
// this function is called when an entry is dropped
void handleDrop(Map<String, dynamic> data) {}){
// 'data' holds information about an entry when it is dropped
// data such as resourceId, resourceIndex, etc.
// you can access the values from data and process them as needed
String id = data['id'];
String hour = data['hour'];
String min = data['minutes'];
String resourceIdx = data['resourceIndex'];
String duration = data['duration'];
...
// print data to see the information about the entry and keys to access the values
print(data);
}
copied to clipboard
If you don't want to manually process the data, TaskScheduleView provides a function to update the schedule view when an entry is dropped.
void handleDrop(Map<String, dynamic> data) {
// create an instance of TaskScheduleView
// create a new TaskScheduler and pass it to the view
TaskScheduleView view = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: taskScheduler.scheduleStartTime,
scheduleEndTime: taskScheduler.scheduleEndTime,
onEmptySlotPressed: handleEmptySlotTap,
onDragAccept: handleDrop,
entries: taskScheduler.entries,
headers: taskScheduler.headers,
timeFormat: taskScheduler.timeFormat,
));
// update the schedule view and rebuild the widget
setState(() {
taskScheduler = view.updateScheduleView(view, data);
});
}
copied to clipboard
Allow Entry Overlap (Drag & Drop) #
allowEntryOverlap property allows overlapping of entries. By default entries are not allowed to overlap.
void handleDrop(Map<String, dynamic> data) {
TaskScheduleView view = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: taskScheduler.scheduleStartTime,
...
allowEntryOverlap: true, // true to allow entries to overlap when drag and dropped
...,
));
// update the schedule view and rebuild the widget
setState(() {
// use try-catch to catch an exception when an overlap occurs
try{
taskScheduler = view.updateScheduleView(view, data);
}catch (e){
print(e.toString());
}
});
}
copied to clipboard
Resizing an Entry #
You can change an entry's end time by resizing the entry.
Currently, only the end time can be changed through resizing. To change the start time you can use the drag and drop feature.
Allow Entry Resize #
taskResizeMode property allows resizing of an entry.
ScheduleEntry(
color: Colors.green,
id: '123444',
...
options: TaskSchedulerSettings(
isTaskDraggable: true,
taskResizeMode: {
'allowResize': true, // if set to true allows an entry to be resizable
'onResizeEnd': onResizeEnd, // a callback function that is called when resizing is completed
'onResizeUpdate': onResizeUpdate // a callback function that is called during resizing of an entry
},
),
...
),
// Define the function to handle resize end event
void onResizeEnd(Map<String, dynamic> resizeData){
// implement the logic here to handle resize end
print('onResizeEnd: $resizeData');
}
// Define the function to handle resize update event
void onResizeUpdate(ScheduleEntry entry){
// implement the logic here to handle resize update
inspect(entry);
}
copied to clipboard
Preventing Overlapping Entries During Resizing #
In onResizeUpdate function you can implement your own logic to prevent entries from overlapping (if required). The TaskScheduleView also provides onResizeEntry function that prevents entries from overlapping.
// Define the function to handle resize update event
void onResizeUpdate(ScheduleEntry entry){
// call onResizeEntry function of the TaskScheduleView
// this will prevent entries from overlapping during resizing
taskScheduleView.onResizeEntry(entry);
}
copied to clipboard
Calendar Views #
Calendar view provides a week schedule view. There are two types to choose from, CalendarView.weekView() and CalendarView.weekViewWithMonth().
CalendarView.weekView() #
CalendarView.weekViewWithMonth() #
This can be achieved by passing CalendarView.weekView() in the headers property of the TaskScheduler.
taskScheduleView = TaskScheduleView(
taskScheduler: TaskScheduler(
scheduleStartTime: ScheduleTimeline(hour: 8),
...
headers: CalendarView.weekView(), // add this
...
));
copied to clipboard
CalendarView.weekView() properties
CalendarView.weekView({
bool? showOneLetterWeekDayName, // if set to false, week day names will show as 3 letter word, i.e Sunday = Sun, Monday = Mon etc, default is true
String? firstDayOfWeek, // first day of the week (CalendarView.monday or CalendarView.sunday)
bool? upperCaseWeekDayName, // upper case the week day name,
bool? showOnlyWeekDay, // shows only the week day name without the date
})
copied to clipboard
CalendarView.weekViewWithMonth() properties
CalendarView.weekViewWithMonth({
String? firstDayOfWeek, // first day of the week (CalendarView.monday or CalendarView.sunday)
})
copied to clipboard
The default firstDayOfWeek is Sunday.
Demo #
View demo in the example tab.
Contributing #
If you have ideas or improvements for this package, we welcome contributions. Please open an issue or create a pull request on our GitHub repository.
License #
This package is available under the MIT License.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.