calendar planner.拖放不能正常工作

问题描述 投票:0回答:0

我正在 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 编码经验并逐步解决这个问题,但我在这个问题上停留了一个多星期。我只想能够将任务拖放到任何时隙

flutter drag-and-drop draggable
© www.soinside.com 2019 - 2024. All rights reserved.