【问题标题】:SQLite connections returns null in FlutterSQLite 连接在 Flutter 中返回 null
【发布时间】:2018-10-10 13:33:32
【问题描述】:

我在我的应用程序中使用 SQFLite 颤振包。但是,由于与数据库的连接,我一直为空。下面是我在初始化数据库、创建表以及添加和获取项目的方法中使用的代码:

  import 'package:sqflite/sqflite.dart';
  import 'package:path_provider/path_provider.dart';
  import 'dart:io';
  import 'package:path/path.dart';
  import 'dart:async';

  class StoreDBProvider {

       Database db;

       init() async {
          Directory documentsDirectory = await 
          getApplicationDocumentsDirectory();
          final path = join(documentsDirectory.path, "carts.db");
          db = await openDatabase(
               path,
               version: 3,
               onCreate: (Database newDB, int version){
                     newDB.execute('CREATE TABLE Cart (id INTEGER 
                                PRIMARY KEY, name TEXT, price DOUBLE, image TEXT, rating DOUBLE)');
               }
               );
               return db;
        }


        Future<dynamic> fetchItem(int id) async {
             print("DB CONNECTION IS: $db");

             final maps = await db.query(
                          "Cart",
                          columns: null,
                          where: "id = ?",
                          whereArgs: [id],
             );

             print(maps);

             if(maps.length > 0){
                 print("Data exists");
                 return maps.first;
             }

             return null;
        }


        Future<int> addItem(item) async{
             return db.insert("Cart", item);
        }
}  

经过一些调试,我注意到“db”值为空。这是错误消息:

 E/flutter ( 6921): [ERROR:flutter/shell/common/shell.cc(182)] Dart 
 Error: Unhandled exception:
 E/flutter ( 6921): NoSuchMethodError: The method 'insert' was called on null.
 E/flutter ( 6921): Receiver: null
 E/flutter ( 6921): Tried calling: insert("Cart", _LinkedHashMap len:5)
 E/flutter ( 6921): #0      Object.noSuchMethod 
 (dart:core/runtime/libobject_patch.dart:50:5)
 E/flutter ( 6921): #1      StoreDBProvider.addItem 

