简体   繁体   中英

Flutter: Unhandled Exception: Null check operator used on a null value

I am practicing with SQFLite database in flutter App. I want to create a database where user can add friend's name and mobile number.

用户界面输出

And after adding this information, that information list will be shown under submit button [ where I using listview.builder() ].

Everything is fine. Data is adding to database. (I checked.db file by manually also). But when I refresh the list it's show me a error like this -

flutter: Done Fetch
[VERBOSE-2:ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value
#0      QueryResultSet.columnIndex (package:sqflite_common/src/collection_utils.dart:114:32)
#1      QueryRow.[] (package:sqflite_common/src/collection_utils.dart:135:40)
#2      new Contact.fromMap (package:sample/model/contact.dart:25:15)
#3      DatabaseHelper.fetchContacts.<anonymous closure> (package:sample/utils/database_helper.dart:63:39)
#4      MappedListIterable.elementAt (dart:_internal/iterable.dart:412:31)
#5      ListIterator.moveNext (dart:_internal/iterable.dart:341:26)
#6      new _GrowableList._ofEfficientLengthIterable (dart:core-patch/growable_array.dart:188:27)
#7      new _GrowableList.of (dart:core-patch/growable_array.dart:150:28)
#8      new List.of (dart:core-patch/array_patch.dart:50:28)
#9      ListIterable.toList (dart:_internal/iterable.dart:212:44)
#10     DatabaseHelper.fetchContacts (package:sample/utils/database_helper.dart:63:51)
<asynchronous suspension>

Data fetch function also run perfectly. (Console show me the print("Done Fetch"); 's output). I think there is a problem in refresh function.

Please someone help me. Where is the problem, and how Can I solve it?

UI Screen =>

import 'package:flutter/material.dart';
.....

class FlutterLocalDatabase extends StatefulWidget {
  @override
  _FlutterLocalDatabaseState createState() => _FlutterLocalDatabaseState();
}

class _FlutterLocalDatabaseState extends State<FlutterLocalDatabase> {
 ....
  Contact _contact = Contact();
  List<Contact> contactList = [];

  DatabaseHelper _dbHelper;
  @override
  void initState() {
    super.initState();
    setState(() {
      _dbHelper = DatabaseHelper.instance;
    });

    refreshContactList();
  }

  @override
  Widget build(BuildContext context) {
    double _width = MediaQuery.of(context).size.width;
    double _height = MediaQuery.of(context).size.height;
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter SQFLite Database"),
      ),
      body: GestureDetector(
        ......
        .....
                ElevatedButton(
                    onPressed: () async {
                      print("${_contact.name}, ${_contact.mobile}");
                      if (validateAndSave(_formKey)) {
                        FocusScope.of(context).requestFocus(new FocusNode());
                        if (_contact.id == null) {
                          await _dbHelper.insertContact(_contact).then((value) {
                            print(value);
                            _resetForm();
                            refreshContactList();
                          });
                        }

                        print(contactList);
                      }
                    },
                    child: Text("Submit")),
                SizedBox(
                  height: 20,
                ),
                Expanded(
                    child: Container(
                  child: ListView.builder(
                      itemCount: contactList.length,
                      itemBuilder: (buildContext, index) {
                        var contact = contactList[index];
                        return Card(
                          child: ListTile(
                            leading: Icon(
                              Icons.account_circle,
                              size: 40,
                              color: Colors.teal,
                            ),
                            title: Text(
                              contact.name,
                              style: TextStyle(
                                color: Colors.teal,
                              ),
                            ),
                            subtitle: Text(contact.mobile,
                                style: TextStyle(
                                  color: Colors.teal,
                                )),
                            trailing: Text("${contact.id}",
                                style: TextStyle(
                                  color: Colors.teal,
                                )),
                          ),
                        );
                      }),
                ))
              ],
            ),
          ),
        ),
      ),
    );
  }

  refreshContactList() async {
    List<Contact> x = await _dbHelper.fetchContacts();
    print("Done Refresh");
    setState(() {
      contactList = x;
    });
  }

  _resetForm() {
    setState(() {
      _formKey.currentState.reset();
      _ctrlName.clear();
      _ctrlMobile.clear();
      _contact.id = null;
    });
  }
}

Model Class =>

class Contact {
  static const tblContact = "contacts";
  static const colId = "id";
  static const colName = "name";
  static const colMobile = "mobile";

