我正在 GPT 的帮助下构建每日计划程序。日历的每一天都分为 30 分钟间隔(时间段),我可以将任务添加到每个时间段。我想在时间段之间拖放任务,但我无法将任务拖放到除前 3 个以外的任何时间段。我正在尝试通过聊天 gpt 解决此问题,但我没有编码经验来解决这个问题。为什么我不能将任务拖放到 13:00-13:30 以外的任何时间段,13:30 -14:00,14:00-14:30 ??
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/intl.dart';
import 'package:flutter/rendering.dart';
class TimeSlot {
final String startTime;
final String endTime;
List<String> tasks;
TimeSlot({
required this.startTime,
required this.endTime,
this.tasks = const [],
});
void updateTasks(List<String> newTasks) {
tasks = newTasks;
}
}
class Task {
String title;
DateTime nextDueDate;
String repeatFrequency;
Task({required this.title, required this.nextDueDate, required this.repeatFrequency});
}
enum RepeatFrequency { daily, weekly, monthly, yearly, custom }
void updateNextDueDate(Task task) {
DateTime currentDate = DateTime.now();
DateTime newDueDate;
// Convert the repeatFrequency from String to RepeatFrequency enum
RepeatFrequency repeatFrequency = RepeatFrequency.values.firstWhere((e) => e.toString() == 'RepeatFrequency.' + task.repeatFrequency);
switch (repeatFrequency) {
case RepeatFrequency.daily:
newDueDate = currentDate.add(Duration(days: 1));
break;
case RepeatFrequency.weekly:
newDueDate = currentDate.add(Duration(days: 7));
break;
case RepeatFrequency.monthly:
newDueDate = DateTime(currentDate.year, currentDate.month + 1, currentDate.day);
break;
case RepeatFrequency.yearly:
newDueDate = DateTime(currentDate.year + 1, currentDate.month, currentDate.day);
break;
case RepeatFrequency.custom:
// For custom repeat frequency, add the specified number of days
int customDays = int.parse(task.repeatFrequency);
newDueDate = currentDate.add(Duration(days: customDays));
break;
}
task.nextDueDate = newDueDate;
}
class DayView extends StatefulWidget {
final DateTime selectedDay;
const DayView({Key? key, required this.selectedDay}) : super(key: key);
@override
_DayViewState createState() => _DayViewState();
}
class _DayViewState extends State<DayView> {
late List<TimeSlot> timeSlots;
late ScrollController _scrollController;
final double _scrollSpeed = 100.0; // Set this to control the speed of auto-scrolling
bool _isDragging = false; // Add this line
Offset _dragOffset = Offset.zero;
@override
void initState() {
super.initState();
createTimeSlots();
loadTimeSlots();
_scrollController = ScrollController();
_scrollController.addListener(() {
if (_isDragging) {
double screenHeight = MediaQuery.of(context).size.height;
if (_dragOffset.dy <= screenHeight * 0.1) {
_scrollController.jumpTo(
_scrollController.offset - _scrollSpeed,
);
} else if (_dragOffset.dy >= screenHeight * 0.9) {
_scrollController.jumpTo(
_scrollController.offset + _scrollSpeed,
);
}
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_formatDate(widget.selectedDay)),
),
body: Stack(
children: [
ListView.builder(
controller: _scrollController,
itemCount: timeSlots.length,
itemBuilder: (context, index) {
return _buildTimeSlot(timeSlots[index]);
},
),
],
),
);
}
Widget _buildChip(String task, TimeSlot timeSlot) {
return Chip(
label: _buildDraggableTask(task, timeSlot),
onDeleted: () {
setState(() {
timeSlot.tasks.remove(task);
});
saveTimeSlot(timeSlot);
},
);
}
void createTimeSlots() {
// Populate the timeSlots list with time slots
timeSlots = [];
for (int i = 13; i <= 20; i++) {
timeSlots.add(
TimeSlot(
startTime: '$i:00',
endTime: '$i:30',
),
);
timeSlots.add(
TimeSlot(
startTime: '$i:30',
endTime: '${i + 1}:00',
),
);
}
}
Future<void> loadTimeSlots() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
for (int i = 0; i < timeSlots.length; i++) {
String key = '${_formatDateForStorage(widget.selectedDay)}${timeSlots[i]
.startTime}';
List<String>? tasks = prefs.getStringList(key);
if (tasks != null) {
setState(() {
timeSlots[i].tasks = tasks;
});
}
}
}
Future<void> saveTimeSlot(TimeSlot timeSlot) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String key = '${_formatDateForStorage(widget.selectedDay)}${timeSlot
.startTime}';
await prefs.setStringList(key, timeSlot.tasks);
}
String _formatDate(DateTime date) {
DateFormat formatter = DateFormat('EEEE, MMMM d, y');
return formatter.format(date);
}
String _formatDateForStorage(DateTime date) {
DateFormat formatter = DateFormat('yyyyMMdd');
return formatter.format(date);
}
Widget _buildTimeSlotDropTarget(TimeSlot timeSlot) {
return DragTarget<Map<String, dynamic>>(
builder: (context, candidateData, rejectedData) {
return Opacity(
opacity: candidateData.isNotEmpty ? 1.0 : 0.3, // Change opacity when there is a candidate
child: Container(
height: 50.0, // Add fixed height
child: ListTile(
title: Text('Add / Move task here'),
),
),
);
},
onWillAccept: (data) => true,
onAccept: (data) {
setState(() {
TimeSlot sourceTimeSlot = data['sourceTimeSlot'];
String task = data['task'];
sourceTimeSlot.tasks.remove(task); // Remove the task from the source time slot.
timeSlot.tasks.add(task);
});
saveTimeSlot(timeSlot);
saveTimeSlot(data['sourceTimeSlot']); // Save the source time slot after removing the task.
},
);
}
Widget _buildDraggableTask(String task, TimeSlot sourceTimeSlot) {
return LongPressDraggable<Map<String, dynamic>>(
data: {'task': task, 'sourceTimeSlot': sourceTimeSlot},
child: ListTile(title: Text(task)),
feedback: Material(
child: ListTile(title: Text(task)),
elevation: 6,
),
onDragStarted: () {
setState(() {
_isDragging = true;
});
},
onDragEnd: (details) {
setState(() {
_isDragging = false;
});
},
);
}
Widget _buildTimeSlot(TimeSlot timeSlot) {
return InkWell(
onTap: () async {
String? newTask = await showDialog<String>(
context: context,
builder: (context) {
return TaskInputDialog();
},
);
if (newTask != null && newTask.isNotEmpty) {
setState(() {
timeSlot.tasks = [...timeSlot.tasks, newTask];
});
saveTimeSlot(timeSlot);
}
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${timeSlot.startTime} - ${timeSlot.endTime}',
style: TextStyle(fontWeight: FontWeight.bold)),
SingleChildScrollView(
child: Wrap(
spacing: 8.0,
runSpacing: 8.0,
children: timeSlot.tasks
.map(
(task) => _buildChip(task, timeSlot),
)
.toList(),
),
),
_buildTimeSlotDropTarget(timeSlot), // Move this line outside the Wrap widget
],
),
),
),
);
}
}
class TaskInputDialog extends StatefulWidget {
final String? initialTask;
TaskInputDialog({this.initialTask});
@override
_TaskInputDialogState createState() => _TaskInputDialogState();
}
class _TaskInputDialogState extends State<TaskInputDialog> {
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
if (widget.initialTask != null) {
_controller.text = widget.initialTask!;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Enter task'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'Task name'),
),
SizedBox(height: 16.0),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(_controller.text);
},
child: Text('Save'),
),
],
),
),
);
}
}
我一直在使用 chatGPT-4 构建这个,因为我有 tiny 编码经验并逐步解决这个问题,但我在这个问题上停留了一个多星期。我只想能够将任务拖放到任何时隙