What is the use of didChangeMetrics ? this is in flutter lifecycle state
#flutter#state
Answer
Overview
text
didChangeMetrics()What is didChangeMetrics?
Part of the
text
WidgetsBindingObserver- Keyboard shows/hides
- Screen orientation changes (portrait ↔ landscape)
- Window size changes (resize on desktop/web)
- Safe area insets change (status bar, notch)
Basic Usage
dartclass MyWidget extends StatefulWidget { _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with WidgetsBindingObserver { void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); // Register observer } void dispose() { WidgetsBinding.instance.removeObserver(this); // Unregister observer super.dispose(); } void didChangeMetrics() { super.didChangeMetrics(); print('Metrics changed!'); // Access updated metrics final mediaQuery = MediaQuery.of(context); print('Size: ${mediaQuery.size}'); print('Orientation: ${mediaQuery.orientation}'); print('Keyboard visible: ${mediaQuery.viewInsets.bottom > 0}'); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Metrics Example')), body: Center(child: Text('Hello')), ); } }
Use Case 1: Detect Keyboard Visibility
dartclass KeyboardDetector extends StatefulWidget { _KeyboardDetectorState createState() => _KeyboardDetectorState(); } class _KeyboardDetectorState extends State<KeyboardDetector> with WidgetsBindingObserver { bool _isKeyboardVisible = false; void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } void didChangeMetrics() { super.didChangeMetrics(); final bottomInset = View.of(context).viewInsets.bottom; final isKeyboardVisible = bottomInset > 0; if (_isKeyboardVisible != isKeyboardVisible) { setState(() { _isKeyboardVisible = isKeyboardVisible; }); print(isKeyboardVisible ? 'Keyboard opened' : 'Keyboard closed'); } } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Keyboard Detector')), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(_isKeyboardVisible ? 'Keyboard Open' : 'Keyboard Closed'), TextField( decoration: InputDecoration(labelText: 'Type here'), ), ], ), ); } }
Use Case 2: Hide FloatingActionButton When Keyboard Opens
dartclass HideFABOnKeyboard extends StatefulWidget { _HideFABOnKeyboardState createState() => _HideFABOnKeyboardState(); } class _HideFABOnKeyboardState extends State<HideFABOnKeyboard> with WidgetsBindingObserver { bool _isKeyboardVisible = false; void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } void didChangeMetrics() { super.didChangeMetrics(); final bottomInset = View.of(context).viewInsets.bottom; setState(() { _isKeyboardVisible = bottomInset > 0; }); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hide FAB')), body: TextField( decoration: InputDecation(labelText: 'Type here'), ), floatingActionButton: _isKeyboardVisible ? null // Hide FAB when keyboard is visible : FloatingActionButton( onPressed: () {}, child: Icon(Icons.add), ), ); } }
Use Case 3: Detect Orientation Change
dartclass OrientationDetector extends StatefulWidget { _OrientationDetectorState createState() => _OrientationDetectorState(); } class _OrientationDetectorState extends State<OrientationDetector> with WidgetsBindingObserver { Orientation? _orientation; void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _orientation = MediaQuery.of(context).orientation; } void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } void didChangeMetrics() { super.didChangeMetrics(); final newOrientation = MediaQuery.of(context).orientation; if (_orientation != newOrientation) { setState(() { _orientation = newOrientation; }); print('Orientation changed to: $_orientation'); } } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Orientation Detector')), body: Center( child: Text( _orientation == Orientation.portrait ? 'Portrait' : 'Landscape', style: TextStyle(fontSize: 32), ), ), ); } }
Use Case 4: Adjust UI When Window Resizes (Desktop/Web)
dartclass ResizeDetector extends StatefulWidget { _ResizeDetectorState createState() => _ResizeDetectorState(); } class _ResizeDetectorState extends State<ResizeDetector> with WidgetsBindingObserver { Size? _windowSize; void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _windowSize = MediaQuery.of(context).size; } void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } void didChangeMetrics() { super.didChangeMetrics(); final newSize = MediaQuery.of(context).size; if (_windowSize != newSize) { setState(() { _windowSize = newSize; }); print('Window resized to: ${newSize.width} x ${newSize.height}'); } } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Resize Detector')), body: Center( child: Text( 'Window: ${_windowSize?.width.toInt()} x ${_windowSize?.height.toInt()}', style: TextStyle(fontSize: 24), ), ), ); } }
Metrics Available in MediaQuery
dartvoid didChangeMetrics() { super.didChangeMetrics(); final mediaQuery = MediaQuery.of(context); print('Size: ${mediaQuery.size}'); // Screen size print('Orientation: ${mediaQuery.orientation}'); // Portrait/Landscape print('Device pixel ratio: ${mediaQuery.devicePixelRatio}'); // DPI print('Padding: ${mediaQuery.padding}'); // Safe area insets print('View insets: ${mediaQuery.viewInsets}'); // Keyboard insets print('Text scale factor: ${mediaQuery.textScaleFactor}'); // Font scale }
Other WidgetsBindingObserver Callbacks
text
didChangeMetrics()| Callback | Triggered When |
|---|---|
text | Screen size, orientation, keyboard changes |
text | App goes to background/foreground |
text | Dark mode toggled |
text | Device language changes |
text | Accessibility settings change |
Example:
dartclass MyObserver extends StatefulWidget { _MyObserverState createState() => _MyObserverState(); } class _MyObserverState extends State<MyObserver> with WidgetsBindingObserver { void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } void didChangeMetrics() { print('Metrics changed'); } void didChangeAppLifecycleState(AppLifecycleState state) { print('App state: $state'); } void didChangePlatformBrightness() { print('Brightness changed'); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Observer')), body: Center(child: Text('Observing...')), ); } }
Best Practices
dart// ✅ Always register observer in initState void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } // ✅ Always unregister in dispose void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } // ✅ Use setState to rebuild UI void didChangeMetrics() { super.didChangeMetrics(); setState(() { // Update UI }); } // ❌ Don't access context before widget is built // Use didChangeDependencies() if needed earlier
Summary
| Use Case | Trigger |
|---|---|
| Keyboard visibility | text |
| Orientation change | text |
| Window resize | text |
| Safe area changes | text |
When to use:
- ✅ Detect keyboard show/hide
- ✅ Handle orientation changes
- ✅ Adjust UI on window resize (desktop/web)
- ✅ Respond to safe area changes
Learn more: WidgetsBindingObserver