Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FlutterError (setState() called after dispose(): (lifecycle state: defunct, not mounted)

Tags:

flutter

dart

the error is thrown in two areas (and the app freezes (when the app is minimized, when phones back button is clicked, or when another app runs on top of the flutter app. Flutter version: 1.20.2 (previous versions did not have this issue): The two functions are:

@override
void initState() {
super.initState();
getItems();
} 

getItems() async {
 initClearVisibility();
 initFilters();
 setState(() {
 loadingItems = true;
 Visibility(visible: true, child: CircularProgressIndicator());
 });

QuerySnapshot querySnapshot = await query.get();
 items = querySnapshot.docs;
 lastDocument = querySnapshot.docs[querySnapshot.docs.length - 1];
 setState(() {
 loadingItems = false;
 Visibility(visible: false, child: CircularProgressIndicator());
 });
}

initClearVisibility() {
 if (Str.filterSelectCategory != Str.CATEGORY) {
  clearCategoryVisible = true;
  allCategoriesVisible = false;
   categoryValue = Str.filterSelectCategory;
  setState(() {});
 }
}

initFilters() async {   
 filterDefaultItems();
}

filterDefaultItems() async {
  query = _firestore
  .collection(Str.ITEMS)
  .where(Str.IS_ITEM_SOLD, isEqualTo: false) 
  .where(Str.ADDRESS, isEqualTo: userAddress1)
  //.orderBy(Str.DATE_POSTED)
  .limit(perPage);
}

Second area is on the following code where I am also getting: :

class FABBottomAppBarItem {
FABBottomAppBarItem({this.iconData, this.itemColor}); //, this.text});
IconData iconData;
var itemColor;
//String text;
}

class FABBottomAppBar extends StatefulWidget {
 FABBottomAppBar({
 this.items,
 this.centerItemText,
 this.height: 65.0,
 this.iconSize: 24.0,
 this.backgroundColor,
 this.color,
 this.selectedColor,
 this.notchedShape,
 this.onTabSelected,
 }) {
 assert(this.items.length == 2 || this.items.length == 4);
}
final List<FABBottomAppBarItem> items;
final String centerItemText;
final double height;
final double iconSize;
final Color backgroundColor;
final Color color;
final Color selectedColor;
final NotchedShape notchedShape;
final ValueChanged<int> onTabSelected;

@override
State<StatefulWidget> createState() => FABBottomAppBarState();
}

class FABBottomAppBarState extends State<FABBottomAppBar> {
//int _selectedIndex = 0;
int unreadCount = 0;

_updateIndex(int index) {
 widget.onTabSelected(index);
 setState(() {
  //_selectedIndex = index;
  });
}

@override
void initState() {
 super.initState();
 countDocuments();
}

@override
Widget build(BuildContext context) {
List<Widget> items = List.generate(widget.items.length, (int index) {
  return _buildTabItem(
    item: widget.items[index],
    index: index,
    onPressed: _updateIndex,
  );
});
items.insert(items.length >> 1, _buildMiddleTabItem());

return BottomAppBar(
  shape: widget.notchedShape,
  child: Row(
    mainAxisSize: MainAxisSize.max,
    mainAxisAlignment: MainAxisAlignment.spaceAround,
    children: items,
  ),
  color: widget.backgroundColor,
 );
}

Widget _buildMiddleTabItem() {
 return Expanded(
  child: SizedBox(
    height: MediaQuery.of(context).size.height * 0.075, //widget.height,
    child: Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        SizedBox(
          height: MediaQuery.of(context).size.height * 0.04,
        ), 
        Text(
          widget.centerItemText ?? '',
          style: TextStyle(
              color: BwerereTheme.bwerereRed,
              fontSize: 14.0,
              fontWeight: FontWeight.w900),
        ),
      ],
    ),
  ),
  );
}

Widget _buildTabItem({
 FABBottomAppBarItem item,
 int index,
 ValueChanged<int> onPressed,
 }) 
{
 return Expanded(
  child: SizedBox(
    height: MediaQuery.of(context).size.height * 0.065,
    child: Material(
      type: MaterialType.transparency,
      child: InkWell(
        onTap: () => onPressed(index),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Stack(
              children: <Widget>[
                Icon(item.iconData,
                    color: item.itemColor,
                    size: IconTheme.of(context).size * 1.2),
                index == 2 ? badge() : Container()
              ],
            )
          ],
        ),
      ),
    ),
   ),
   );
  }

  Widget badge() => unreadCount < 1
   ? Container()
    : Container(
      padding: EdgeInsets.all(4.0),
      decoration: BoxDecoration(
          color: BwerereTheme.bwerereRed, shape: BoxShape.circle),
      child: Center(
        child: RobotoFont(
            text: "$unreadCount",
            textSize: 12.0,
            textColor: Colors.white,
            fontWeight: FontWeight.w400),
      ));

 void countDocuments() async {
 final uid = await FetchUserData().getCurrentUserID();
 QuerySnapshot _myDoc = await FirebaseFirestore.instance
    .collection("userUnreadMessages")
    .doc(uid)
    .collection(Str.MESSAGE_COLLECTION)
    .get();
 List<DocumentSnapshot> _myDocCount = _myDoc.docs;
 setState(() {
  unreadCount = _myDocCount.length;
  print('NOTIY LENGTH::: $unreadCount');
 });
}

}

