error: The non-nullable local variable 'newTaskTitle' must be assigned before it can be used

Viewed 5489

Hi everyone the first error is in the title , let me show the code first :

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todoye_app_angela/models/task_data.dart';

class AddTaskScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    String newTaskTitle;
    return Container(
      color: Color(0xFF757575),
      child: Container(
        padding: EdgeInsets.all(20),
        decoration: BoxDecoration(
          color: Colors.limeAccent[400],
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(60),
            topRight: Radius.circular(60),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'Add Task',
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 30, color: Colors.brown[900]),
            ),
            SizedBox(
              height: 12,
            ),
            TextField(
              onChanged: (newText) {
                newTaskTitle = newText;
              },
              autocorrect: false,
              decoration: InputDecoration(
                enabledBorder: OutlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.brown[800]!,
                    width: 2,
                  ),
                  borderRadius: BorderRadius.circular(30),
                ),
                hintText: 'Type Your Task ...',
                labelStyle: TextStyle(
                  color: Colors.green[900],
                ),
                helperStyle: TextStyle(
                  color: Colors.brown[900],
                ),
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(60),
                  borderSide: BorderSide(
                    color: Colors.brown[900]!,
                    width: 2,
                  ),
                ),
              ),
            ),
            SizedBox(
              height: 12,
            ),
            ElevatedButton(
              onPressed: () {
                Provider.of<TaskData>(context, listen: false)
                    .addTask(newTaskTitle);
                Navigator.pop(context);
              },
              child: Text(
                'Add',
                style: TextStyle(color: Colors.brown[900]),
              ),
              style: ButtonStyle(
                backgroundColor: MaterialStateColor.resolveWith(
                    (states) => Colors.lightGreen),
                elevation: MaterialStateProperty.resolveWith((states) => 6),
                shadowColor:
                    MaterialStateColor.resolveWith((states) => Colors.green),
                minimumSize: MaterialStateProperty.resolveWith(
                    (states) => Size.square(40.67)),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

the error is in the onPress elevated button where i am trying to add this value to "addTask" method ...i have fixed this errro by the change below :

String? newTaskTitle;

Now I get this new error : error: The argument type 'String?' can't be assigned to the parameter type 'String'. it points to this method :

void addTask(String newTaskTitle) {
    _tasks.add(Task(name: newTaskTitle));
    notifyListeners();
  }

i have also fixed it by changing it to this one :

void addTask(String? newTaskTitle) {
    _tasks.add(Task(name: newTaskTitle));
    notifyListeners();
  }

Now I get this new error : error: The argument type 'String?' can't be assigned to the parameter type 'String'. which points to this code :

class Task {
  final String name;
  bool isDone;

  Task({
    required this.name,
    this.isDone = false,
  });

  void toggleDone() {
    isDone = !isDone;
  }
}

which i have also fixed it and here is the change :

class Task {
  final String? name;
  bool isDone;

  Task({
    required this.name,
    this.isDone = false,
  });

  void toggleDone() {
    isDone = !isDone;
  }
}

and now this error again : error: The argument type 'String?' can't be assigned to the parameter type 'String'. it seems like this error loves me !?... which point to this code {this is the whole file }:

class TaskList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // the consumer widget comes from the provider package we simply wrap it around the widget we want to listen to ....we have to specify the data type we want to have access to then...
    return Consumer<TaskData>(
      // then we provide the builder property which takes 3 arguments => 1: context : where we are in the widget tree ? second: it will provide the current data [Provider.of<TaskData>(context).task] and we can give that object a name ..here we named it taskData and at last it takes a property called child.
      builder: (context, taskData, child) {
        // here we return any widget that is needed to built based on this taskData .
        return ListView.builder(
          itemCount: taskData.taskCount,
          itemBuilder: (context, index) {
            return TaskTile(
              taskTitle: taskData.tasks[index].name,
              isChecked: taskData.tasks[index].isDone,
              checkboxCallback: (checkboxState) {
                // setState(() {
                //   Provider.of<TaskData>(context).tasks[index].toggleDone();
                // });
              },
            );
          },
        );
      },
    );
  }
}

it points to this line :

taskTitle: taskData.tasks[index].name,

which gets me to this file :

class TaskTile extends StatelessWidget {
  final String taskTitle;
  final bool isChecked;
  final ValueChanged<bool?> checkboxCallback;

  const TaskTile({
    required this.taskTitle,
    required this.isChecked,
    required this.checkboxCallback,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(
        taskTitle,
        style: TextStyle(
          decoration: isChecked ? TextDecoration.lineThrough : null,
        ),
      ),
      trailing: Checkbox(
        activeColor: Colors.lime,
        value: isChecked,
        onChanged: checkboxCallback,
        // onChanged: toggleCheckBoxState,
      ),
    );
  }
}

this file wants me to change this code :

final String taskTitle;

to this one :

final String? taskTitle;

and at last this code :

title: Text(
        taskTitle,
        style: TextStyle(
          decoration: isChecked ? TextDecoration.lineThrough : null,
        ),
      ),

to this one :

title: Text(
        taskTitle!,
        style: TextStyle(
          decoration: isChecked ? TextDecoration.lineThrough : null,
        ),
      ),

then i hotrestared the app and tried to add a new task to list but i get this error : Null check operator used on a null value and it point to this part of this code :

title: Text(
            taskTitle!,
            style: TextStyle(
              decoration: isChecked ? TextDecoration.lineThrough : null,
            ),
          ),

Null Safety in dart is really confusing ..it makes a loop of confusion that doesn't let the programmer write code in peace .... please help me solve this problem ... I really appreciate it in advance.

3 Answers

just add late keyword before newTaskTitle declaration. as follows.

late String newTaskTitle;

this error is because dart null safety where you cannot asign null type to non-nullable fields.

Instead of using

String? newTaskTitle;

use this

final newTaskTitle = TextEditingController();

and on the TextField change the on change to

controller:newTaskTitle,

then on the onPressed change to this

onPressed: () {
                Provider.of<TaskData>(context, listen: false)
                    .addTask(newTaskTitle.text);
                Navigator.pop(context);
              },

this is the right way to change string in StatelessWidget.

Just add a null check(!) in newtasktitle and it will work

like in my code I added here

onPressed: (){
  Provider.of<TaskData>(context, listen: false).addTask**(newTaskTitle!);**
  Navigator.pop(context);
},
Related