  Contact({
    this.id,
    this.name,
    this.mobile,
  });

  int id;
  String name;
  String mobile;

  Map<String, dynamic> toMap() {
    Map map = <String, dynamic>{colName: name, colMobile: mobile};
    if (id != null) map[colId] = id;
    return map;
  }

  Contact.fromMap(Map<String, dynamic> map) {
    id = map[colId];
    name = map[name];
    mobile = map[colMobile];
  }
}

Database Helper Class =>

import 'dart:io';
.....
class DatabaseHelper {
  static const _databaseName = "ContactData.db";
  static const _databaseVersion = 1;

//<====== Singleton Class
  DatabaseHelper._();
  static final DatabaseHelper instance = DatabaseHelper._();

  Database _database;
  Future<Database> get database async {
    if (_database != null) {
      return _database;
    } else {
      _database = await _initDatabase();
      return _database;
    }
  }

//CREATE DATABASE
  _initDatabase() async {
    Directory dataDirectory = await getApplicationDocumentsDirectory();
    String dbPath = join(dataDirectory.path, _databaseName);
    print(dbPath);
    return await openDatabase(dbPath,
        version: _databaseVersion, onCreate: _onCreate);
  }

  //CREATE TABLE
  _onCreate(Database db, int version) async {
    db.execute(''' 
CREATE TABLE ${Contact.tblContact}(
  ${Contact.colId} INTEGER PRIMARY KEY AUTOINCREMENT,
  ${Contact.colName} STRING NOT NULL,
  ${Contact.colMobile} STRING NOT NULL
);
''');
    print("Done on Create");
  }

  //<===================  ADD DATA

  Future<int> insertContact(Contact contact) async {
    Database db = await database;
    print("Done on Insert");
    return await db.insert(Contact.tblContact, contact.toMap());
  }

  //<==================== Read Data
  Future<List<Contact>> fetchContacts() async {
    Database db = await database;
    List<Map> contacts = await db.query(Contact.tblContact);
    print("Done Fetch");
    return contacts.length == 0
        ? []
        : contacts.map((x) => Contact.fromMap(x)).toList();
  }

}

The map index operator is nullable
This means that it can return null if the index is not found therefore you should make your model properties nullable

class Contact {
  ...

  int? id;
  String? name;
  String? mobile;

From Dart documentation:

Sound null safety is available in Dart 2.12 and Flutter 2.

If you enable null safety, variables can't contain null unless you say they can.

You can make a variable nullable by putting a question mark (?) at the end of its type.

So say we are reading "contacts" from a database, and we are expecting a List of Strings, each of which could be null. With null safety you can indicate that a value can accept null with the? operator, which makes a data type nullable (by default no variable can hold a null). Let's further assume that we "know" that all of our names read will not be null. We can assert this with the. operator, Using a loop and exception handling, we will look at each element and test this assertion. and if it proves to be false then the null is replaced by an empty string (or whatever you like).

// without the nullable operator (?), the next line will not compile
// The element type 'Null' can't be assigned to the list type
List<String?> contactsRead = ['Steve', null, 'Tina', 'Aaron', 'Jessica'];
print('Contacts read: $contactsRead\n');
for (int i = 0; i < contactsRead.length; i++) {
    try {
        // without the assertion operator (!) the next line will not compile
        // A value of type 'String?' can't be assigned to a variable of type 'String'
        String contact = contactsRead[i]!;

        // Item was not null, everything fine
        print('Contact ${i + 1} is valid:  $contact');
     } catch (e) {
        // Item was null, let's fix it
        print("An error occurred: e.toString() = '${e.toString()}'");
        print("Its type is: e.runtimeType = ${e.runtimeType}");
        print('Contact ${i + 1} is not valid, we will set to ""');
        print('Contact ${i + 1} is now ${contactsRead[i] = ""}an empty string');
     }
}
print('\n$contactsRead');

Here's the output:

Contacts read: [Steve, null, Tina, Aaron, Jessica]

Contact 1 is valid:  Steve
An error occurred: e.toString() = 'Null check operator used on a null value'
Its type is: e.runtimeType = _CastError
Contact 2 is not valid, we will set to ""
Contact 2 is now an empty string
Contact 3 is valid:  Tina
Contact 4 is valid:  Aaron
Contact 5 is valid:  Jessica

[Steve, , Tina, Aaron, Jessica]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM