Flutter任务调度插件task_scheduler的使用

以下是关于“Flutter任务调度插件task_scheduler的使用”的完整示例demo:

Flutter任务调度插件task_scheduler的使用

概述

Task Scheduler Widget 是一个用于在Flutter中显示可定制时间表的组件。它允许用户安排任务、重新排列任务,并且能够高效地可视化这些任务。

功能特性

  1. 日程视图:显示一个可定制的日程视图来管理任务和预约。
  2. 资源头:定义资源头以对任务进行分类。每个头可以包含自定义的小部件。
  3. 日程配置
    • 定义日程的开始时间和结束时间。
    • 自定义时间轴显示的时间间隔。
    • 支持12小时和24小时格式。
  4. 空槽处理:当用户点击日程中的空槽时触发自定义动作。
  5. 拖放功能
    • 允许用户在日程中拖动和放置任务。
    • 定义回调函数来处理放置的任务,包括更新日程视图。
  6. 条目管理
    • 在日程中添加和编辑条目。
    • 可以自定义条目的属性,如颜色、持续时间、是否可拖动和调整大小。
    • 阻止特定时间段内某个资源上的安排。
  7. 条目交互
    • 定义回调函数来处理与条目的交互,例如点击事件。
    • 在每个条目中显示自定义内容,包括文本和小部件。
  8. 条目调整大小
    • 允许用户调整条目的大小以改变其持续时间。
    • 定义回调函数来处理调整大小的事件,包括更新和完成事件。
    • 使用可自定义的逻辑防止在调整大小过程中条目重叠。

开始使用

要使用此包,将其添加到您的pubspec.yaml文件中:

dependencies:
  task_scheduler: ^最新版本

最新版本替换为最新的版本号,例如task_scheduler: ^0.0.1

示例代码

导入库

import 'package:flutter/material.dart';
import 'package:task_scheduler/task_scheduler.dart';

声明任务调度器和视图

late TaskScheduler taskScheduler;
late TaskScheduleView taskScheduleView;

创建资源头

List<ScheduleResourceHeader> headers = [
  ScheduleResourceHeader(
    id: '1',
    position: 0,
    title: 'Yung',
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Yung',
          style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
        ),
        const SizedBox(height: 3),
      ],
    ),
  ),
  ScheduleResourceHeader(
    id: '2',
    position: 1,
    title: 'Cedric',
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Cedric',
          style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
        ),
        const SizedBox(height: 3),
      ],
    ),
  ),
  ScheduleResourceHeader(
    id: '3',
    position: 2,
    title: 'Moss',
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Moss',
          style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
        ),
        const SizedBox(height: 3),
      ],
    ),
  ),
  ScheduleResourceHeader(
    id: '4',
    position: 3,
    title: 'Matt',
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Matt',
          style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
        ),
        const SizedBox(height: 3),
      ],
    ),
  ),
];

实例化任务调度视图并加载条目

taskScheduleView = TaskScheduleView(
  taskScheduler: TaskScheduler(
    scheduleStartTime: ScheduleTimeline(hour: 8),
    scheduleEndTime: ScheduleTimeline(hour: 17),
    onEmptySlotPressed: handleEmptySlotTap,
    onDragAccept: handleDrop,
    headers: headers,
    entries: [],
    timeFormat: SchedulerTimeSettings(minuteInterval: 30),
  ),
);

List<ScheduleEntry> entries = [
  ScheduleEntry(
    color: Colors.blue,
    id: '123',
    resource: ResourceScheduleEntry(index: 0, hour: 9, minutes: 60),
    duration: 60,
    options: TaskSchedulerSettings(isTaskDraggable: true),
    onTap: () {},
    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'),
  ),
];

taskScheduler = taskScheduleView.loadScheduleView(entries: entries);

渲染视图

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Center(
      child: taskScheduler,
    ),
  );
}

处理空槽点击事件

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'];
  String resourceName = resources[data['resource']['index']];

  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();

  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: [
          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) {
                TaskScheduler newTaskScheduler = TaskScheduler(
                  scheduleStartTime: taskScheduler.scheduleStartTime,
                  scheduleEndTime: taskScheduler.scheduleEndTime,
                  onEmptySlotPressed: handleEmptySlotTap,
                  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
                ];

                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,
                  ),
                  onTap: () {
                    print('clicked');
                  },
                  child: Text(title, style: TextStyle(fontSize: 14)),
                );

                if (taskScheduleView.isResourceSlotAvailable(newEntry)) {
                  newTaskScheduler.entries?.add(newEntry);
                } else {
                  ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
                    content: Text("Error: Slot not Available."),
                    backgroundColor: Colors.red,
                    behavior: SnackBarBehavior.floating
                  ));
                }

                setState(() {
                  taskScheduler = newTaskScheduler;
                });
              }
            },
          ),
        ],
      );
    },
  );
}

String generateId(int length) {
  const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  Random random = Random();
  return List.generate(length, (index) => charset[random.nextInt(charset.length)]).join();
}

阻止特定时间段

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,
  )
];

taskScheduleView = TaskScheduleView(
  taskScheduler: TaskScheduler(
    scheduleStartTime: ScheduleTimeline(hour: 8),
    scheduleEndTime: ScheduleTimeline(hour: 17),
    onEmptySlotPressed: handleEmptySlotTap,
    onDragAccept: handleDrop,
    entries: [],
    blockedEntries: blockedEntries,
    headers: headers,
    timeFormat: SchedulerTimeSettings(minuteInterval: 30),
  ),
);

