Search code examples
flutterdartriverpod

Flutter: Transforming a Button into a Task Card Widget Upon Press


I'm developing a to-do list app with Flutter and need assistance in implementing a specific functionality. My app includes a "New Task" button, and I want to transform this button into a task card where users can enter a task's title and description upon pressing it.

Here's the simplified code for my button widget and the part of the UI where the list of tasks is displayed:

to_do_page.dart

class ToDoPage extends ConsumerStatefulWidget {
  const ToDoPage({Key? key}) : super(key: key);

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

class _ToDoPageState extends ConsumerState<ToDoPage> {
  
  bool isActive = false;
  List<Todo> todos = [
    Todo(title: 'Task 1', description: 'Explain note 1'),
    Todo(title: 'Task 2', description: 'Explain note 2'),
    // You can add more tasks here
  ];
  @override
  void dispose() {
    todos.forEach((todo) {
      todo.titleController.dispose();
      todo.descriptionController.dispose();
    });
    super.dispose();
  }

  Widget build(BuildContext context) {
 return Scaffold(

//This is my to-do card where I want to transform the button once the user clicks on 'Add New Task,' and the button transforms into a new to-do card.
             Column(
                        children: todos.map((todo) {
                          return Padding(
                            padding: const EdgeInsets.fromLTRB(23, 8, 23, 16),
                            child: Align(
                              alignment: Alignment.center,
                              child: Glassmorphism(
                                blur: 5,
                                opacity: 0.2,
                                radius: 15,
                                child: Container(
                                  // height: todo.isEditable ? 180 : 112,
                                  padding: const EdgeInsets.all(8),
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceAround,
                                    children: [
                                      Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.spaceBetween,
                                        children: [
                                          Expanded(
                                            child: Container(
                                              width: 
                                                  200,

                                              child: TextField(
                                                controller:
                                                    todo.titleController,
                                                style: GoogleFonts.nunito(
                                                  color:
                                                      const Color(0xffFAFAFA),
                                                  fontSize: 20.0,
                                                  fontWeight: FontWeight.w500,
                                                ),
                                                decoration: InputDecoration(
                                                  border: InputBorder.none,
                                                ),
                                                enabled: todo.isEditable,
                                                onSubmitted: (newTitle) {
                                                  setState(() {
                                                    todo.title = newTitle;
                                                  });
                                                },
                                                maxLines:
                                                    null, // allows textfield to grow as user types
                                                minLines:
                                                    1, // start with one line
                                                keyboardType:
                                                    TextInputType.multiline,
                                              ),
                                            ),
                                          ),
                                          IconButton(
                                            icon: Icon(Icons.edit),
                                            onPressed: () {
                                              setState(() {
                                                todo.isEditable = !todo
                                                    .isEditable; // Toggle the isEditable flag when the edit button is clicked

                                                if (todo.isEditable) {
                                                  todo.originalTitle = todo
                                                      .title; // Store the current title as original
                                                  todo.originalDescription = todo
                                                      .description; // Store the current description as original
                                                }
                                              });
                                            },
                                          ),
                                          IconButton(
                                              icon: Icon(Icons.delete),
                                              onPressed: () {
                                                setState(() {
                                                  todos.remove(todo);
                                                });
                                              }),
                                          SmileFaceCheckbox(
                                              isActive: todo.isActive,
                                              onPress: () {
                                                setState(() {
                                                  todo.isActive =
                                                      !todo.isActive;
                                                });
                                              }),
                                        ],
                                      ),
                                      Container(
                                        width: 200,
                                        child: TextField(
                                          controller:
                                              todo.descriptionController,
                                          style: GoogleFonts.nunito(
                                            color: const Color(0xffFAFAFA),
                                            fontSize: 16.0,
                                          ),
                                          decoration: InputDecoration(
                                            border: InputBorder.none,
                                          ),
                                          onSubmitted: (newDescription) {
                                            setState(() {
                                              todo.description = newDescription;
                                            });
                                          },
                                          maxLines: null,
                                          minLines: 1,
                                          keyboardType: TextInputType.multiline,
                                        ),
                                      ),
                                      if (todo.isEditable)
                                        Row(
                                          mainAxisAlignment:
                                              MainAxisAlignment.spaceAround,
                                          children: [
                                            ElevatedButton(
                                              onPressed: () {
                                                setState(() {
                                                  todo.title =
                                                      todo.titleController.text;
                                                  todo.description = todo
                                                      .descriptionController
                                                      .text;
                                                  todo.isEditable = false;
                                                });
                                              },
                                              child: const Text('Save'),
                                              style: ButtonStyle(
                                                backgroundColor:
                                                    MaterialStateProperty.all<
                                                        Color>(Colors.green),
                                                foregroundColor:
                                                    MaterialStateProperty.all<
                                                        Color>(Colors.white),
                                              ),
                                            ),
                                            ElevatedButton(
                                              onPressed: () {
                                                setState(() {
                                                  todo.titleController.text = todo
                                                      .originalTitle; // Restore the original title
                                                  todo.descriptionController
                                                          .text =
                                                      todo.originalDescription; // Restore the original description
                                                  todo.isEditable = false;
                                                });
                                              },
                                              child: const Text('Cancel'),
                                              style: ButtonStyle(
                                                backgroundColor:
                                                    MaterialStateProperty.all<
                                                        Color>(Colors.red),
                                                foregroundColor:
                                                    MaterialStateProperty.all<
                                                        Color>(Colors.white),
                                              ),
                                            ),
                                          ],
                                        )
                                    ],
                                  ),
                                ),
                              ),
                            ),
                          );
                        }).toList(),
                      ),
// The following code is for the button where I want to implement the transformation into a new todo card, where the user can add a new task.

const Align(
                    alignment: Alignment.bottomCenter,
                    child: Padding(
                      padding: EdgeInsets.all(23.0),
                      child: NewTaskButton(),
                    ),
                  )

Currently, pressing the "Add Task" button does not trigger any transformation or action. I aim to make this button transform into a new task card inline.

Upon pressing the "Add Task" button, it should transform into a new task card within the same UI space, allowing the user to input a task title and description. Ideally, this transition should be smooth, and the newly created task card should seamlessly integrate into the existing list of tasks.

I've considered using a Visibility widget to toggle between the button and a pre-defined task card widget but am unsure how to proceed with the transformation effect or if there's a better approach to achieve this dynamic UI change.

