Как исправить «оператор проверки null, используемый для нулевого значения» во flutter?

#flutter #dart #flutter-test #dart-null-safety #null-safety

Вопрос:

Я создаю приложение для списка дел. Я написал код и использовал функцию переноса dart для обеспечения нулевой безопасности. Я не знаю, как это исправить, может кто-нибудь, пожалуйста, помогите мне.

Журналы ошибок

Отображается ошибка при запуске приложения на устройстве

Это экран вывода

главная.дротик

 import 'package:flutter/material.dart';
import 'package:todo_list/screens/todo_list_screen.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'TODO List',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: TodoListScreen(),
    );
  }
}
 

database_helpers.dart

 import 'dart:io';

import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:todo_list/models/task_model.dart';

class DatabaseHelper {
  static final DatabaseHelper instance = DatabaseHelper._instance();
  static Database? _db;

  DatabaseHelper._instance();

  String tasksTable = 'task_table';
  String colId = 'id';
  String colTitle = 'title';
  String colDate = 'date';
  String colPriority = 'priority';
  String colStatus = 'status';

  //Task Table
  //id | Title | Date | Priority | Status

  Future<Database?> get db async {
    if (_db == null) {
      _db = await _initDb();
    }
    return _db;
  }

  Future<Database> _initDb() async {
    Directory dir = await getApplicationDocumentsDirectory();
    String path = dir.path   'todo_list.db';
    final todoListDb =
        await openDatabase(path, version: 1, onCreate: _createDb);
    return todoListDb;
  }

  void _createDb(Database db, int version) async {
    await db.execute(
        'CREATE TABLE $tasksTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDate TEXT, $colPriority TEXT, $colStatus INTEGER');
  }

  Future<List<Map<String, dynamic>>> getTaskMapList() async {
    Database db = await (this.db as FutureOr<Database>);
    final List<Map<String, dynamic>> result = await db.query(tasksTable);
    return resu<
  }

  Future<List<Task>> getTaskList() async {
    final List<Map<String, dynamic>> taskMapList = await getTaskMapList();
    final List<Task> taskList = [];
    taskMapList.forEach((taskMap) {
      taskList.add(Task.fromMap(taskMap));
    });
    taskList.sort((taskA, taskB) => taskA.date!.compareTo(taskB.date!));
    return taskList;
  }

  Future<int> insertTask(Task task) async {
    Database db = await (this.db as FutureOr<Database>);
    final int result = await db.insert(tasksTable, task.toMap());
    return resu<
  }

  Future<int> updateTask(Task task) async {
    Database db = await (this.db as FutureOr<Database>);
    final int result = await db.update(
      tasksTable,
      task.toMap(),
      where: '$colId=',
      whereArgs: [task.id],
    );
    return resu<
  }

  Future<int> deleteTask(int? id) async {
    Database db = await (this.db as FutureOr<Database>);
    final int result = await db.delete(
      tasksTable,
      where: '$colId = ',
      whereArgs: [id],
    );
    return resu<
  }
}
 

task_model.dart

 class Task {
  int? id;
  String? title;
  DateTime? date;
  String? priority;
  int? status;

  Task({this.date, this.priority, this.status, this.title});
  Task.withId({this.id, this.date, this.priority, this.status, this.title});

  Map<String, dynamic> toMap() {
    final map = Map<String, dynamic>();
    // if (id = null) {
    //   map['id'] = id;
    // }
    map['id'] = id;
    map['title'] = title;
    map['date'] = date!.toIso8601String();
    map['priority'] = priority;
    map['status'] = status;
    return map;
  }

  factory Task.fromMap(Map<String, dynamic> map) {
    return Task.withId(
      id: map['id'],
      date: DateTime.parse(map['date']),
      priority: map['priority'],
      status: map['status'],
      title: map['title'],
    );
  }
}
 

add_task_screen.dart

 import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:todo_list/helpers/database_helpers.dart';
import 'package:todo_list/models/task_model.dart';

class AddTaskScreen extends StatefulWidget {
  final Function? updateTaskList;
  final Task? task;
  AddTaskScreen({this.task, this.updateTaskList});

  @override
  _AddTaskScreenState createState() => _AddTaskScreenState();
}

class _AddTaskScreenState extends State<AddTaskScreen> {
  final _formKey = GlobalKey<FormState>();
  String? _title = '';
  String? _priority = '';
  DateTime? _date = DateTime.now();
  int? _status = 0;
  TextEditingController _dateController = TextEditingController();

  final DateFormat _dateFormatter = DateFormat('MMM dd, yyyy');
  final List<String> _priorities = ['Low', 'Medium', 'High'];

