skip to Main Content

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.

enter image description here

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


  1. 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

    class ToggleButton extends StatefulWidget {
        
        
          final Function(bool) 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]);
                });
              },
            ),
          );
        }
    
    Login or Signup to reply.
  2. 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:

    FloatingActionButton.extended(
      onPressed: () {
        var todo = Todo(
          id: idGenerator(),
          title: controllerTask.value.text,
          description: controllerDescription.value.text,
          isRemote: remoteTask,
        );
    
        context.read<TodosBloc>().add(AddTodo(todo: todo));
    
        Navigator.pop(context);
      },
      label: const Text(
        'add task',
        style: TextStyle(color: Colors.black),
      ),
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search