Esta é a classe para um aplicativo de lista de tarefas que eu estou usando em dart. Eu gostaria de adicionar a seguinte animação a ela
Ao concluir uma tarefa, ela deve deslizar por sobre a lista de tarefas (8 pontos)
Uma opção simples é usar um Stack e um AnimatedPositioned
Pode usar as classes do tutorial de animação da aula de hoje.
Pode (deve!) usar a classe AnimatedList.
A tarefa concluída deve fazer um "tilt" ao ir para a posição, como se estivesse caindo um lado primeiro (inclinar e depois alinhar horizontalmente de novo). (2 pontos)
Essas animações devem ser feitas apenas ao concluir uma tarefa.
Não é necessário fazer a animação ao "desconcluir" uma tarefa (embora seja fácil...)
Não altere o comportamento do "deletar tarefa" (continua o desfazer com 3 segundos)
Se você colocou um "desfazer" de três segundos no concluir tarefa, tire - vai atrapalhar a animação!
Implemente o desconcluir tarefa! Deve apenas mover a tarefa para o final da lista de tarefas não concluída.


 class TodoApp extends StatefulWidget {
  final String email;
  final String token;

  // Adicionando os parâmetros no construtor
  TodoApp({required this.email, required this.token});

  @override
  _TodoAppState createState() => _TodoAppState();
}

class _TodoAppState extends State<TodoApp> {
  late String _token;
  late String _email;
  List<TodoItem> _tasks = []; // Lista de tarefas

  @override
  void initState() {
    super.initState();
    _token = widget.token;
    _email = widget.email;
    _fetchTasks(); // Fazendo a requisição ao carregar a tela
  }

  Future<void> _fetchTasks() async {
    final response = await http.get(
      Uri.parse('https://barra.cos.ufrj.br:443/rest/tarefas'),
      headers: {
        'Authorization': 'Bearer ${widget.token}',
        'Content-Type': 'application/json',
      },
    );
    
    if (response.statusCode == 200) {
      // O JSON será salvo
      final tasksJson = jsonDecode(response.body);
      final tasks = tasksJson[0]['valor'] as List; // Acessando o campo 'valor'

      // Adicionando as tarefas na lista
      setState(() {
        _tasks = tasks.map((task) {
          return TodoItem(
            task['titulo'],
            completed: task['concluida'] ?? false,
          );
        }).toList();
      });
    } else {
      // Se falhar, mostre um erro
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Erro ao carregar as tarefas.')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TodoList(tasks: _tasks, email: _email, token: _token),
    );
  }
}

class TodoItem {
  String task;
  bool completed;
  bool isDeleting;

  TodoItem(this.task, {this.completed = false, this.isDeleting = false});
}

class TodoList extends StatefulWidget {
  final List<TodoItem> tasks; // Recebendo a lista de tarefas
  final String email;
  final String token;
  
  TodoList({required this.tasks, required this.email, required this.token});

  @override
  _TodoListState createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  List<TodoItem> get _tasks => widget.tasks; // Acessando as tarefas passadas
    // Função para salvar as tarefas
  Future<void> _saveTasks() async {
  final tasksJson = _tasks.map((task) {
    return {
      'titulo': task.task,
      'concluida': task.completed,
      'ordem': _tasks.indexOf(task) + 1,
    };
  }).toList();

  final body = jsonEncode({
    'email': widget.email,
    'valor': tasksJson,
  });

  try {
    final response = await http.patch(
      Uri.parse('https://barra.cos.ufrj.br:443/rest/tarefas'),
      headers: {
        'Authorization': 'Bearer ${widget.token}',
        'Content-Type': 'application/json',
      },
      body: body,
    );
    if (response.statusCode == 204) {
      // Se a requisição for bem-sucedida
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Tarefas salvas com sucesso!')),
      );      
    } else if (response.headers['content-type']?.contains('application/json') == true) {
      final Map<String, dynamic> responseBody = json.decode(response.body);

      // Verifica se a resposta tem a mensagem "JWT expired"
      if (responseBody['message'] == 'JWT expired') {
        // Token expirado, redireciona para LoginScreen
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (context) => LoginScreen()),
        );

        // Exibe a mensagem de "Sessão Expirada"
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Sessão Expirada')),
        );
        return;
      } else {
      // Caso haja erro, exibe o código HTTP e a resposta do corpo
          print("Erro ao salvar tarefas. Status Code: ${response.statusCode}");
          print("Resposta: ${response.body}");
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('Erro ao salvar as tarefas.')),
          );
        }
    }
    else {
      // Caso haja erro, exibe o código HTTP e a resposta do corpo
      print("Erro ao salvar tarefas. Status Code: ${response.statusCode}");
      print("Resposta: ${response.body}");
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Erro ao salvar as tarefas.')),
      );
    }
  } catch (e) {
    // Em caso de exceção durante a requisição
    print("Erro na requisição: $body, $e");
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Erro ao salvar as tarefas.')),
    );
  }
}
  final TextEditingController _controller = TextEditingController();
  int _deletingIndex = -1;
  bool _undoAvailable = false;

  void _addTask(String task) {
    if (task.trim().isEmpty || _tasks.any((t) => t.task == task.trim())) return;
    setState(() {
      _tasks.insert(0, TodoItem(task.trim()));
    });
    _controller.clear();
  }

  void _markForDeletion(int index) {
    setState(() {
      _tasks[index].isDeleting = true;
      _deletingIndex = index;
      _undoAvailable = true;
    });

    Future.delayed(Duration(seconds: 3), () {
      if (_undoAvailable && _deletingIndex == index) {
        _deleteTask();
      }
    });
  }

  void _deleteTask() {
    setState(() {
      if (_deletingIndex >= 0 && _deletingIndex < _tasks.length) {
        _tasks.removeAt(_deletingIndex);
        _deletingIndex = -1;
        _undoAvailable = false;
      }
    });
  }

  void _undoDelete() {
    setState(() {
      _tasks[_deletingIndex].isDeleting = false;
      _deletingIndex = -1;
      _undoAvailable = false;
    });
  }

