skip to Main Content

I have a simple todo app that allows info input into the text field to show up in a list.
However, when I want to tick off an item in the list, instead of ticking that one item, it ticks them all. How do I fix this?

import 'package:flutter/material.dart';

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

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

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

class _TodoAppState extends State<TodoApp> {
  final _textyController = TextEditingController();
  List<String> storedText = [];
  bool checkedValue = false;

  @override
  Widget build(context) {
    return Scaffold(
      backgroundColor: Colors.yellow[100],
      appBar: AppBar(
        title: const Text("Todo"),
        centerTitle: true,
        elevation: 0,
      ),
      body: Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 15.0),
          child: Column(
            children: [
              Expanded(
                //
                // LIST STARTS
                //

                child: ListView.builder(
                  reverse: true,
                  itemCount: storedText.length,
                  //LEARN: UNDERSTAND THIS
                  itemBuilder: (context, index) {
                    //
                    //{STYLING} LIST TILE STARTS
                    //
                    return Padding(
                      padding: const EdgeInsets.symmetric(vertical: 8.0),
                      child: Container(
                        decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(10),
                            color: Colors.yellow),
                        child: Padding(
                          padding: const EdgeInsets.all(8.0),
                          //
                          //LIST TILE STARTS
                          //
                          child: CheckboxListTile(
                              activeColor: Colors.black,
                              controlAffinity: ListTileControlAffinity.leading,
                              //LEARN: UNDERSTAND THIS
                              title: Text(storedText[index]),
                              value: checkedValue,
                              onChanged: (newCheckedValue) {
                                setState(() {
                                  checkedValue = newCheckedValue!;
                                });
                              }),
                        ),
                      ),
                    );
                  },
                ),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 20.0),
                child: TextField(
                  controller: _textyController,
                  onSubmitted: (value) {
                    setState(() {
                      //LEARN: UNDERSTAND THIS
                      storedText.insert(0, value);
                      _textyController.clear();
                    });
                  },
                  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],
                ),
              ),
            ],
          )),
    );
  }
}

I think it might be something do with KEYS, but im not sure? (my next stage is to make this into a ‘Reorderable List’ so I will likely need keys at that point too.

Im completely new to flutter (and programming in general), so if youre able to also explain why, that would be ideal.

2

Answers


  1. You have a single variable in your widgets state:

    bool checkedValue = false;
    

    In your list, you’re using (and setting) that same variable for every item in the list. You need to store a flag for each item, perhaps by changing your storedText list from Strings to a new class that can store both the text and the checked state (or instead of a class you could use records, like List<(String, bool)> – which is a list of String/bool pairs).

    Login or Signup to reply.
  2. Make the following changes:

    1. Add a new list of itemChecked to keep track of the checked state for each task in the list.

    Replace

    bool checkedValue = false;
    

    with

    List<bool> itemChecked = [];
    

    2. In the CheckboxListTile, update the value property. Now, it checks the itemChecked list to determine whether a task is checked or not.

    Replace

    value: checkedValue,
    

    with

    value: itemChecked.isNotEmpty ? itemChecked[index] : false,
    

    3. on click item, onChanged callback of the CheckboxListTile, update the itemChecked list with the new checked value for the specific task.

    Replace

    checkedValue = newCheckedValue!;
    

    with

    itemChecked[index] = newCheckedValue ?? false;
    

    4. On submitting an item using onSubmitted callback of the TextField, you already add a new task to the storedText list, also add state for the item is not checked. Add code after storedText.insert(0, value);

    itemChecked.insert(0, false);
    

    Here is the updated code:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MaterialApp(
        home: const TodoApp(),
        theme: ThemeData(primarySwatch: Colors.yellow),
      ));
    }
    
    class TodoApp extends StatefulWidget {
      const TodoApp({Key? key}) : super(key: key);
    
      @override
      State<TodoApp> createState() => _TodoAppState();
    }
    
    class _TodoAppState extends State<TodoApp> {
      final _textyController = TextEditingController();
      List<String> storedText = [];
      List<bool> itemChecked = []; // 1st modification
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.yellow[100],
          appBar: AppBar(
            title: const Text("Todo"),
            centerTitle: true,
            elevation: 0,
          ),
          body: Padding(
            padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 15.0),
            child: Column(
              children: [
                Expanded(
                  child: ListView.builder(
                    reverse: true,
                    itemCount: storedText.length,
                    itemBuilder: (context, index) {
                      return Padding(
                        padding: const EdgeInsets.symmetric(vertical: 8.0),
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(10),
                            color: Colors.yellow,
                          ),
                          child: Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: CheckboxListTile(
                              activeColor: Colors.black,
                              controlAffinity: ListTileControlAffinity.leading,
                              title: Text(storedText[index]),
                              value: itemChecked.isNotEmpty
                                  ? itemChecked[index]
                                  : false, // 2nd modification
                              onChanged: (newCheckedValue) {
                                setState(() {
                                  itemChecked[index] = newCheckedValue ?? false; // 3rd modification
                                });
                              },
                            ),
                          ),
                        ),
                      );
                    },
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 20.0),
                  child: TextField(
                    controller: _textyController,
                    onSubmitted: (value) {
                      setState(() {
                        storedText.insert(0, value);
                        itemChecked.insert(0, false); // 4th modification
                        _textyController.clear();
                      });
                    },
                    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],
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    Here is the expected result:

    Preview Link: https://dartpad.dev/?id=426729548698c392334a758d7dbc1899

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search