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.
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"),
),
),
),
);
}
}