Flutter - виджет, который больше не отображается в дереве виджетов, или эта ошибка может указывать на утечку памяти. Предупреждение - PullRequest
1 голос
/ 12 июля 2020

У меня есть 3 страницы. Я проверяю переходы с помощью bottomNavigationBar. Первая страница - это Soclose на этой странице, я получаю информацию из базы данных и распечатываю ее на экране. Я получаю информацию из базы данных плавно, но когда я переключаю экраны, моя консоль выдает предупреждающие сообщения. В консоли появляется ошибка, но приложение работает нормально. При смене экранов и возврате на старую страницу (страницу Soclose) страница ошибки появляется и исчезает в течение миллисекунд.

Я не могу найти похожие вопросы и пытался внести предложения в предупреждения, но либо я не смог это или решения не работают.

enter image description here

введите описание изображения здесь

Связанный файл soclose dart:

class _Closesevents extends State<Soclose> {
  List<Event> eventList;
  int eventListLen;

  @override
  void initState() {
    try{
    final Future<Database> dbFuture = DbHelper.initializeDatabase();
    dbFuture.then((database) {
      Future<List<Event>> eventListFuture = DbHelper().getEventList();
      eventListFuture.then((eventList) {
        setState(() {
          this.eventList = eventList;
          this.eventListLen = eventList.length;
        });
      });
    });}
    catch (e,s)
    {
      print("[ERROR] $e");
      print("[ERROR TREE]\n$s");
    }
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Container(
      child: new ListView.builder(
          itemCount: eventListLen,
          itemBuilder: (BuildContext context, int index) =>
              buildTripCard(context, index)),
    );
  }

  Widget buildTripCard(BuildContext context, int index)
  ...

Файл Databasehelper

import ...

class DbHelper {
  static DbHelper _databaseHelper;    // Singleton DatabaseHelper
  static Database _database;

  static final String _tablename = EventConstants.TABLE_NAME;
  static final String _columnId = EventConstants.COLUMN_ID;
  static final String _columnTitle = EventConstants.COLUMN_TITLE;
  static final String _columnDate = EventConstants.COLUMN_DATE;
  static final String _columnStartTime = EventConstants.COLUMN_STARTTIME;
  static final String _columnFinishTime = EventConstants.COLUMUN_FINISHTIME;
  static final String _columnDesc = EventConstants.COLUMN_DESCRIPTION;
  static final String _columnIsActive = EventConstants.COLUMN_ISACTIVE;

  DbHelper._createInstance(); // Named constructor to create instance of DatabaseHelper

  factory DbHelper() {

    if (_databaseHelper == null) {
      _databaseHelper = DbHelper._createInstance(); // This is executed only once, singleton object
    }
    return _databaseHelper;
  }

  Future<Database> get database async {

    if (_database == null) {
      _database = await initializeDatabase();
    }
    return _database;
  }

  static Future<Database> initializeDatabase() async {
    Directory directory = await getApplicationDocumentsDirectory();
    String path = directory.path + 'takvimapp.db';

    // Open/create the database at a given path
    var notesDatabase = await openDatabase(path, version: 1, onCreate: _createDb);
    return notesDatabase;
  }

  static void _createDb(Database db, int newVersion) async {

    await db.execute('CREATE TABLE $_tablename ( $_columnId INTEGER PRIMARY KEY NOT NULL,$_columnTitle TEXT ,$_columnDate TEXT,$_columnStartTime TEXT,$_columnFinishTime TEXT,$_columnDesc TEXT,$_columnIsActive INTEGER);');
  }

  // Get all events --map
  Future<List<Map<String, dynamic>>> getEventMapList() async {
    Database db = await this.database;
    var result = await db.query(_tablename, orderBy: '$_columnTitle ASC');
    return result;
  }

  // Insert Operation: Insert a Event object to database
  Future<int> insertEvent(Event event) async {
    Database db = await this.database;
    var result = await db.insert(_tablename, event.toMap());
    return result;
  }

