ombiro
ombiro

Reputation: 1063

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

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).

Upvotes: 28

Views: 50824

Answers (7)

Rahul Kushwaha
Rahul Kushwaha

Reputation: 6722

Override dispose method and dispose the any open controller/socket etc instance.

 @override
    dispose() {
      controller.dispose(); // you should write before dispose.
      super.dispose();
    }

Upvotes: 0

Awmrit
Awmrit

Reputation: 21

With this code, my error has been solved!

if (mounted) { 
setState(() {
  // make your changes here
});
}

Upvotes: 2

I found there is no that perfect and easy way! I have written a subclass extends the State, you can use VMState instead of State, then just call safeSetState instead of setState.


import 'package:flutter/widgets.dart';

class VMState<T extends StatefulWidget> extends State<T> {

  bool didDispose = false;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    throw UnimplementedError();
  }

  @override
  void dispose() {
    didDispose = true;
    super.dispose();
  }

  void safeSetState(VoidCallback callback) {
    if (!didDispose) {
      setState(callback);
    }
  }
  
}

Someone says to use mounted but that brings another exception.

Upvotes: 1

Rano Paimin
Rano Paimin

Reputation: 199

@override
void setState(VoidCallback fn) {
  if (!mounted) return;
  super.setState(fn);
}

Upvotes: 5

Gazihan Alankus
Gazihan Alankus

Reputation: 11984

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.

Upvotes: 58

Mohsin AR
Mohsin AR

Reputation: 3108

For beginners and easy to understand check bool isMount = true; when dispose() would be called bool isMount = false; and setState() won't be called.

class TotalBooks extends StatefulWidget {
  TotalBooks({Key? key}) : super(key: key);
  // code omitted..
  bool isMount = true;
  @override
  _TotalBooksState createState() => _TotalBooksState();
}

class _TotalBooksState extends State<TotalBooks> {
  @override
  void initState() {
  // code omitted..
      if (widget.isMount) {
        setState(() {
  // code omitted..
        });
      }
    });
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    widget.isMount = false;
  // code omitted..        super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox();
  }
}

Upvotes: 0

timilehinjegede
timilehinjegede

Reputation: 14043

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

Upvotes: 22

Related Questions