MouseTracker no longer attaches annotations
Summary
#
                  Removed MouseTracker's methods attachAnnotation,
                  detachAnnotation, and isAnnotationAttached.
                
Context
#
                  Mouse events, such as when a mouse pointer has entered a region,
                  exited, or is hovering over a region, are detected with the help of
                  MouseTrackerAnnotations that are placed on interested regions
                  during the render phase. Upon each update (a new frame or a new event),
                  MouseTracker compares the annotations hovered by the mouse
                  pointer before and after the update, then dispatches
                  callbacks accordingly.
                
                  The MouseTracker class, which manages the state of mouse pointers,
                  used to require MouseRegion to attach annotations when mounted,
                  and detach annotations when unmounted.
                  This was used by MouseTracker to perform the
                  mounted-exit check (for example, MouseRegion.onExit
                  must not be called if the exit was caused by the unmounting
                  of the widget), in order to prevent calling setState
                  of an unmounted widget and throwing exceptions (explained
                  in detail in Issue #44631).
                
                  This mechanism has been replaced by making MouseRegion
                  a stateful widget, so that it can perform the mounted-exit
                  check by itself by blocking the callback when unmounted.
                  Therefore, these methods have been removed, and MouseTracker
                  no longer tracks all annotations on the screen.
                
Description of change
#
                  The MouseTracker class has removed three methods related
                  to attaching annotations:
                
class MouseTracker extends ChangeNotifier {
  // ...
  void attachAnnotation(MouseTrackerAnnotation annotation) {/* ... */}
  void detachAnnotation(MouseTrackerAnnotation annotation) {/* ... */}
  @visibleForTesting
  bool isAnnotationAttached(MouseTrackerAnnotation annotation) {/* ... */}
}
                    
                    
                    
                  
                  RenderMouseRegion and MouseTrackerAnnotation no longer perform the
                  mounted-exit check, while MouseRegion still does.
                
Migration guide
#
                  Calls to MouseTracker.attachAnnotation and
                  detachAnnotation should be removed with little to no impact:
                
- Uses of 
MouseRegionshould not be affected at all. - 
                    If your code directly uses 
RenderMouseRegionorMouseTrackerAnnotation, be aware thatonExitis now called when the exit is caused by events that used to callMouseTracker.detachAnnotation. This should not be a problem if no states are involved, otherwise you might want to add the mounted-exit check, especially if the callback is leaked so that outer widgets might callsetStatein it. For example: 
Code before migration:
class MyMouseRegion extends SingleChildRenderObjectWidget {
  const MyMouseRegion({this.onHoverChange});
  final ValueChanged<bool> onHoverChange;
  @override
  RenderMouseRegion createRenderObject(BuildContext context) {
    return RenderMouseRegion(
      onEnter: (_) { onHoverChange(true); },
      onExit: (_) { onHoverChange(false); },
    );
  }
  @override
  void updateRenderObject(BuildContext context, RenderMouseRegion renderObject) {
    renderObject
      ..onEnter = (_) { onHoverChange(true); }
      ..onExit = (_) { onHoverChange(false); };
  }
}
                    
                    
                    
                  Code after migration:
class MyMouseRegion extends SingleChildRenderObjectWidget {
  const MyMouseRegion({this.onHoverChange});
  final ValueChanged<bool> onHoverChange;
  @override
  RenderMouseRegion createRenderObject(BuildContext context) {
    return RenderMouseRegion(
      onEnter: (_) { onHoverChange(true); },
      onExit: (_) { onHoverChange(false); },
    );
  }
  @override
  void updateRenderObject(BuildContext context, RenderMouseRegion renderObject) {
    renderObject
      ..onEnter = (_) { onHoverChange(true); }
      ..onExit = (_) { onHoverChange(false); };
  }
  @override
  void didUnmountRenderObject(RenderMouseRegion renderObject) {
    renderObject
      ..onExit = onHoverChange == null ? null : (_) {};
  }
}
                    
                    
                    
                  
                  Calls to MouseTracker.isAnnotationAttached must be removed.
                  This feature is no longer technically possible,
                  since annotations are no longer tracked.
                  If you somehow need this feature, please submit an issue.
                
Timeline
#
                  Landed in version: 1.15.4
                  In stable release: 1.17
                
References
#API documentation:
Relevant PRs:
- MouseTracker no longer requires annotations attached, which made the change
 - Improve MouseTracker lifecycle: Move checks to post-frame, which first introduced the mounted-exit change, explained at The change to onExit.
 
Unless stated otherwise, the documentation on this site reflects Flutter 3.35.5. Page last updated on 2025-10-30. View source or report an issue.