Background: I have a Todo app I’m trying to make where I can either save my tasks locally (sqflite) or remotely to firebase. Below is a snapshot of the adding a Todo page.
Problem
I recently added a toggle button as a separate widget and takes the selected button (personal/remote) and returns a boolean via a callback function to my AddTodoScreen.
However, when I click remote and add the task it says that the task is being saved locally and not remotely, and that the boolean for isRemote is false.
But when I checked my callback function its able to set my variable to true. It’s just that when I reference the variable it hasn’t updated despite using setState.
CODE:
callback function
getSaveLocation(bool isRemote) {
setState(() {
remoteTask = isRemote;
print("SET STATE CALLED VALUE OF ISREMOTE: $isRemote");
});
}
Logic for adding the todo
ToggleButton(callbackFunction: getSaveLocation,),
FloatingActionButton.extended(onPressed: ()
{
//creates ToDo item
var todo = Todo(
id: idGenerator(),
title: controllerTask.value.text,
description: controllerDescription.value.text,
isRemote: remoteTask, //THIS SHOULD CHANGE FROM THE CALLBACK
);
print("the value of is remote");
print(todo.isRemote);
//get Todos bloc add new item
context.read<TodosBloc>().add(AddTodo(todo: todo));
Navigator.pop(context);
},
label: const Text('add task',
style: TextStyle(color: Colors.black),
),
FULL CODE: addTodoScreen
class _AddTodoScreen extends State<AddTodoScreen> {
final controllerTask = TextEditingController();
String taskTitle = '';
final controllerDescription = TextEditingController();
String description ='';
bool remoteTask = false; //THIS VARIABLE CHANGES TO TRUE NOT RECOGNIZED
@override
void initState() {
super.initState();
controllerTask.addListener(() => setState(() {}));
controllerDescription.addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
int idGenerator() {
final now = DateTime.now();
return now.microsecondsSinceEpoch;
}
getSaveLocation(bool isRemote) {
setState(() {
remoteTask = isRemote;
print("SET STATE CALLED VALUE OF ISREMOTE: $isRemote");
});
}
return Scaffold(
appBar: AppBar(
title: const Text('BloC Pattern: Add a To Do'),
),
body: BlocListener<TodosBloc, TodosState>(
listener: (context, state) {
// TODO: implement listener
if(state is TodosLoaded) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('added'),
)
);
}
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_inputField('Title', controllerTask),
_inputField('Description', controllerDescription),
//CALLBACK FN CHANGES 'remoteTask' value based on toggle button
ToggleButton(callbackFunction: getSaveLocation,),
FloatingActionButton.extended(
onPressed: () {
//creates ToDo item should take the updated remoteTask value
var todo = Todo(
id: idGenerator(),
title: controllerTask.value.text,
description: controllerDescription.value.text,
isRemote: remoteTask, //WHEN REFERENCED ALWAYS RETURNS FALSE
);
print("the value of is remote");
print(todo.isRemote);
//get Todos bloc add new item
context.read<TodosBloc>().add(AddTodo(todo: todo));
Navigator.pop(context);
},
label: const Text('add task',
style: TextStyle(color: Colors.black),
),
),
],
),
),
),
]
)
),
),
),
);
}
}
CODE: toggle button widget
class ToggleButton extends StatefulWidget {
final Function callbackFunction;
const ToggleButton({Key? key, required this.callbackFunction}) : super(key: key);
@override
ToggleButtonState createState() => ToggleButtonState();
}
class ToggleButtonState extends State<ToggleButton> {
List<bool> isSelected = [true, false];
@override
Widget build(BuildContext context) => Container(
color: Colors.green.withOpacity(.5),
child: ToggleButtons(
fillColor: Colors.lightBlue.shade900,
selectedColor: Colors.white,
isSelected: isSelected,
renderBorder: false,
children: const [
Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Text('Personal',)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Text('Remote',)
),
],
onPressed: (int newIndex){
setState(() {
for(int idx = 0; idx < isSelected.length; idx++ ){
if(idx == newIndex){
isSelected[idx] = true;
}
else{
isSelected[idx] = false;
}
}
//check the first toggle button value
bool isRemote = isSelected[1];
print("Sending this value $isRemote to callback function");
widget.callbackFunction(isSelected[1]);
});
},
),
);
}
I’m fairly certain the problem isn’t from the toggle button because I am getting print outs saying the state changes.
Also willing to take suggestions i.e moving the logic into my BloC or handling the update without a callback. Fairly new to MVVM/Bloc architecture pattern so maybe I should be handling this logic elsewhere or with a new Bloc?
2
Answers
your ToggleButton’s callbackFunction is look like in below code,
I have modified it, Basically if you are passing any values in the callback functions you have to define it’s dataType there for properly working
To fix this is to move the creation of the Todo object inside the onPressed function of the FloatingActionButton. This will ensure that the latest value of remoteTask is used when creating the Todo.
Code: