Error: SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints

813

Just use prepared SliverPersistentHeaderDelegate it works from the box...

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.teal,
        canvasColor: Colors.transparent,
      ),
      home: DemoScreen(),
    );
  }
}

class DemoScreen extends StatelessWidget {
  static String id = '/demo';

  int numberOfColumns(dynamic context) =>
      ((MediaQuery.of(context).size.width - (2 * kBigBoxPadding)) /
              kMaxCrossAxisExtent)
          .floor();

  // Widget _buildGrid() => GridView.extent(
  //     maxCrossAxisExtent: kMaxCrossAxisExtent,
  //     padding: const EdgeInsets.all(4),
  //     mainAxisSpacing: 4,
  //     crossAxisSpacing: 4,
  //     children: _buildGridTileList(500));

  List<Container> _buildGridTileList(dynamic context, int count) =>
      List.generate(
          count,
          (i) => Container(
                //NOTE: workaround according to: https://github.com/flutter/flutter/issues/25009
                decoration: BoxDecoration(
                  color: colorBackground, //the color of the main container
                  border: Border.all(
                    //apply border to only that side where the line is appearing i.e. top | bottom | right | left.
                    width: 4, //depends on the width of the unintended line
                    color: colorBackground,
                  ),
                ),
                child: Container(
                  decoration: BoxDecoration(
                    color: colorBackground,
                  ),

                  child: Center(
                    child: Text(
                      '$i / ${numberOfColumns(context)}',
                      style: TextStyle(color: Colors.grey),
                      //textAlign: TextAlign.center,
                    ),
                  ),
                  //margin: EdgeInsets.all(0),
                ),
              ));

  // List<Widget> tabbarViewItems() {
  //   List<Widget> items = [];
  //   for (int i = 0; i < 25; i++) {
  //     Widget listView = _buildGrid();
  //     items.add(listView);
  //   }
  //   return items;
  // }

  List<Widget> listViewItems() {
    List<Widget> items = [];

    for (int i = 0; i < 500; i++) {
      Widget widgetItem = Text(
        'item $i',
        textAlign: TextAlign.center,
        style: TextStyle(color: Colors.white),
      );
      items.add(widgetItem);
    }
    return items;
  }

