Интеграция провайдера с sqflite в приложении Flutter

Я пытаюсь реализовать шаблон поставщика, но мне сложно интегрировать его с базой данных sqflite. Класс ChangeNotifier получает список строк из базы данных и затем отображает его с помощью ListView. Я полагаю, проблема в том, что когда ListView строит виджет, класс ChangeNotifier еще не инициализировал список, поэтому приложение вылетает. Как я могу это решить?

class FavouritesProvider with ChangeNotifier {

  List<String> _favourites;

  List<String> get favourites => [..._favourites];

  FavouritesProvider() {
    fetchAndSetFav();
  }

  Future<void> fetchAndSetFav() async {
    final data = await DBHelper.instance.getFavourites();
    _favourites = data;
  }

}

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ChangeNotifierProvider(
      create: (_) => FavouritesProvider(),
      child: Container (
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.bottomCenter,
            end: Alignment.topCenter,
            colors: [
              Colors.blue[200],
              Colors.blue
            ],
            stops: [0.0,1]
          )
        ),
        child: Consumer<FavouritesProvider>(
          builder: (context, favouritesProvider, child) => ListView.builder (
                itemCount: favouritesProvider.favourites.length,
                itemBuilder: (context, index) {
                  return Container(
                    padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
                    width: double.maxFinite,
                    child: FavouritePositionWidget(key: new Key(index.toString()), streetName: favouritesProvider.favourites[index])
                  );
                },
          )
        )
      )
      )
    );
  }

это ошибка, которую я получил:

The following NoSuchMethodError was thrown building Consumer<FavouritesProvider>(dirty, dependencies: [_InheritedProviderScope<FavouritesProvider>]):
The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
Future<List<String>> getFavourites() async {
  // Get a reference to the database.
  Database db = await DBHelper.instance.database;
  List<Map> res = await db.query(table, columns: [columnName]);
  List<String> list = [];
  for (Map i in res){
      var str = i.values.toString();
      str=str.substring(1, str.length-1);
      list.add(str);
  }
  return list;
  }

person pz7    schedule 20.11.2020    source источник


Ответы (1)


Перед сборкой необходимо await для fetchAndSetFav():

class FavouritesProvider with ChangeNotifier {

  Future isInitCompleted;
  List<String> _favourites;
  List<String> get favourites => [..._favourites];

  FavouritesProvider() {
   isInitCompleted = fetchAndSetFav();
  }

  Future<void> fetchAndSetFav() async {
    final data = await DBHelper.instance.getFavourites();
    _favourites = data;
  }

}

А также:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ChangeNotifierProvider(
      create: (_) => FavouritesProvider(),
      child: Container (
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.bottomCenter,
            end: Alignment.topCenter,
            colors: [
              Colors.blue[200],
              Colors.blue
            ],
            stops: [0.0,1]
          )
        ),
        child: Consumer<FavouritesProvider>(
          builder: (context, favouritesProvider, child) {
              return FutureBuilder(
                       future: favouritesProvider.isInitCompleted,
                       builder: (context, snapshot) {
                         if (snapshot.hasData) {
                         return ListView.builder(
                                  itemCount: favouritesProvider.favourites.length,
                                  itemBuilder: (context, index) {
                                    return Container(
                                             padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
                                             width: double.maxFinite,
                                             child: FavouritePositionWidget(
                                               key: Key(index.toString()),
                                               streetName: favouritesProvider.favourites[index],
                                                     )
                                            );
                                  },
                                )
                         } else {
                             return Center(
                                      child: CircularProgressIndicator(),
                                    );
                         }
                       },
                     ),
          }
        )
      )
      )
    );
  }
class DBHelper {
  static final _databaseName = "FavDatabase.db";
  static final _databaseVersion = 1;

  static final table = 'favPosition';

  static final columnName = 'street';
  static final columnNot = 'notification';

  DBHelper._privateConstructor();
  static final DBHelper instance = DBHelper._privateConstructor();
  static Database _database;

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

  _initDatabase() async {
  Directory documentsDirectory = await getApplicationDocumentsDirectory();
  String path = join(documentsDirectory.path, _databaseName);
  return await openDatabase(path,
      version: _databaseVersion,
      onCreate: _onCreate);

  }

  // SQL code to create the database table
  Future _onCreate(Database db, int version) async {
    await db.execute('''
          CREATE TABLE $table (
            $columnName TEXT NOT NULL PRIMARY KEY,
            $columnNot BIT(1) NOT NULL
          )
          ''');
    print("db was created");    
  }

  Future<void> insertPos(String street) async {
  
    final Database db = await database;
    final List<Map<String, dynamic>> res = await db.query(table, where: "street = ?", whereArgs: [street]);
    if (!res.isNotEmpty) {
      print("lo deve aggiungere");
      Map<String, dynamic> row = {
        DBHelper.columnName : street,
        DBHelper.columnNot  : 1,
      };
      await db.insert(table, row);
    } else {
      print("gia aggiunt!");
    }
  }

  //Delete a street from the DB
  Future<void> deletePos(String street) async {
    Database db = await DBHelper.instance.database;
    await db.delete(table, where: 'street = ?', whereArgs: [street]);
  }

  //Update the notification boolean 
  Future<void> updateNot(String street, bool value) async {
    // Get a reference to the database.
    Database db = await DBHelper.instance.database;

    Map<String, dynamic> row = {
      DBHelper.columnName : street,
      DBHelper.columnNot  : value?1:0,
    };
    await db.update(table, row, where: '${DBHelper.columnName} = ?', whereArgs: [street]);
  }

  //Return a String list of all street's name in the DB
  Future<List<String>> getFavourites() async {
    // Get a reference to the database.
    Database db = await DBHelper.instance.database;

    List<Map> res = await db.query(table, columns: [columnName]);
    List<String> list = [];
    for (Map i in res){
        var str = i.values.toString();
        str=str.substring(1, str.length-1);
        list.add(str);
    }
    return list;
  }


  Future<bool> getNotification(String street) async {
    // Get a reference to the database.
    Database db = await DBHelper.instance.database;

    List<Map> res = await db.query(table, columns: [columnNot], where: '${DBHelper.columnName} = ?', whereArgs: [street]);
    String value = res[0].values.toString();
    value = value.substring(1, 2);
    if (value == "1") {
      return true;
    } else if (value == "0") {
      return false;
    } else {
      throw FormatException("Value can only be 0 or 1");
    } 
  }

}
person intraector    schedule 20.11.2020
comment
Как я могу это сделать ? - person pz7; 21.11.2020
comment
Он застревает в будущем построителе, ожидая IsInitCompleted. - person pz7; 21.11.2020
comment
Что-то не так с DBHelper.instance.getFavourites(). Покажите, пожалуйста, код. - person intraector; 21.11.2020
comment
Что внутри DBHelper? - person intraector; 21.11.2020
comment
функции для выполнения операций с базой данных sqflite, но до сих пор они работали отлично - person pz7; 21.11.2020
comment
Иногда проблема спрятана в таком месте, которое мы не могли себе представить, и это вполне нормальная ситуация. Покажите, пожалуйста, код. - person intraector; 21.11.2020
comment
Я добавляю код, по ошибке ставлю под вашим ответом. Кстати спасибо за помощь - person pz7; 22.11.2020
comment
Позвольте нам продолжить это обсуждение в чате. - person intraector; 22.11.2020