  // Update Operation: Update a Event object and save it to database
  Future<int> updateEvent(Event event) async {
    var db = await this.database;
    var result = await db.update(_tablename, event.toMap(), where: '$_columnId = ?', whereArgs: [event.id]);
    return result;
  }

  // Delete Operation: Delete a Event object from database
  Future<int> deleteEvent(int id) async {
    var db = await this.database;
    int result = await db.rawDelete('DELETE FROM $_tablename WHERE $_columnId = $id');
    return result;
  }

  // Get number of Event objects in database
  Future<int> getCount() async {
    Database db = await this.database;
    List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT (*) from $_tablename');
    int result = Sqflite.firstIntValue(x);
    return result;
  }

  // Convert map to list
  Future<List<Event>> getEventList() async {

    var eventMapList = await getEventMapList(); // Get 'Map List' from database
    int count = eventMapList.length;         // Count the number of map entries in db table

    List<Event> eventList = List<Event>();
    // For loop to create a 'Event List' from a 'Event List'
    for (int i = 0; i < count; i++) {
      eventList.add(Event.fromMap(eventMapList[i]));
    }

    return eventList;
  }
  static Future closeDb() => _database.close();
}

Предупреждение об ошибке постоянно записывается в консоль в бесконечном l oop . Чтобы избавиться от предупреждения, мне нужно закрыть приложение и перезапустить эмулятор.

Предупреждение:

E / flutter (30455): [ERROR: flutter / lib / ui / ui_dart_state. cc (157)] Необработанное исключение: setState ()> вызывается после dispose (): _CountDownItemState # 2bbc3 (состояние жизненного цикла: несуществующий, не смонтированный)

E / flutter (30455): Это ошибка возникает, если вы вызываете setState () для объекта State для виджета, который> больше не отображается в дереве виджетов (например, родительский виджет которого больше не включает этот виджет в свою> сборку). Эта ошибка может возникнуть, когда код вызывает setState () из таймера или обратного вызова анимации. E / flutter (30455): предпочтительное решение - отменить таймер или прекратить прослушивание анимации> в обратном вызове dispose ().

Другое решение - проверить свойство «смонтировано» этого объекта> перед вызов setState (), чтобы убедиться, что объект все еще находится в дереве.

E / flutter (30455): эта ошибка может указывать на утечку памяти, если вызывается setState (), потому что> другой объект сохраняет ссылку на этот объект состояния после того, как он был удален из дерева>. Чтобы избежать утечек памяти, рассмотрите возможность разрыва ссылки на этот объект во время dispose ().

Решение:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: _db.getEventList(),
          builder: (context, snapshot) {
            if (snapshot.data == null) {
              return Container(
                child: Text("Loading....."),
              );
            } else {
              return ListView.builder(
                  itemCount: snapshot.data.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      title: Text(snapshot.data[index].title),
                    );
                  });
            }
          }),
    );
  }

1 Ответ

3 голосов
/ 12 июля 2020

Проблема заключается в переопределении вашей функции initState. Рекомендуется также вызывать super из initState, super.initState перед всеми другими logi c. Ваш Future s может завершаться слишком быстро и вызывать setState до того, как состояние будет даже инициализировано. Просто переместите super.initState(); в качестве первого оператора переопределения. Пример.

@override
void initState() {
  super.initState();//Always call this first

  try{
    final Future<Database> dbFuture = DbHelper.initializeDatabase();
    dbFuture.then((database) {
      Future<List<Event>> eventListFuture = DbHelper().getEventList();
      eventListFuture.then((eventList) {
        setState(() {
          this.eventList = eventList;
          this.eventListLen = eventList.length;
        });
      });
    });}
    catch (e,s)
    {
      print("[ERROR] $e");
      print("[ERROR TREE]\n$s");
    }
}

Изменить: Однако это все еще может привести к ошибкам, поскольку setState все еще может быть вызван до монтирования виджета. Вот почему существует виджет FutureBuilder. Оберните виджет, которому требуются эти Future данные, в свой метод build, передайте Future параметру future из FutureBuilder и получите доступ к данным с помощью AsyncSnapshot, предоставляемого builder. Подробнее о FutureBuilder.

...