skip to Main Content

I want the radio buttons to be hidden, and only appear when the TextField is focused/selected. I tried Visibility(visible: _textFocus.hasFocus || isEditing, to show the row of radio buttons when I the textField has focus either to create a new task, or editing an existing task. But that didnt work. I also tried putting the row in a container and that does nothing.

import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
//import 'package:testy/page2.dart';

void main() {
  runApp(MaterialApp(
    home: const TodoApp(),
    theme: ThemeData(primarySwatch: Colors.yellow),
  ));
}

class Todo {
  final String text;
  final bool checked;
  int priority;
  static int _tasknumber = 0;
  final int key;
//TODO: UNDERSTAND THIS
  Todo({
    required this.text,
    required this.checked,
    required this.priority,
  }) : key = _tasknumber++;
}

class TodoApp extends StatefulWidget {
  const TodoApp({super.key});

  @override
  State<TodoApp> createState() => _TodoAppState();
}

class _TodoAppState extends State<TodoApp> {
  final _textyController = TextEditingController();
  List<Todo> data = [];
  final FocusNode _textFocus = FocusNode();
  int? _radioValue = 1;
  bool isEditing = false;
  int editingIndex =
      -1; // Initialize as -1 to indicate no active edit initially.

//clear completed tasks
  void deleteComplete() {
    List<Todo> completedTasks = data.where((todo) => todo.checked).toList();
    setState(() {
      data.removeWhere((todo) => todo.checked);
    });

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('${completedTasks.length} completed tasks deleted'),
        duration: const Duration(seconds: 4),
        action: SnackBarAction(
          label: 'Undo',
          onPressed: () {
            setState(() {
              // Re-insert the completed tasks
              data.insertAll(0, completedTasks);
            });
          },
        ),
      ),
    );
  }

//Untick all tasks
  void untickAll() {
    // Create a copy of the tasks before unticking them.
    List<Todo> tasksBeforeUntick = List.from(data);

    setState(() {
      for (int index = 0; index < data.length; index++) {
        if (data[index].checked) {
          data[index] = Todo(
            text: data[index].text,
            checked: false, // Untick the task
            priority: data[index].priority,
          );
        }
      }
    });

    // Show a Snackbar with an "Undo" option.
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('All completed tasks unticked'),
        duration: const Duration(seconds: 4),
        action: SnackBarAction(
          label: 'Undo',
          onPressed: () {
            // Restore the tasks to their previous state.
            setState(() {
              data = List.from(tasksBeforeUntick);
            });
          },
        ),
      ),
    );
  }