  @override
  void initState() {
    super.initState();

    _title = widget.task!.title;
    _date = widget.task!.date;
    _priority = widget.task!.priority;
    _status = widget.task!.status;

    _dateController.text = _dateFormatter.format(_date!);
  }

  @override
  void dispose() {
    _dateController.dispose();
    super.dispose();
  }

  _handleDatePicker() async {
    final DateTime? date = await showDatePicker(
      context: context,
      initialDate: _date!,
      firstDate: DateTime.now(),
      lastDate: DateTime(2100),
      // currentDate: DateTime.now(),
    );
    if (date != null amp;amp; date != _date) {
      setState(() {
        _date = date;
      });
      _dateController.text = _dateFormatter.format(date);
    }
  }

  _delete() {
    DatabaseHelper.instance.deleteTask(widget.task!.id);
    widget.updateTaskList!();
    Navigator.pop(context);
  }

  _submit() {
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();

      Task task = Task(
          date: _date, title: _title, priority: _priority, status: _status);

      if (task.status == 0)
        DatabaseHelper.instance.insertTask(task);
      else {
        task.id = widget.task!.id;
        task.status = widget.task!.status;
        DatabaseHelper.instance.updateTask(task);
      }
      widget.updateTaskList!();
      Navigator.pop(context);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: () => FocusScope.of(context).unfocus(),
        child: SingleChildScrollView(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 40, vertical: 80),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                GestureDetector(
                  onTap: () => Navigator.pop(context),
                  child: Icon(
                    Icons.arrow_back_ios_new,
                    size: 30,
                    color: Theme.of(context).primaryColor,
                  ),
                ),
                SizedBox(height: 20),
                Text(
                  widget.task == null ? 'Add Task' : 'Update Task',
                  style: TextStyle(
                    fontSize: 40,
                    fontWeight: FontWeight.bold,
                    color: Colors.black,
                  ),
                ),
                SizedBox(height: 10),
                Form(
                  key: _formKey,
                  child: Column(
                    children: [
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 20),
                        child: TextFormField(
                          style: TextStyle(fontSize: 18),
                          decoration: InputDecoration(
                            labelText: 'Title',
                            labelStyle: TextStyle(fontSize: 18),
                            border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(10),
                            ),
                          ),
                          validator: (input) =>
                              input != null amp;amp; input.trim().isEmpty
                                  ? 'Please enter a task title!'
                                  : null,
                          onSaved: (input) {
                            if (input != null) _title = input;
                          },
                          initialValue: _title,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 20),
                        child: TextFormField(
                          style: TextStyle(fontSize: 18),
                          controller: _dateController,
                          onTap: _handleDatePicker,
                          readOnly: true,
                          decoration: InputDecoration(
                            labelText: 'Date',
                            labelStyle: TextStyle(fontSize: 18),
                            border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(10),
                            ),
                          ),
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 20),
                        child: DropdownButtonFormField(
                          isDense: true,
                          icon: Icon(Icons.arrow_drop_down_circle),
                          iconSize: 22,
                          iconEnabledColor: Theme.of(context).primaryColor,
                          items: _priorities.map((String priority) {
                            return DropdownMenuItem(
                              value: priority,
                              child: Text(
                                priority,
                                style: TextStyle(
                                  color: Colors.black,
                                  fontSize: 18,
                                ),
                              ),
                            );
                          }).toList(),
                          style: TextStyle(fontSize: 18),
                          decoration: InputDecoration(
                            labelText: 'Priority',
                            labelStyle: TextStyle(fontSize: 18),
                            border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(10),
                            ),
                          ),
                          validator: (dynamic input) => _priority == null
                              ? 'Please select a priority level!'
                              : null,
                          onChanged: (dynamic value) {
                            setState(() {
                              _priority = value.toString();
                            });
                          },
                        ),
                      ),
                      Container(
                        margin: EdgeInsets.symmetric(vertical: 20),
                        height: 60,
                        width: double.infinity,
                        decoration: BoxDecoration(
                          color: Theme.of(context).primaryColor,
                          borderRadius: BorderRadius.circular(30),
                        ),
                        child: TextButton(
                          onPressed: _submit,
                          child: Text(
                            widget.task == null ? 'Add' : 'Update',
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 20,
                            ),
                          ),
                        ),
                      ),
                      widget.task != null
                          ? Container(
                              margin: EdgeInsets.symmetric(vertical: 20),
                              height: 60,
                              width: double.infinity,
                              decoration: BoxDecoration(
                                color: Theme.of(context).primaryColor,
                                borderRadius: BorderRadius.circular(30),
                              ),
                              child: TextButton(
                                onPressed: _delete,
                                child: Text(
                                  'Delete',
                                  style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 20,
                                  ),
                                ),
                              ),
                            )
                          : SizedBox(height: 0),
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}
 