void _toggleComplete(int index) {
  setState(() {
    // Remove a tarefa do índice atual
    final task = _tasks.removeAt(index);
    // Alterna o status de completada da tarefa
    task.completed = !task.completed;

    // Encontra o primeiro índice de uma tarefa concluída (se houver)
    int firstCompletedIndex = _tasks.indexWhere((t) => t.completed);

    if (task.completed) {
      // Se a tarefa foi marcada como concluída, coloca-a no topo das concluídas
      if (firstCompletedIndex == -1) {
        _tasks.add(task); // Se não há tarefas concluídas, coloca no final
      } else {
        _tasks.insert(firstCompletedIndex, task); // Coloca antes das concluídas
      }
    } else {
      // Se a tarefa foi desmarcada, coloca-a no final das não concluídas
      if (firstCompletedIndex == -1) {
        _tasks.add(task); // Se não há tarefas concluídas, coloca no final
      } else {
        _tasks.insert(firstCompletedIndex, task); // Coloca antes das concluídas
      }
    }
  });
}

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Lista de Tarefas"),
      ),
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    onSubmitted: (value) => _addTask(value),
                    decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: "Insira uma nova tarefa",
                    ),
                  ),
                ),
                SizedBox(width: 10),
                ElevatedButton(
                  onPressed: () => _addTask(_controller.text),
                  child: Text("Incluir"),
                ),
              ],
            ),
          ),
          Expanded(
            child: Container(
              margin: EdgeInsets.all(10),
              padding: EdgeInsets.all(10),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                border: Border.all(color: Colors.grey),
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey.withOpacity(0.5),
                    spreadRadius: 2,
                    blurRadius: 5,
                    offset: Offset(0, 3),
                  ),
                ],
                color: Colors.white,
              ),
              child: ListView.builder(
                itemCount: _tasks.length,
                itemBuilder: (context, index) {
                  final task = _tasks[index];

                  return Dismissible(
                    key: Key(task.task),
                    confirmDismiss: (direction) async {
                      if (direction == DismissDirection.startToEnd) {
                        _toggleComplete(index);
                        return false;
                      } else if (direction == DismissDirection.endToStart) {
                        _markForDeletion(index);
                        return false;
                      }
                      return false;
                    },
                    background: Container(
                      color: Colors.green,
                      alignment: Alignment.centerLeft,
                      padding: EdgeInsets.only(left: 16),
                      child: Icon(Icons.check, color: Colors.white),
                    ),
                    secondaryBackground: Container(
                      color: Colors.red,
                      alignment: Alignment.centerRight,
                      padding: EdgeInsets.only(right: 16),
                      child: Icon(Icons.delete, color: Colors.white),
                    ),
                    child: Container(
                      color: index % 2 == 0 ? Colors.white : Colors.grey,
                      child: ListTile(
                        contentPadding:
                            EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                        textColor: task.isDeleting
                            ? Colors.red
                            : (index % 2 == 0 ? Colors.black : Colors.black),
                        title: Text(
                          task.task,
                          style: task.completed
                              ? TextStyle(
                                  decoration: TextDecoration.lineThrough,
                                  color: Colors.black)
                              : TextStyle(),
                        ),
                        trailing: task.isDeleting
                            ? Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  TextButton(
                                    onPressed: _undoDelete,
                                    child: Text(
                                      "Desfazer",
                                      style: TextStyle(color: Colors.red),
                                    ),
                                  ),
                                ],
                              )
                            : Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Checkbox(
                                    value: task.completed,
                                    onChanged: (_) => _toggleComplete(index),
                                  ),
                                  IconButton(
                                    icon: Icon(Icons.delete),
                                    onPressed: () => _markForDeletion(index),
                                  ),
                                ],
                              ),
                      ),
                    ),
                  );
                },
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: ElevatedButton(
              onPressed: _saveTasks,
              child: Text("Salvar Tarefas"),
            ),
          ),
        ],
      ),
    );
  }
}