(file:///Users/Oluwashola/workspace/fashion_style/lib/src/resources/store_db_provider.dart:46:15)

请在此提供任何帮助,我们将不胜感激。谢谢。

【问题讨论】:

  • 在调用addItem之前是否调用了init()方法?
  • 我假设 Flutter 在实例化其类时调用了 init() 方法。此外,不可能通过对象引用调用该方法:例如:storeDBProviderobject.init()。这会出错。
  • 在我的应用程序中,我有这样的方法,名为open(),我必须在每次交易之前调用它。那么,也许您会尝试重命名它并在保存项目之前调用它?
  • 我无法正常调用该方法;如果我尝试使用对象引用在另一个类中调用它,我会收到错误消息“无效的构造函数名称”。我也在我打算使用它的课堂上试过这个,但还是一样。 init(){ setState(() { storeDB.open(); }); }
  • 为什么要放在setState里面?尝试storeDB = StoreDBProvider(),然后是await storeDB.open(),然后是await storeDB.addItem(...)

标签: android sqlite dart flutter


【解决方案1】:

如果有帮助,这是我的工作代码。注意我在方法中使用 db 时为 db 和 await 使用异步 getter。恐怕我也是 Flutter 新手。

注意我通过 Future 与 DatabaseHelper 交互。请参阅第二个代码块。我认为这需要使用FutureBuilder。我怀疑您需要使用它来应对(稍微)缓慢加载异步数据库调用。

class DatabaseHelper {
  final String tableName = "Gear";

  static Database _db;

  Future<Database> get db async {
    if (_db != null) return _db;
    _db = await initDb();
    await importData();
    return _db;
  }

  initDb() async {
    // Get a location using path_provider
    var databasesPath = await getDatabasesPath();
    String path = join(databasesPath, "gear_log.db");

    await deleteDatabase(path);
    var theDb = await openDatabase(path, version: 1,
        onCreate: (Database db, int version) async {
      print('creating db...');

      String sql = await rootBundle.loadString('assets/db/schema.txt');
      for (var s in sql.split(";")) {
        if (s.length > 5) {
          // catching any hidden characters at end of schema
          await db.execute(s + ';');
        }
      }
      // When creating the db, create the table
    });

    return theDb;
  }

  importData() async {
    final datasources =
        'observation_bodyissue.json,observation_observation.json,observation_observation_bodyIssues.json,observation_observation_shoeIssues.json,observation_shoeissue.json,observation_gearissue.json,runner_runner.json,gear_actualpair.json,gear_characteristic.json,gear_shoe.json,gear_family.json,gear_maker.json,gear_gear.json,gear_gear_characteristics.json,users_user.json'
            .split(',');
    var batch = _db.batch();

    for (var datasource in datasources) {
      try {
        String str = await rootBundle.loadString('assets/db/data/$datasource');
        String table = datasource.split('.')[0];

        if (str.length > 0 && str != '[]') {
          List<dynamic> _list = await json.decode(str);
          for (var _json in _list) {
            batch.insert(table, _json);
          }
        }
      } catch (e) {
        print(e.toString());
      }
    }
    print('added db data');
    var results = await batch.commit();
    //print(results);
    //print('imported data');
  }

  Future<List<Item>> getItems() async {
    var dbClient = await db;

    List<Map> list = await dbClient.rawQuery('SELECT *'
        'FROM "gear_actualpair" '
        'DESC, "gear_actualpair"."created" DESC');

    //prob need to specify 'first X' at sometime

    List<Item> items = new List();
    for (int i = 0; i < list.length; i++) {
      Item item = Item.fromMap(list[i]);
      if (list[i]['selected'] == 1) {
        item.observations = await getObservations(list[i]['id']);
      }
      items.add(item);
    }

    return items;
  }
}

与 dbHelper 类交互:

Future<List<Item>> fetchItemsFromDatabase() async {
  var dbHelper = DatabaseHelper();
  Future<List<Item>> items = dbHelper.getItems();
  return items;
}

【讨论】:

  • 安迪,感谢您的时间和精力。我在这里遗漏了两个方面:首先,我没有在方法上调用连接到 DB(这是“db”对象)。其次,当我调用这些方法时,我需要将它们视为异步,因为它们返回 Future 对象。我实现了这两个,现在一切都很好。这里还要提一件事,在这种情况下,在重新创建表时更改代码中的 SQFLite 版本(指定命令时)非常有帮助。
【解决方案2】:

我有这样的通用抽象提供者:

abstract class DbProvider<T extends DbItem> {
  static Future<String> get localPath async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path;
  }

  Database db;

  Future open() async {
    String path = await localPath;
    db = await openDatabase(join(path, file_name), version: 1, onCreate: (database, version) async {
      await database.execute(''' create table */code for creating table/* ''');
    });
  }

  Future _update(T item) async {
    return await db.update(getTableName(), item.toMap(), where: "$col_dbId = ?", whereArgs: [item.dbId]);
  }

  Future _insert(T item) async {
    return await db.insert(getTableName(), item.toMap());
  }

  Future delete(String dbId) async {
    return await db.delete(getTableName(), where: "$col_dbId = ?", whereArgs: [dbId]);
  }

  Future close() => db.close();

  Future remove() => db.delete(getTableName());

  String getTableName();

  Future save(T item);

  Future<Iterable<T>> get();
}

以及进行一些交易:

void loadDbData() async {
  FavoriteProvider favoriteProvider = FavoriteProvider();
  await favoriteProvider.open();
  Iterable<DbItem> favorites = await favoriteProvider.getConferences();
  await favoriteProvider.close();
  ...
}

【讨论】:

  • 感谢您的时间和精力。我在这里遗漏了两个方面:首先,我没有在方法上调用连接到 DB(即“db”对象)。其次,当我调用这些方法时,我需要将它们视为异步,因为它们返回 Future 对象。我实现了这两个,现在一切正常。这里还要提一件事,在这种情况下,在重新创建表时更改代码中的 SQFLite 版本(指定命令时)非常有帮助。
猜你喜欢
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
  • 2020-08-23
  • 2021-09-28
  • 2020-11-10
相关资源
最近更新 更多