调整时间格式

TaskScheduler(
  scheduleStartTime: ScheduleTimeline(hour: 8),
  scheduleEndTime: ScheduleTimeline(hour: 17),
  timeFormat: SchedulerTimeSettings(
    minuteInterval: 30,
    use24HourFormat: true,
    includePeriod: true,
    includeMinutes: false,
    showHoursOnly: true,
  ),
)

处理拖放事件

TaskScheduler(
  scheduleStartTime: ScheduleTimeline(hour: 8),
  onDragAccept: handleDrop,
)

void handleDrop(Map<String, dynamic> data) {
  TaskScheduleView view = TaskScheduleView(
    taskScheduler: TaskScheduler(
      scheduleStartTime: taskScheduler.scheduleStartTime,
      scheduleEndTime: taskScheduler.scheduleEndTime,
      onEmptySlotPressed: handleEmptySlotTap,
      onDragAccept: handleDrop,
      entries: taskScheduler.entries,
      headers: taskScheduler.headers,
      timeFormat: taskScheduler.timeFormat,
    ),
  );

  setState(() {
    try {
      taskScheduler = view.updateScheduleView(view, data);
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text("Error: $e"),
        backgroundColor: Colors.red,
        behavior: SnackBarBehavior.floating,
      ));
    }
  });
}

允许条目重叠

void handleDrop(Map<String, dynamic> data) {
  TaskScheduleView view = TaskScheduleView(
    taskScheduler: TaskScheduler(
      scheduleStartTime: taskScheduler.scheduleStartTime,
      allowEntryOverlap: true,
      onEmptySlotPressed: handleEmptySlotTap,
      onDragAccept: handleDrop,
      entries: taskScheduler.entries,
      headers: taskScheduler.headers,
      timeFormat: taskScheduler.timeFormat,
    ),
  );

  setState(() {
    try {
      taskScheduler = view.updateScheduleView(view, data);
    } catch (e) {
      print(e.toString());
    }
  });
}

调整条目大小

ScheduleEntry(
  color: Colors.green,
  id: '123444',
  options: TaskSchedulerSettings(
    isTaskDraggable: true,
    taskResizeMode: {
      'allowResize': true,
      'onResizeEnd': onResizeEnd,
      'onResizeUpdate': onResizeUpdate
    },
  ),
)

void onResizeEnd(Map<String, dynamic> resizeData) {
  print('onResizeEnd: $resizeData');
}

void onResizeUpdate(ScheduleEntry entry) {
  print('onResizeUpdate');
  taskScheduleView.onResizeEntry(entry);
}

防止重叠

void onResizeUpdate(ScheduleEntry entry) {
  taskScheduleView.onResizeEntry(entry);
}

更多关于Flutter任务调度插件task_scheduler的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter任务调度插件task_scheduler的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


task_scheduler 是一个 Flutter 插件,用于在 Android 和 iOS 设备上调度后台任务。它允许你在特定的时间间隔或特定条件下执行任务,即使应用程序处于后台或设备处于休眠状态。

以下是如何使用 task_scheduler 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 task_scheduler 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  task_scheduler: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 task_scheduler 插件:

import 'package:task_scheduler/task_scheduler.dart';

3. 初始化任务

你可以使用 TaskScheduler 类来初始化和管理任务。以下是一个简单的示例,展示如何调度一个周期性任务:

void scheduleTask() async {
  // 创建一个任务
  Task task = Task(
    id: 'my_task_id',  // 任务的唯一标识符
    type: TaskType.periodic,  // 任务类型,周期性任务
    interval: Duration(hours: 1),  // 任务执行间隔
    initialDelay: Duration(seconds: 10),  // 任务首次执行前的延迟
    requiredNetwork: TaskNetwork.connected,  // 任务执行所需的网络条件
    requiresCharging: false,  // 任务是否需要在充电时执行
    requiresDeviceIdle: false,  // 任务是否需要在设备空闲时执行
    persist: true,  // 任务是否持久化
  );

  // 调度任务
  await TaskScheduler.schedule(task, callback);
}

void callback(TaskInfo taskInfo) {
  // 任务执行时的回调函数
  print('Task ${taskInfo.id} is running');
  // 在这里执行你的后台任务逻辑
}

4. 取消任务

如果你想取消一个已调度的任务,可以使用 cancel 方法:

void cancelTask() async {
  await TaskScheduler.cancel('my_task_id');
}

5. 检查任务状态

你可以使用 getTaskInfo 方法来获取某个任务的当前状态:

void checkTaskStatus() async {
  TaskInfo taskInfo = await TaskScheduler.getTaskInfo('my_task_id');
  print('Task status: ${taskInfo.status}');
}

6. 处理任务执行

callback 函数中,你可以执行任何你想要的后台任务逻辑。请注意,后台任务的执行时间有限,因此你应该尽量优化任务的执行时间。

7. 处理权限

在 Android 上,调度后台任务可能需要一些权限。请确保在 AndroidManifest.xml 中添加以下权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

8. iOS 后台模式

在 iOS 上,你需要在 Info.plist 文件中添加后台模式配置:

<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>processing</string>
</array>
回到顶部