THE ERROR FROM FRAMEWORK.DART for FABBottomAppBarState.

The same error thrown on the getItems on HomePage()

Exception has occurred. FlutterError (setState() called after dispose(): FABBottomAppBarState#250ac(lifecycle state: defunct, not mounted) 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. 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. 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().)

Further investigation then shows that the app takes about 400MB of memory (Ram) for the phone which I find rather too high.

Help on figuring out the issue will really help. Thanks in advance.

Additional information: Error occurs when running on android 7.0, flutter 1.20.2. See similar/related issue on https://github.com/flutter/flutter/issues/35900. Note that I upgraded to Flutter 1.20.2 adnd Downgrading to 1.7.5 will require a lot of changes I made after upgrading especially on Firestore (NOTE: https://firebase.flutter.dev/docs/migration which was recently updated).

like image 215
ombiro Avatar asked Aug 26 '20 07:08

ombiro


People also ask

How do I dispose of setState?

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.

Does state object live after Dispose ()?

Yes, it's a…

How do you change the state in flutter?

setState method Null safety Notify the framework that the internal state of this object has changed. Whenever you change the internal state of a State object, make the change in a function that you pass to setState: setState(() { _myState = newValue; }); The provided callback is immediately called synchronously.

What is mounted in flutter?

Mounting is the process of creating the state of a StatefulWidget and attaching it to a BuildContext.


Video Answer


2 Answers

After an await, your widget may not be mounted anymore. Doing setState gives you an exception at that time. This is actually a good thing, the code that follows should not be executing anyway, since you are somewhere else.

You have three options about the "setState() called after dispose()" exception:

  1. Safely ignore it. The exception is saving your async function from continuing. You will see an exception in your logs that you can just ignore.
  2. Place a if (!mounted) return; between each await and setState(). It may be a good habit to put it after each await. This also stops the async function and hides the exception, if you are allergic to it.
  3. Replace your setState() calls with setStateIfMounted() and define it as:
void setStateIfMounted(f) {
  if (mounted) setState(f);
}

However, if (mounted) setState() does not stop the async function, so this 3rd option is the worst between the three as discussed here.

I also explain these approaches in this video.

like image 162
Gazihan Alankus Avatar answered Oct 19 '22 15:10

Gazihan Alankus


You can use:

  if (this.mounted) { // check whether the state object is in tree
    setState(() {
      // make changes here
    });
  }

The mounted checks whether Whether this State object is currently in a tree. mounted class

like image 19
V.N Avatar answered Oct 19 '22 16:10

V.N