Search code examples
flutterdartuser-interface

Why does adding this Flutter widget make the app not responsive to mouse clicks?


I'm writing a Flutter app and running it on the desktop. One of its pages has a column with the following widget as the first child:

class HeroBanner extends StatelessWidget {
  String? image;
  String title = "";
  double maxHeight = 400;

  HeroBanner({Key? key, this.image, this.title = "", this.maxHeight = 100});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return image != null
        ? LayoutBuilder(builder: (context, constraints) {
            return ClipRect(
              clipBehavior: Clip.hardEdge,
              child: SizedBox(
                height: maxHeight,
                child: Stack(
                  children: [
                    SizedBox(
                        width: MediaQuery.of(context).size.width,
                        child: FittedBox(
                            fit: BoxFit.cover, 
                            child: Image.asset(image!))),
                    const FadeToBlackFilter(),
                    Align(
                      alignment: Alignment.bottomLeft,
                      child: Padding(
                        padding: comfortable,
                        child: Text(
                          title,
                          overflow: TextOverflow.ellipsis,
                          style:
                              pageTitle(context).copyWith(color: Colors.white),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            );
          })
        : PageTitle(title);
  }
}

Showing this widget makes the app not responsive to mouse events. The widget is correctly shown as far as I can see and the app is still updating (e.g. resizing still updates the widgets). When I check the debug console I see the following:

════════ Exception caught by rendering library ═════════════════════════════════
'package:flutter/src/rendering/box.dart': Failed assertion: line 327 pos 12: 'width > 0.0': is not true.
The relevant error-causing widget was:
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by rendering library ═════════════════════════════════
RenderBox was not laid out: RenderFittedBox#abf19 relayoutBoundary=up22 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
'package:flutter/src/rendering/box.dart':
Failed assertion: line 2165 pos 12: 'hasSize'
The relevant error-causing widget was:
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by scheduler library ═════════════════════════════════
'package:flutter/src/rendering/mouse_tracker.dart': Failed assertion: line 200 pos 12: '!_debugDuringDeviceUpdate': is not true.
════════════════════════════════════════════════════════════════════════════════

I'm at a loss at what could be the culprit.

I've tried wrapping the widget in sized bounds, including Expanded() (which made the whole thing not render at all), SizedBox() with defined width and height (also does not render) and adding a DebugBorder() (a container with red border) to see where the widget's borders are (correctly extends to the width of the screen and finite height only). However, the widget does not seem to be of the height specified (i.e. not 400, and I did not override it).


Solution

  • The first and second errors are related to the fact that you are not setting a height for the SizedBox. Although the error shows width, it is because it checks the width first, and both width and height become 0 at this point. If after correcting this you see the third error, I believe it is related to another piece of code that is not here.

    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Material App',
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Material App Bar'),
            ),
            body: const HeroBanner(
              title: "Hero Banner",
              image: "assets/images/hero.png",
            ),
          ),
        );
      }
    }
    
    class HeroBanner extends StatelessWidget {
      final String? image;
      final String title;
      final double maxHeight;
    
      const HeroBanner(
          {super.key, this.image, this.title = "", this.maxHeight = 100});
    
      @override
      Widget build(BuildContext context) {
        return image != null
            ? LayoutBuilder(builder: (context, constraints) {
                return ClipRect(
                  clipBehavior: Clip.hardEdge,
                  child: SizedBox(
                    height: maxHeight,
                    child: Stack(
                      children: [
                        SizedBox(
                            width: MediaQuery.of(context).size.width,
                            height: maxHeight,  <----------------add height here
                            child: FittedBox(
                                fit: BoxFit.cover, child: Image.asset(image!))),
                        Align(
                          alignment: Alignment.bottomLeft,
                          child: Padding(
                            padding: comfortable,
                            child: Center(
                              child: Text(
                                title,
                                overflow: TextOverflow.ellipsis,
                                style: const TextStyle(
                                    color: Colors.blue, fontSize: 24),
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              })
            : PageTitle(title);
      }
    }
    
    var comfortable = const EdgeInsets.all(16);
    
    class PageTitle extends StatelessWidget {
      const PageTitle(this.title, {super.key});
    
      final String title;
    
      @override
      Widget build(BuildContext context) {
        return Text(
          title,
        );
      }
    }