  @override
  Widget build(BuildContext context) {
    var topPadding = kCoverHeightProportion *
        kCoverHeightProportion *
        MediaQuery.of(context).size.height /
        (kCoverHeightProportion *
            (MediaQuery.of(context).size.height -
                kBigBoxPadding -
                kBottomBigBoxPadding));
    return DefaultTabController(
      length: 25,
      child: Scaffold(
        backgroundColor: colorBackground,
        //floatingActionButton: MyTabBar(),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
        body: Stack(
          children: [
            Container(
              width: double.infinity,
              color: Colors.amber,
              child: Image.network(
                'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80',
                fit: BoxFit.cover,
                height:
                    MediaQuery.of(context).size.height * kCoverHeightProportion,
              ),
              //color: Colors.green,
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Container(
                height: 40,
                width: 300,
                color: Colors.red,
              ),
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Container(
                margin: EdgeInsets.only(
                    left: kBigBoxPadding,
                    right: kBigBoxPadding,
                    top: kBigBoxPadding,
                    bottom: kBottomBigBoxPadding),

                //width: MediaQuery.of(context).size.width * 0.9,
                //margin: EdgeInsets.symmetric(horizontal: kBigBoxPadding),
                decoration: BoxDecoration(
                  //color: Colors.pink,
                  borderRadius: BorderRadius.all(Radius.circular(30)),
                ),
                child: ClipRRect(
                  borderRadius: BorderRadius.all(Radius.circular(30)),
                  child: CustomScrollView(
                    //physics: FixedExtentScrollPhysics(),
                    anchor: topPadding,
                    slivers: [
                      SliverPadding(
                        padding: EdgeInsets.only(
                          top: topPadding,
                        ),
                        sliver: SliverPersistentHeader(
                          pinned: true,
                          floating: false,
                          delegate: _SliverPersistentHeaderDelegate(
                            Container(
                              width: double.infinity,
                              height: 100,
                              decoration: BoxDecoration(
                                  color: colorBackground,
                                  borderRadius: BorderRadius.only(
                                      topLeft: Radius.circular(30),
                                      topRight: Radius.circular(30))),
                              child: Center(
                                child: Text(
                                  'La casa de don Juan',
                                  style: TextStyle(
                                      color: colorPrimary1,
                                      fontSize: 32,
                                      fontWeight: FontWeight.w800),
                                ),
                              ),
                            ),
                          ),
                        ),
                      ),
                      SliverGrid.extent(
                        maxCrossAxisExtent: kMaxCrossAxisExtent,
                        childAspectRatio: 1,
                        mainAxisSpacing: 0,
                        crossAxisSpacing: 0,
                        children: _buildGridTileList(context, 250),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

const Color colorShade1 = Color(0xFFEFF0F2);
const Color colorShade2 = Color(0xFF777777);
const Color colorShade3 = Color(0xFF424242);
const Color colorShade4 = Color(0xFF4B4935);
const Color colorShade5 = Color(0xFF3D2916);
const Color colorShade6 = Color(0xFF1D1C0A);

const Color colorBackground = Color(0xFF101A24);
const Color colorPrimary1 = Color(0xFFCC9757);

const double kTabIconHeight = 28;
const double kCoverHeightProportion = 0.35;
const double kBigBoxPadding = 8;
const double kBottomBigBoxPadding = 60;
const double kMaxCrossAxisExtent = 150;

class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  _SliverPersistentHeaderDelegate(this.child);

  final Widget child;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return child;
  }

  @override
  double get maxExtent => 100;

  @override
  double get minExtent => 100;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => false;
}
Share:
813
Tomas Baran
Author by

Tomas Baran

Updated on December 23, 2022

Comments

  • Tomas Baran
    Tomas Baran 12 months

    I get this error:

    ════════ Exception caught by rendering library ═════════════════════════════════
    SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints.
    The relevant error-causing widget was
        SliverAppBarLayer
    

    It happens every time I scroll it up. The error is not visible on the screen, only in the console which also prevents me to do hot restart/hot reload. What does it mean? Why does it happen? How to fix it, please?

    screenshot

    enter image description here

    my SliverWidget: sliver_app_bar_layer.dart

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    
    class SliverAppBarLayer extends SingleChildRenderObjectWidget {
      SliverAppBarLayer({Widget child, Key key}) : super(child: child, key: key);
      @override
      RenderObject createRenderObject(BuildContext context) {
        return RenderSliverAppBarLayer();
      }
    }
    
    class RenderSliverAppBarLayer extends RenderSliverToBoxAdapter {
      RenderSliverAppBarLayer({
        RenderBox child,
      }) : super(child: child);
    
      @override
      void performResize() {}
    
      @override
      void performLayout() {
        if (child == null) {
          geometry = SliverGeometry.zero;
          return;
        }
        final SliverConstraints constraints = this.constraints;
        child.layout(constraints.asBoxConstraints(/* crossAxisExtent: double.infinity */), parentUsesSize: true);
        double childExtent;
        switch (constraints.axis) {
          case Axis.horizontal:
            childExtent = child.size.width;
            break;
          case Axis.vertical:
            childExtent = child.size.height;
            break;
        }
        assert(childExtent != null);
        final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: childExtent);
        final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: childExtent);
    
        assert(paintedChildSize.isFinite);
        assert(paintedChildSize >= 0.0);
        geometry = SliverGeometry(
          scrollExtent: 0,
          paintExtent: childExtent,
          paintOrigin: constraints.scrollOffset,
          cacheExtent: cacheExtent,
          maxPaintExtent: childExtent,
          hitTestExtent: paintedChildSize,
        );
        setChildParentData(child, constraints, geometry);
      }
    }
    

    main.dart

    import 'package:flutter/material.dart';
    import 'theme/style_constants.dart';
    import 'widgets/sliver_app_bar_layer.dart';
    
    void main() {
     runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
     @override
     Widget build(BuildContext context) {
       return MaterialApp(
         theme: ThemeData(
           primarySwatch: Colors.teal,
           canvasColor: Colors.transparent,
         ),
         home: DemoScreen(),
       );
     }
    }
    
    class DemoScreen extends StatelessWidget {
     static String id = '/demo';
    
     int numberOfColumns(dynamic context) => ((MediaQuery.of(context).size.width - (2 * kBigBoxPadding)) / kMaxCrossAxisExtent).floor();
    
     // Widget _buildGrid() => GridView.extent(
     //     maxCrossAxisExtent: kMaxCrossAxisExtent,
     //     padding: const EdgeInsets.all(4),
     //     mainAxisSpacing: 4,
     //     crossAxisSpacing: 4,
     //     children: _buildGridTileList(500));
    
     List<Container> _buildGridTileList(dynamic context, int count) => List.generate(
         count,
         (i) => Container(
               //NOTE: workaround according to: https://github.com/flutter/flutter/issues/25009
               decoration: BoxDecoration(
                 color: colorBackground, //the color of the main container
                 border: Border.all(
                   //apply border to only that side where the line is appearing i.e. top | bottom | right | left.
                   width: 4, //depends on the width of the unintended line
                   color: colorBackground,
                 ),
               ),
               child: Container(
                 decoration: BoxDecoration(
                   color: colorBackground,
                 ),
    
                 child: Center(
                   child: Text(
                     '$i / ${numberOfColumns(context)}',
                     style: TextStyle(color: Colors.grey),
                     //textAlign: TextAlign.center,
                   ),
                 ),
                 //margin: EdgeInsets.all(0),
               ),
             ));
    
     // List<Widget> tabbarViewItems() {
     //   List<Widget> items = [];
     //   for (int i = 0; i < 25; i++) {
     //     Widget listView = _buildGrid();
     //     items.add(listView);
     //   }
     //   return items;
     // }
    
     List<Widget> listViewItems() {
       List<Widget> items = [];
    
       for (int i = 0; i < 500; i++) {
         Widget widgetItem = Text(
           'item $i',
           textAlign: TextAlign.center,
           style: TextStyle(color: Colors.white),
         );
         items.add(widgetItem);
       }
       return items;
     }
    
     @override
     Widget build(BuildContext context) {
       return DefaultTabController(
         length: 25,
         child: Scaffold(
           backgroundColor: colorBackground,
           //floatingActionButton: MyTabBar(),
           floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
           body: Stack(
             children: [
               Container(
                 width: double.infinity,
                 color: Colors.amber,
                 child: Image.network(
                   'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80',
                   fit: BoxFit.cover,
                   height: MediaQuery.of(context).size.height * kCoverHeightProportion,
                 ),
                 //color: Colors.green,
               ),
               Align(
                 alignment: Alignment.bottomCenter,
                 child: Container(
                   height: 40,
                   width: 300,
                   color: Colors.red,
                 ),
               ),
               Align(
                 alignment: Alignment.bottomCenter,
                 child: Container(
                   margin: EdgeInsets.only(left: kBigBoxPadding, right: kBigBoxPadding, top: kBigBoxPadding, bottom: kBottomBigBoxPadding),
    
                   //width: MediaQuery.of(context).size.width * 0.9,
                   //margin: EdgeInsets.symmetric(horizontal: kBigBoxPadding),
                   decoration: BoxDecoration(
                     //color: Colors.pink,
                     borderRadius: BorderRadius.all(Radius.circular(30)),
                   ),
                   child: ClipRRect(
                     borderRadius: BorderRadius.all(Radius.circular(30)),
                     child: CustomScrollView(
                       //physics: FixedExtentScrollPhysics(),
                       anchor: kCoverHeightProportion *
                           kCoverHeightProportion *
                           MediaQuery.of(context).size.height /
                           (kCoverHeightProportion * (MediaQuery.of(context).size.height - kBigBoxPadding - kBottomBigBoxPadding)),
                       slivers: [
                         SliverAppBarLayer(
                           child: Container(
                             width: double.infinity,
                             height: 100,
                             decoration: BoxDecoration(
                                 color: colorBackground, borderRadius: BorderRadius.only(topLeft: Radius.circular(30), topRight: Radius.circular(30))),
                             child: Center(
                               child: Text(
                                 'La casa de don Juan',
                                 style: TextStyle(color: colorPrimary1, fontSize: 32, fontWeight: FontWeight.w800),
                               ),
                             ),
                           ),
                         ),
                         SliverGrid.extent(
                           maxCrossAxisExtent: kMaxCrossAxisExtent,
                           childAspectRatio: 1,
                           mainAxisSpacing: 0,
                           crossAxisSpacing: 0,
                           children: _buildGridTileList(context, 250),
                         ),
                       ],
                     ),
                   ),
                 ),
               ),
             ],
           ),
         ),
       );
     }
    }
    

    style_constants.dart

    import 'package:flutter/material.dart';
    
    const Color colorShade1 = Color(0xFFEFF0F2);
    const Color colorShade2 = Color(0xFF777777);
    const Color colorShade3 = Color(0xFF424242);
    const Color colorShade4 = Color(0xFF4B4935);
    const Color colorShade5 = Color(0xFF3D2916);
    const Color colorShade6 = Color(0xFF1D1C0A);
    
    const Color colorBackground = Color(0xFF101A24);
    const Color colorPrimary1 = Color(0xFFCC9757);
    
    const double kTabIconHeight = 28;
    const double kCoverHeightProportion = 0.35;
    const double kBigBoxPadding = 8;
    const double kBottomBigBoxPadding = 60;
    const double kMaxCrossAxisExtent = 150;
    
    
  • Tomas Baran
    Tomas Baran over 3 years
    Wow! Huge thanks @Kherel! Your solution rocks! Not only it solved the issue with the error but the fps went from 0.9 fps - 7.7 fps (avg. ~5 fps) -> 7 fps - 24 fps (avg. ~18 fps). Could you please explain why SliverPersistentHeaderDelegate is the correct solution here instead of extending SingleChildRenderObjectWidget? Why is your solution errorless and SingleChildRenderObjectWidget gives the error and slow frame rate? Un saludo de Las Palmas de Gran Canaria :)
  • Kherel
    Kherel over 3 years
    The main difference in paint methods of RenderSliverSingleBoxAdapter and RenderSliverPersistentHeader. When you are using SingleChildRenderObjectWidget with RenderSliverToBoxAdapter, your are pushing sliver down using paintOrigin. You have to do it on every paint cycle. But when you are are using SliverPersistentHeader and SliverPersistentHeaderDelegate, we need to calucalte offset only if sliver header is moving on the screen.
  • Kherel
    Kherel over 3 years
    After it's find its fixed place, performLayout not trigered anymore and geometry is not changing. And paint method just need to know where it need to be paint, without calculating all overflowed slivers. I'm not 100% sure,, but this is how I understand it. Thanks for the kind words, I've checked some of your articles. Good luck with your projects, and happy codding mi amigo.
  • Tomas Baran
    Tomas Baran over 3 years
    Very valuable insights. Now, it's clear. Thanks a lot for sharing, tío :)