  • How can I implement the transformation of the "Add Task" button into an editable task card widget upon press?
  • Are there any recommended patterns or widgets in Flutter that facilitate this kind of dynamic UI modification? Thank you for any guidance or suggestions!

Solution

  • You can use AnimatedSwitcher Widget to switch between widgets with animations.

    Example Code:

    class AnimatedSwitcherExample extends StatefulWidget {
      const AnimatedSwitcherExample({super.key});
    
      @override
      State<AnimatedSwitcherExample> createState() =>
          _AnimatedSwitcherExampleState();
    }
    
    class _AnimatedSwitcherExampleState extends State<AnimatedSwitcherExample> {
      bool switchChild = true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              setState(
                () {
                  switchChild = !switchChild;
                },
              );
            },
          ),
          body: Center(
            child: AnimatedSwitcher(
              duration: const Duration(milliseconds:100),
              transitionBuilder: (Widget child, Animation<double> animation) {
                return ScaleTransition(
                  scale: animation,
                  child: child,
                );
              },
              child: switchChild
                  ? Container(
                      key: UniqueKey(),
                      color: Colors.pink,
                      height: 50, width: 100,
                      alignment: Alignment.center,
                      child: const Text("Add To DO"),
                    )
                  : Container(
                      key: UniqueKey(),
                      color: Colors.cyan,
                      height: 50, width: 100,
                      alignment: Alignment.center,
                      child: const Text("To Do Added"),
                    ),
            ),
          ),
        );
      }
    }