Thanks to visit codestin.com
Credit goes to github.com

Skip to content

OverlayEntry Rebuild is slow #168355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
maheshj01 opened this issue May 6, 2025 · 1 comment
Open

OverlayEntry Rebuild is slow #168355

maheshj01 opened this issue May 6, 2025 · 1 comment
Labels
in triage Presently being triaged by the triage team waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds

Comments

@maheshj01
Copy link
Member

maheshj01 commented May 6, 2025

Steps to reproduce

  1. Run the code sample on the desktop or chrome browser
  2. Resize the window too fast and see that the overlay entry is laggy and at a times fails to update its dimensions

Whenever the window size changes I am getting its dimension and rebuilding overlay entry. But it seems its slow to rerender.

  @override
  void didChangeDependencies() {
    _calculateDimensions();
    if (_overlayEntry != null && _overlayEntry!.mounted) {
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        if (mounted) {
          _overlayEntry!.markNeedsBuild();
        }
      });
    }
    super.didChangeDependencies();
  }

Expected results

I would expect _overlayEntry!.markNeedsBuild(); to be performant to rerender the OverlayEntry smoothly just like how other flutter widgets do.

Actual results

rerender of OverlayEntry is slow and at a times the rerenders are missed.

Code sample

Code sample
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ScrollController _scrollController = ScrollController();

  Widget _list() {
    return Container(
      height: 5 * 40,
      child: Scrollbar(
          controller: _scrollController,
          thumbVisibility: true,
          child: ListView.builder(
              controller: _scrollController,
              padding: EdgeInsets.zero,
              itemCount: 20,
              itemBuilder: (context, index) => ListTile(
                    title: Text('item $index'),
                  ))),
    );
  }

  final LayerLink _layerLink = LayerLink();

  OverlayEntry _createOverlayEntry() {
    return OverlayEntry(
        builder: (context) => Positioned(
              left: searchFieldDimensions.offset!.dx,
              width: searchFieldDimensions.width,
              child: CompositedTransformFollower(
                  offset: Offset(0, 50),
                  link: _layerLink,
                  child: Material(color: Colors.red, child: _list())),
            ));
  }

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        _calculateDimensions();
      }
    });
    super.initState();
  }

  @override
  void didChangeDependencies() {
    // update overlay dimensions on mediaQuery change
    _calculateDimensions();
    if (_overlayEntry != null && _overlayEntry!.mounted) {
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        if (mounted) {
          _overlayEntry!.markNeedsBuild();
        }
      });
    }
    super.didChangeDependencies();
  }

  void _calculateDimensions() {
    if (!mounted || key.currentContext == null) {
      return;
    }

    try {
      final MediaQueryData mediaQuery = MediaQuery.of(context);
      final RenderBox? textFieldRenderBox =
          key.currentContext!.findRenderObject() as RenderBox?;

      if (textFieldRenderBox == null || !textFieldRenderBox.hasSize) {
        return;
      }

      final textFieldsize = textFieldRenderBox.size;
      final offset = textFieldRenderBox.localToGlobal(Offset.zero);

      searchFieldDimensions = SearchFieldDimensions(
        offset: offset,
        height: textFieldsize.height,
        width: textFieldsize.width,
        bottom: mediaQuery.size.height - offset.dy - textFieldsize.height,
        top: offset.dy,
      );
    } catch (e) {
      debugPrint('Error calculating SearchField dimensions: $e');
    }
  }

  OverlayEntry? _overlayEntry;

  var searchFieldDimensions = SearchFieldDimensions();
  final GlobalKey key = GlobalKey();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CompositedTransformTarget(
              link: _layerLink,
              child: TextFormField(
                key: key,
                decoration: InputDecoration(
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(
                      color: Colors.black.withOpacity(0.8),
                    ),
                  ),
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.red),
                  ),
                ),
                onTapOutside: (event) {
                  if (_overlayEntry != null && _overlayEntry!.mounted) {
                    _overlayEntry!.remove();
                    _overlayEntry = null;
                    FocusScope.of(context).unfocus();
                  }
                },
                onTap: () {
                  _overlayEntry?.remove();
                  _overlayEntry = _createOverlayEntry();
                  Overlay.of(context)!.insert(_overlayEntry!);
                },
                onChanged: (query) {},
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class SearchFieldDimensions {
  /// height of the searchfield
  final double? height;

  /// width of the searchfield
  final double? width;

  /// offset of the searchfield
  final Offset? offset;

  /// Space Above the searchfield
  final double? top;

  /// Space Below the searchfield
  ///
  final double? bottom;

  SearchFieldDimensions({
    this.height,
    this.width,
    this.offset,
    this.top,
    this.bottom,
  });
}

Screenshots or Video

Screenshots / Video demonstration
Screen.Recording.2025-05-05.at.23.24.39.mov

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[!] Flutter (Channel master, 3.32.0-1.0.pre.244, on macOS 15.4 24E248 darwin-arm64, locale en-US) [12.4s]
    • Flutter version 3.32.0-1.0.pre.244 on channel master at /Users/mahesh/Development/flutter_master
    ! Warning: `flutter` on your path resolves to /Users/mahesh/Development/flutter/bin/flutter, which is not inside your
      current Flutter SDK checkout at /Users/mahesh/Development/flutter_master. Consider adding
      /Users/mahesh/Development/flutter_master/bin to the front of your path.
    ! Warning: `dart` on your path resolves to /Users/mahesh/Development/flutter/bin/dart, which is not inside your
      current Flutter SDK checkout at /Users/mahesh/Development/flutter_master. Consider adding
      /Users/mahesh/Development/flutter_master/bin to the front of your path.
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 5574a51f56 (11 days ago), 2025-04-24 19:34:07 -0400
    • Engine revision 5574a51f56
    • Dart version 3.9.0 (build 3.9.0-52.0.dev)
    • DevTools version 2.45.0
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to
      perform update checks and upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [3.6s]
    • Android SDK at /Users/mahesh/Library/Android/sdk
    • Emulator version 35.2.10.0 (build_id 12414864) (CL:N/A)
    • Platform android-35, build-tools 35.0.0
    • ANDROID_HOME = /Users/mahesh/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.5+-12932927-b750.29)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.3) [3.3s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16E140
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [134ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.3) [133ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.5+-12932927-b750.29)

[✓] IntelliJ IDEA Community Edition (version 2021.2.1) [130ms]
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 61.2.4
    • Dart plugin version 212.5080.8

[✓] VS Code (version 1.96.2) [18ms]
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.108.0

[✓] Connected device (2 available) [6.5s]
    • macOS (desktop) • macos  • darwin-arm64   • macOS 15.4 24E248 darwin-arm64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 136.0.7103.49

[✓] Network resources [390ms]
    • All expected network resources are available.

! Doctor found issues in 1 category.
@darshankawar darshankawar added the in triage Presently being triaged by the triage team label May 6, 2025
@darshankawar
Copy link
Member

@maheshj01 I came across this article by our very own @huycozy :), which shows responsive layout implementation for OverlayEntry. See if it helps further in your case or not.

@darshankawar darshankawar added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label May 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in triage Presently being triaged by the triage team waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds
Projects
None yet
Development

No branches or pull requests

2 participants