//undo deleted tasks
  void deleteTasks(int index, Todo itemToDelete) {
    setState(() {
      data.removeAt(index);
    });

    // Show a SnackBar with an "Undo" action.
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Task deleted'),
        duration: const Duration(seconds: 4),
        action: SnackBarAction(
          label: 'Undo',
          onPressed: () {
            setState(() {
              data.insert(index, itemToDelete);
            });
          },
        ),
      ),
    );
  }

  @override
  Widget build(context) {
    data.sort((a, b) {
      if (a.checked == b.checked) {
        // If checked status is the same, sort by priority
        if (a.priority == b.priority) {
          return 0; // Same priority
        } else if (a.priority < b.priority) {
          return -1; // Sort by ascending priority
        } else {
          return 1; // Sort by descending priority
        }
      } else if (a.checked) {
        return 1; // Move completed tasks to the bottom
      } else {
        return -1; // Keep incomplete tasks at the top
      }
    });

    priorityLabel(int priority) {
      if (priority == 1) {
        return 'High';
      } else if (priority == 2) {
        return 'Medium';
      } else {
        return 'Low';
      }
    }

    Color tileColor(Todo todo) {
      if (todo.checked) {
        // If the task is checked, use a grey color.
        return Colors.grey.withOpacity(0.2);
      } else {
        // If the task is not checked, determine the color based on priority.
        if (todo.priority == 1) {
          return Colors.red[300] ?? Colors.red; // High priority
        } else if (todo.priority == 2) {
          return Colors.orange[300] ?? Colors.yellow; // Medium priority
        } else {
          return Colors.yellow[300] ?? Colors.green; // Low priority
        }
      }
    }

    return Scaffold(
      backgroundColor: Colors.yellow[100],
      appBar: AppBar(
        title: const Text("Todo"),
        centerTitle: true,
        elevation: 0,
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: deleteComplete,
            child: Text('Clear Completed'),
          ),
          ElevatedButton(
            onPressed: untickAll,
            child: Text('Untick All'),
          ),
          Expanded(
            //
            // LIST STARTS
            //
            child: Padding(
              padding:
                  const EdgeInsets.symmetric(vertical: 8.0, horizontal: (15.0)),
              child: ReorderableListView.builder(
                reverse: false,
                itemCount: data.length,
                onReorder: (oldIndex, newIndex) {
                  setState(() {
                    if (oldIndex < newIndex) {
                      newIndex -= 1;
                    }
                    final Todo item = data.removeAt(oldIndex);
                    data.insert(newIndex, item);
                  });
                },
                itemBuilder: (context, index) {
                  if (data.isEmpty) {
                    // Handle the case when the list is empty
                    return const ListTile(
                      title: Text("No tasks to display"),
                    );
                  } else {
                    //
                    //{STYLING} LIST TILE STARTS
                    //
                    return Container(
                      key: ValueKey(data[index].key),
                      padding: const EdgeInsets.symmetric(vertical: 10),
                      child: Slidable(
                          endActionPane: ActionPane(
                            motion: const ScrollMotion(),
                            children: [
                              SlidableAction(
                                backgroundColor: Colors.green,
                                icon: Icons.edit,
                                label: 'Edit',
                                onPressed: (context) => {
                                  setState(() {
                                    isEditing = true;
                                    editingIndex = index;
                                    _textyController.text = data[index].text;
                                    _radioValue = data[index].priority;
                                  }),
                                },
                              ),
                              SlidableAction(
                                backgroundColor: Colors.red[400] ?? Colors.red,
                                icon: Icons.delete_forever,
                                label: 'Delete',
                                onPressed: (context) => {
                                  setState(() {
                                    if (isEditing &&
                                        editingIndex >= 0 &&
                                        editingIndex < data.length) {
                                      isEditing = false;
                                      _textyController.text = '';
                                      _radioValue = 1;
                                    }
                                    deleteTasks(index, data[index]);
                                  }),
                                },
                              ),
                            ],
                          ),
                          child: Container(
                            decoration: BoxDecoration(
                              //borderRadius: BorderRadius.circular(10),
                              color: tileColor(data[index]),
                            ),
                            child: Padding(
                              padding: const EdgeInsets.all(8.0),
                              //
                              //LIST TILE STARTS
                              //
                              child: CheckboxListTile(
                                activeColor: Colors.black,
                                controlAffinity:
                                    ListTileControlAffinity.leading,
                                title: Row(
                                  mainAxisAlignment:
                                      MainAxisAlignment.spaceBetween,
                                  children: [
                                    Expanded(
                                      child: Text(
                                        data[index].text,
                                        style: TextStyle(
                                            color: data[index].checked
                                                ? Colors.grey.withOpacity(0.6)
                                                : Colors.black,
                                            decoration: data[index].checked
                                                ? TextDecoration.lineThrough
                                                : TextDecoration.none),
                                      ),
                                    ),
                                    Text(
                                      data[index].checked ? '' : '${index + 1}',
                                      style: TextStyle(
                                          color: Colors.black.withOpacity(0.1),
                                          fontSize: 21,
                                          fontWeight: FontWeight.w800),
                                    ),
                                  ],
                                ),
                                subtitle:
                                    Text(priorityLabel((data[index].priority))),
                                value: data[index].checked,
                                onChanged: (newCheckedValue) {
                                  setState(() {
                                    data[index] = Todo(
                                      text: data[index].text,
                                      checked: newCheckedValue ?? false,
                                      priority: data[index].priority,
                                    );
                                  });
                                },
                              ),
                            ),
                          )),
                    );
                  }
                },
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 15),
            child: Column(
              children: [
                Visibility(
                  visible: _textFocus.hasFocus || isEditing,
                  child: Container(
                    color: Colors.red,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        Column(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            Radio(
                              value: 1,
                              groupValue: isEditing
                                  ? data[editingIndex].priority
                                  : _radioValue,
                              onChanged: (changedRadio) {
                                setState(() {
                                  if (isEditing) {
                                    data[editingIndex] = Todo(
                                      text: _textyController.text,
                                      checked: data[editingIndex].checked,
                                      priority: changedRadio ?? 1,
                                    );
                                  } else {
                                    _radioValue = changedRadio;
                                  }
                                });
                              },
                            ),
                            Text('High'),
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            Radio(
                              value: 2,
                              groupValue: isEditing
                                  ? data[editingIndex].priority
                                  : _radioValue,
                              onChanged: (changedRadio) {
                                setState(() {
                                  if (isEditing) {
                                    data[editingIndex] = Todo(
                                      text: _textyController.text,
                                      checked: data[editingIndex].checked,
                                      priority: changedRadio ?? 1,
                                    );
                                  } else {
                                    _radioValue = changedRadio;
                                  }
                                });
                              },
                            ),
                            Text('Medium'),
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            Radio(
                              value: 3,
                              groupValue: isEditing
                                  ? data[editingIndex].priority
                                  : _radioValue,
                              onChanged: (changedRadio) {
                                setState(() {
                                  if (isEditing) {
                                    data[editingIndex] = Todo(
                                      text: _textyController.text,
                                      checked: data[editingIndex].checked,
                                      priority: changedRadio ?? 1,
                                    );
                                  } else {
                                    _radioValue = changedRadio;
                                  }
                                });
                              },
                            ),
                            Text('Low'),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
                TextField(
                  focusNode: _textFocus,
                  controller: isEditing
                      ? TextEditingController(text: data[editingIndex].text)
                      : _textyController,
                  onSubmitted: (value) {
                    _textFocus.requestFocus();
                    setState(() {
                      if (isEditing) {
                        data[editingIndex] = Todo(
                          text: value,
                          checked: data[editingIndex].checked,
                          priority: _radioValue ?? 1,
                        );
                        isEditing = false;
                      } else {
                        data.insert(
                            0,
                            Todo(
                              text: value,
                              checked: false,
                              priority: _radioValue ?? 1,
                            ));
                      }
                      _textyController.clear();
                      for (var todo in data) {
                        print(
                            'Text: ${todo.text}, Completed: ${todo.checked}, Priority: ${todo.priority}, Key: ${todo.key}');
                      }
                    });
                  },
                  decoration: const InputDecoration(
                    labelText: '+ Add a task',
                    labelStyle: TextStyle(
                      color: Colors.grey,
                    ),
                    border: OutlineInputBorder(),
                    focusedBorder: OutlineInputBorder(
                      borderSide: BorderSide(color: Colors.black),
                    ),
                    focusColor: Colors.blue,
                  ),
                  cursorColor: Colors.grey[400],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

2

Answers


  1. You can make an additional boolean variable to control the visibility of the Container and listen to the FocusNode changes by using addListener:

    class _TodoAppState extends State<TodoApp> {
      // ...
      bool isContainerVisible = false;
    
      @override
      void initState() {
        // ...
        _textFocus.addListener(() {
          setState(() {
            isContainerVisible = _textFocus.hasFocus;
          });
        });
      }
    
      // ...
    }
    
    Login or Signup to reply.
  2. You should try this and make sure to use TextFromField instead TextField:

    onTapOutside: (event) {
                    FocusManager.instance.primaryFocus?.unfocus();
                    // Here you change your variable value in setState
                    // isEditing = false;
                  },
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search