todo_list_screen.dart

 import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:todo_list/helpers/database_helpers.dart';
import 'package:todo_list/models/task_model.dart';
import 'package:todo_list/screens/add_task_sreen.dart';

class TodoListScreen extends StatefulWidget {
  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  Future<List<Task>>? _taskList;
  final DateFormat _dateFormatter = DateFormat('MMM dd, yyyy');

  @override
  void initState() {
    super.initState();
    _updateTaskList();
  }

  _updateTaskList() {
    setState(() {
      _taskList = DatabaseHelper.instance.getTaskList();
    });
  }

  Widget _buildTask(Task task) {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 25),
      child: Column(
        children: [
          ListTile(
            title: Text(
              task.title!,
              style: TextStyle(
                  fontSize: 18,
                  decoration: task.status == 0
                      ? TextDecoration.none
                      : TextDecoration.lineThrough),
            ),
            subtitle: Text(
              '${_dateFormatter.format(task.date!)}·${task.priority}',
              style: TextStyle(
                  fontSize: 15,
                  decoration: task.status == 0
                      ? TextDecoration.none
                      : TextDecoration.lineThrough),
            ),
            trailing: Checkbox(
              value: task.status == 1 ? true : false,
              onChanged: (value) {
                // task.status = value ? 1 : 0;
                if (value == false)
                  task.status = 0;
                else
                  task.status = 1;
                DatabaseHelper.instance.updateTask(task);
                _updateTaskList();
              },
              activeColor: Theme.of(context).primaryColor,
            ),
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(
                builder: (_) => AddTaskScreen(
                  updateTaskList: _updateTaskList(),
                  task: task,
                ),
              ),
            ),
          ),
          Divider(),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        backgroundColor: Theme.of(context).primaryColor,
        child: Icon(Icons.add),
        onPressed: () => Navigator.push(
          context,
          MaterialPageRoute(
            builder: (_) => AddTaskScreen(
              updateTaskList: _updateTaskList(),
            ),
          ),
        ),
      ),
      body: FutureBuilder(
        future: _taskList,
        builder: (context, AsyncSnapshot<List<Task>> snapshot) {
          // if (!snapshot.hasData) {
          //   return Center(
          //     child: CircularProgressIndicator(),
          //   );
          // }
          final int completedTaskCount = snapshot.data!
              .where((Task task) => task.status == 1)
              .toList()
              .length;

          return ListView.builder(
            padding: EdgeInsets.symmetric(
              vertical: 80,
            ),
            itemCount: 1   snapshot.data!.length,
            itemBuilder: (BuildContext context, int index) {
              if (index == 0) {
                return Padding(
                  padding: EdgeInsets.symmetric(horizontal: 40, vertical: 20),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'My Tasks',
                        style: TextStyle(
                          color: Colors.black,
                          fontSize: 40,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      SizedBox(
                        height: 10,
                      ),
                      Text(
                        '$completedTaskCount of ${snapshot.data!.length}',
                        style: TextStyle(
                          color: Colors.grey,
                          fontSize: 20,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                    ],
                  ),
                );
              }
              return _buildTask(snapshot.data![index - 1]);
            },
          );
        },
      ),
    );
  }
}
 

Комментарии:

1. Если переменные будут инициализированы позже, рекомендуется использовать поздние переменные . Позволь late Future<List<Task>> _taskList;

Ответ №1:

FutureBuilder разработан таким образом, чтобы забрать Future у вас объект и построить Widget его с помощью builder функции всякий раз, когда его состояние Future меняется.

Поскольку Future s являются асинхронными, ваши данные будут доступны не сразу. Вот где он, возможно, ломается.

Вы использовали, snapshot.data!.where не проверяя, действительно ли данные присутствуют или нет, и поскольку snapshot.data они будут равны нулю до тех пор, пока будущее не будет завершено, у вас будет эта ошибка.

Раскомментируйте этот код, так как именно он выполнял проверку того, действительно ли данные присутствуют или нет.

 // if (!snapshot.hasData) {
//   return Center(
//     child: CircularProgressIndicator(),
//   );
// }
 

Комментарии:

1. Я сделал это , теперь мой CircularProgressIndicator() продолжает работать, и при нажатии на кнопку для добавления задач отображается другая ошибка.

2. Если ваш прогресс не останавливается, это означает, что ваше будущее еще не завершено. должно быть, для получения данных требуется время. Лучше проверьте, сколько времени это занимает и почему это занимает так много времени

3.Консоль отладки Unhandled Exception: type 'Future<Database?>' is not a subtype of type 'FutureOr<Database>' in type cast