flutter - Flutter-不再显示在小部件树中的小部件,或者此错误可能表示内存泄漏警告

标签 flutter dart

我有3页,我用bottomNavigationBar检查过渡,第一页是Soclose,在此页中,我从数据库中获取信息并在屏幕上打印出来。
我正在从数据库中顺利获取信息,但是当我切换屏幕时,控制台会发出警告消息。控制台中出现错误,但应用程序正常运行。切换画面并返回到旧页面(关闭页面)时,出现错误页面并在几毫秒内消失。
我找不到类似的问题,并且尝试在警告中提出建议,但要么我做不到,要么解决方案不起作用。
enter image description here
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();
}

错误警告会无限循环不断地写入控制台。
为了摆脱警告,我需要关闭应用程序并重新启动模拟器。
警告信息:

E/flutter (30455): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: setState() >called after dispose(): _CountDownItemState#2bbc3(lifecycle state: defunct, not mounted)

E/flutter (30455): This error happens if you call setState() on a State object for a widget that no >longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its >build). This error can occur when code calls setState() from a timer or an animation callback. E/flutter (30455): The preferred solution is to cancel the timer or stop listening to the animation >in the dispose() callback.

Another solution is to check the "mounted" property of this object >before calling setState() to ensure the object is still in the tree.

E/flutter (30455): This error might indicate a memory leak if setState() is being called because >another object is retaining a reference to this State object after it has been removed from the >tree. To avoid memory leaks, consider breaking the reference to this object during 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),
                    );
                  });
            }
          }),
    );
  }

最佳答案

问题在于您的initState函数覆盖。最好在所有其他逻辑之前调用initStatesuper.initState的 super 。您的Future可能完成得太快,甚至在初始化状态之前调用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传递给futureFutureBuilder参数,然后使用AsyncSnapshot提供的builder访问数据。查看有关 FutureBuilder 的更多信息。

关于flutter - Flutter-不再显示在小部件树中的小部件,或者此错误可能表示内存泄漏警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62863526/

相关文章:

json - 在 Flutter 中本地写入和读取 JSON 文件不包括引号

flutter | Dart - NoSuchMethodError : The method 'map' was called on null

flutter - GridView的子级定义为类成员时不会重绘

dart - flutter 日期时间选择器添加开始和结束日期

flutter - 如何在 Flutter 中反转具有不同持续时间的动画

dart - 有没有办法取消 Dart future ?

flutter - 如何在Flutter中完成数据加载后呈现我的小部件

多次调用 Flutter 构建

get - flutter/Dart : get and set with private variables

flutter - 实现 PreferredSizeWidget 以用作 Appbar