Flutter: How to prevent multiple MaterialApp.router builder calls when opening the keyboard?

Viewed 36

I could not find an answer to my question, basically all similar situations are related to rebuilding widgets that use MediaQuery (but not in my case). When I tap on the TextField, the keyboard appears, after which the builder inside the MaterialApp.router is called dozens of times. Is it possible to avoid so many builder calls?

main.dart:

import 'package:flutter/material.dart';
import 'package:routemaster/routemaster.dart';

final routes = RouteMap(
  onUnknownRoute: (route) => const Redirect("/"),
  routes: {
    '/': (_) => const MaterialPage(child: FirstPage()),
  },
);

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      builder: (context, widget) {
        debugPrint("Info: MaterialApp.router Build");
        return widget!;
      },
      routeInformationParser: const RoutemasterParser(),
      routerDelegate: RoutemasterDelegate(
        routesBuilder: (context) {
          debugPrint("Info: Router Build");
          return routes;
        },
      ),
    );
  }
}

class FirstPage extends StatelessWidget {
  const FirstPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    debugPrint("Info: FirstPage Build");
    return Scaffold(
      body: Column(
        children: const [
          Padding(
            padding: EdgeInsets.only(top: 200),
            child: TextField(),
          ),
        ],
      ),
    );
  }
}

Console:

Launching lib/main.dart on iPhone 13-2 in debug mode...
Running Xcode build...
Xcode build done.                                           14,4s
Debug service listening on ws://127.0.0.1:62493/BzKbBCNai2Q=/ws
Syncing files to device iPhone 13-2...
flutter: Info: MaterialApp.router Build
flutter: Info: Router Build
flutter: Info: FirstPage Build
flutter: Info: MaterialApp.router Build // opening keyboard
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
flutter: Info: MaterialApp.router Build
1 Answers

The short answer to your question is "sort of". https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html provides several suggestions for optimizing the build method to reduce the performance impact, which includes ways to reduce the number of calls:

Minimize the number of nodes transitively created by the build method and any widgets it creates. For example, instead of an elaborate arrangement of Rows, Columns, Paddings, and SizedBoxes to position a single child in a particularly fancy manner, consider using just an Align or a CustomSingleChildLayout. Instead of an intricate layering of multiple Containers and with Decorations to draw just the right graphical effect, consider a single CustomPaint widget.

Use const widgets where possible, and provide a const constructor for the widget so that users of the widget can also do so.

Consider refactoring the stateless widget into a stateful widget so that it can use some of the techniques described at StatefulWidget, such as caching common parts of subtrees and using GlobalKeys when changing the tree structure.

If the widget is likely to get rebuilt frequently due to the use of InheritedWidgets, consider refactoring the stateless widget into multiple widgets, with the parts of the tree that change being pushed to the leaves. For example instead of building a tree with four widgets, the inner-most widget depending on the Theme, consider factoring out the part of the build function that builds the inner-most widget into its own widget, so that only the inner-most widget needs to be rebuilt when the theme changes.

When trying to create a reusable piece of UI, prefer using a widget rather than a helper method. For example, if there was a function used to build a widget, a State.setState call would require Flutter to entirely rebuild the returned wrapping widget. If a Widget was used instead, Flutter would be able to efficiently re-render only those parts that really need to be updated. Even better, if the created widget is const, Flutter would short-circuit most of the rebuild work.

The build method is called every time the BuildContext changes at all:

The framework calls this method when this widget is inserted into the tree in a given BuildContext and when the dependencies of this widget change (e.g., an InheritedWidget referenced by this widget changes). This method can potentially be called in every frame and should not have any side effects beyond building a widget. (From https://api.flutter.dev/flutter/widgets/Builder/build.html.)

Based on the two links above, the best way would probably be to make FirstPage() a stateful widget so it can adjust better to the keyboard opening.

Related