Search code examples
flutterdartstate-managementnon-nullable

The non-nullable local variable 'title' must be assigned before it can be used. Flutter error


I want to take user input from the textfield widget and assign in title variable as in this code

import 'package:flutter/material.dart';

class AddTaskScreen extends StatelessWidget {
  final Function callBack; // function

  AddTaskScreen({required this.callBack});

  @override
  Widget build(BuildContext context) {
    String title; // declaring the variable
    return Container(
      height: 350,
      color: Color(0xff757575),
      child: Container(
        padding: EdgeInsets.all(20),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topRight: Radius.circular(20),
            topLeft: Radius.circular(20),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'Add Task',
              textAlign: TextAlign.center,
              style: TextStyle(
                color: Colors.lightBlueAccent,
                fontSize: 24,
              ),
            ),
            SizedBox(
              height: 20,
            ),
            TextField(
              textAlign: TextAlign.center,
              autofocus: false,
              style: TextStyle(
                color: Colors.black,
                fontSize: 15,
              ),
              onChanged: (value) {
                title = value; // taking the user input
              },
              decoration: InputDecoration(
                focusColor: Colors.lightBlueAccent,
                hintText: 'Type a Task',
                hintStyle: TextStyle(
                  color: Colors.grey,
                  fontSize: 12,
                ),
              ),
            ),
            SizedBox(
              height: 20,
            ),
            TextButton(
              onPressed: () {
                callBack(title); // error
              },
              style: TextButton.styleFrom(
                backgroundColor: Colors.lightBlueAccent,
              ),
              child: Text(
                'Add',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 18,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

as you can see there is a function callBack variable. it has the String parameter when I pass title in this function it gives me a warning. here is the code for the callBack when will send this information.

import 'package:flutter/material.dart';
import 'package:todoey/models/Task.dart';
import 'package:todoey/screens/add_task_screen.dart';
import 'package:todoey/screens/bottom_section.dart';
import 'package:todoey/screens/top_section.dart';

class TasksScreen extends StatefulWidget {
  @override
  State<TasksScreen> createState() => _TasksScreenState();
}

class _TasksScreenState extends State<TasksScreen> {
  List<Task> tasks = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showModalBottomSheet(
            context: context,
            builder: (context) => SingleChildScrollView(
              child: Container(
                padding: EdgeInsets.only(
                    bottom: MediaQuery.of(context).viewInsets.bottom),
                child: AddTaskScreen(
                  callBack: (String taskTitle) {
                    setState(() {
                      print(taskTitle);
                      tasks.add(
                        Task(
                          name: taskTitle,
                        ),
                      );
                    });
                    Navigator.pop(context);
                  },
                ).build(context),
              ),
            ),
          );
        },
        child: Icon(
          Icons.add,
          color: Colors.white,
          size: 35,
        ),
        elevation: 5,
        backgroundColor: Colors.lightBlueAccent,
      ),
      backgroundColor: Colors.lightBlueAccent,
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TopSection(),
            SizedBox(
              height: 30,
            ),
            BottomSection(tasks: tasks),
          ],
        ),
      ),
    );
  }
}

for the main class, here is the code.

import 'package:flutter/material.dart';
import 'package:todoey/screens/task_screen.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: TasksScreen(),
    );
  }
}

Solution

  • In flutter, you want to store mutable objects (such as your title) in a State. This is because the build function can theoretically be called many times, you have no control over it. Therefore, storing the variable in the State of an object allows you to keep it "alive" while build is called many times.

    Here is your example, adapted to work as intended:

    import 'package:flutter/material.dart';
    
    class AddTaskScreen extends StatefulWidget {
      final Function callBack; // function
    
      AddTaskScreen({required this.callBack});
    
      @override
      State<AddTaskScreen> createState() => _AddTaskScreenState();
    }
    
    class _AddTaskScreenState extends State<AddTaskScreen> {
      /// Declare title in the state, so that its a long lived object
      var title = ""; // Initialize it (likely as empty)
      
      @override
      Widget build(BuildContext context) {
    
        return Container(
          height: 350,
          color: Color(0xff757575),
          child: Container(
            padding: EdgeInsets.all(20),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(
                topRight: Radius.circular(20),
                topLeft: Radius.circular(20),
              ),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Text(
                  'Add Task',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: Colors.lightBlueAccent,
                    fontSize: 24,
                  ),
                ),
                SizedBox(
                  height: 20,
                ),
                TextField(
                  textAlign: TextAlign.center,
                  autofocus: false,
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 15,
                  ),
                  onChanged: (value) {
                    title = value; // taking the user input
                  },
                  decoration: InputDecoration(
                    focusColor: Colors.lightBlueAccent,
                    hintText: 'Type a Task',
                    hintStyle: TextStyle(
                      color: Colors.grey,
                      fontSize: 12,
                    ),
                  ),
                ),
                SizedBox(
                  height: 20,
                ),
                TextButton(
                  onPressed: () {
                    widget.callBack(title); // error
                  },
                  style: TextButton.styleFrom(
                    backgroundColor: Colors.lightBlueAccent,
                  ),
                  child: Text(
                    'Add',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 18,
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }