yadhu
yadhu

Reputation: 75

Why does accessing MediaQuery properties in a Flutter widget trigger rebuilds only in the widget accessing it and not its parent?

I'm working on a Flutter project where I'm accessing MediaQuery properties (e.g., MediaQuery.viewInsetsOf(context).bottom) inside a widget, let's call it Widget2.

Widget2 is a child of Widget1, and I noticed that whenever the value of MediaQuery.viewInsetsOf(context).bottom changes (like when the keyboard is shown or hidden), it triggers the build method of Widget2 but not its parent, Widget1. In other words, only Widget2 rebuilds when the property value changes, not the widgets above it in the tree.

My Question:

Curious how this works under the hood. Any insights into how Flutter optimizes the rebuild process in such cases would be helpful!

Additional Information:

class Widget1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Widget2();
  }
}

class Widget2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double bottomPadding = MediaQuery.viewInsetsOf(context).bottom;
    return Container(
      padding: EdgeInsets.only(bottom: bottomPadding),
      child: Text("Hello World"),
    );
  }
}

Upvotes: 1

Views: 72

Answers (1)

DevQt
DevQt

Reputation: 629

To answer your questions:

  • How does Flutter manage to identify which widget to rebuild when a MediaQuery property changes?
  • Why is only the widget directly accessing the MediaQuery value rebuilt and not its parent or ancestor widgets?
class Widget1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Widget2();
  }
}

class Widget2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    double bottomPadding = MediaQuery.viewInsetsOf(context).bottom; //here where you gave it a context
    return Container(
      padding: EdgeInsets.only(bottom: bottomPadding),
      child: Text("Hello World"),
    );
  }
}

This is my test presentation where I did logging under the two different implementations of didChangeDependencies() methods between Widget1 and Widget2, to find out what widget class is currently mounted in the Widget tree:

import 'package:flutter/material.dart';
import 'dart:developer' as developer;

class Widget1 extends StatefulWidget {
  const Widget1({super.key});

  @override
  State<Widget1> createState() => _Widget1State();
}

class _Widget1State extends State<Widget1> {
  @override
  void didChangeDependencies() {
    if (mounted) {
      developer.log('Current widget in the widget tree in Widget1 class test');
    }
    super.didChangeDependencies();
  }

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

class Widget2 extends StatefulWidget {
  const Widget2({super.key});

  @override
  State<Widget2> createState() => _Widget2State();
}

class _Widget2State extends State<Widget2> {
  @override
  void didChangeDependencies() {
    if (mounted) {
      developer.log('Current widget in the widget tree in Widget2 class test');
    }
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    double bottomPadding = MediaQuery.viewInsetsOf(context).bottom;
    return Scaffold(
      body: Container(
        padding: EdgeInsets.only(bottom: bottomPadding),
        child: SafeArea(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Text(
                "Hello World",
                textDirection: TextDirection.ltr,
              ),
              Container(
                padding: const EdgeInsets.only(top: 8.0),
                child: TextFormField(
                  decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    hintText: 'Put some text',
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Explanations: In a StatefulWidget, have you ever used?

if (mounted) {
  //process related to the currently mounted in the widget tree, using BuildContext
}

You may have already used it. I only mentioned that because we used it when we needed to be sure which class is currently mounted in the widget tree that is using the BuildContext context to prevent exceptions even if you are using asynchronous operations defined in the widget class, to read more: mounted property. It simply verifies whether you are currently mounted in the widget tree, so sometimes you must ensure that the widget class is mounted first before any BuildContext context-parameterized tasks to processed there. The rebuild in Flutter differs from what method you're using such as MediaQuery and where you declared the triggers of the rebuild. In your case, it is because you defined it inside the class:

class Widget2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

Since you are using MediaQuery.viewInsetsOf(context).bottom in the Widget2 class which in your case is currently mounted in the widget tree, and the Flutter team developers most possibly did this to enhance the performance of dealing with the cross-platform processes and keep as straightforward as possible to maintain the granular control over the data and design processes simultaneously at per current widget-case scenario as its default. As long as you are not using asynchronous operation for MediaQuery implementation in your app, then MediaQuery only entertains which currently mounted widget class in the widget tree. Unless you did a special declaration of MediaQuery in the main.dart class like this:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
      MediaQuery(
        data: MediaQueryData.fromView(
                WidgetsBinding.instance.platformDispatcher.views.single)
            .copyWith(textScaler: const TextScaler.linear(1)),
        child: const MyApp(),
      ),
      );
}

This is just a sample code using MediaQuery class to apply in the entire app, and retain the

textScaler: const TextScaler.linear(1)

which prevents the font from being resized when the device adjusts the font size from the device's settings.

I hope that I've broadened your insight into,

How Flutter optimizes the rebuild process in such cases.

Upvotes: 0

Related Questions