24

I'm trying to use ClipRect with a Column inside it, but it doesn't seem to work well.

What I'd like to achieve is to clip the column's content and to show a text message if there is an overflow (if the column's content cannot be displayed within the available space).

Do you have any suggestions how I can make it happen?

import 'package:flutter/material.dart';

void main() => runApp(ContentOverflowDetectionApp());

class ContentOverflowDetectionApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Overflow detection"),
        ),
        body: Stack(
          fit: StackFit.expand,
          children: [
            ClipRect(
              child: Column(
                children: [
                  Container(
                    width: 300,
                    height: 400,
                    color: Colors.green[200],
                    child: Text('first widget'),
                  ),
                  Container(
                    width: 350,
                    height: 350,
                    color: Colors.yellow[200],
                    child: Text('overflowed widget'),
                  ),
                ],
              ),
            ),
            Positioned(
              child: Align(
                alignment: FractionalOffset.bottomCenter,
                child: Text("SHOW THIS TEXT ONLY IF CONTENT HAS OVERFLOWED."),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Sean Lintern
  • 3,103
  • 22
  • 28
pasul
  • 1,182
  • 2
  • 12
  • 20
  • 1
    You can use the "SingleChildScrollView" instead of "Column " –  Mar 11 '19 at 05:04
  • 2
    I'm afraid I don't need scrolling. Also I didn't quite understand how "SingleChildScrollView" can be used instead if "Column" in the example taking into account that it supports child not children as the column does. – pasul Mar 11 '19 at 08:54

2 Answers2

15

You shouldn't use ClipRect for your goals. Please try to add clipBehavior parameter with the value Clip.antiAlias to your Stack widget.

import 'package:flutter/material.dart';

void main() => runApp(ContentOverflowDetectionApp());

class ContentOverflowDetectionApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Overflow detection"),
        ),
        body: Stack(
          fit: StackFit.expand,
          clipBehavior: Clip.antiAlias,
          children: [
            Positioned(
              top: 0,
              child: Column(
                children: [
                  Container(
                    width: 300,
                    height: 400,
                    color: Colors.green[200],
                    child: Text('first widget'),
                  ),
                  Container(
                    width: 350,
                    height: 350,
                    color: Colors.yellow[200],
                    child: Text('overflowed widget'),
                  ),
                ],
              ),
            ),
            Positioned(
              child: Align(
                alignment: FractionalOffset.bottomCenter,
                child: Text("SHOW THIS TEXT ONLY IF CONTENT HAS OVERFLOWED."),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

This solution just fixes a clipping.

How to get height of a Widget? gives answer how to check widget height and add Text with a message about overflow

Michal Šrůtek
  • 1,647
  • 16
  • 17
2

For those who want a "height limiter" (a widget which limits the height of its children, and if the child wants to be too high, show some hint), here is my code snippet that you can copy-and-paste:

class HeightLimiter extends StatefulWidget {
  final Widget child;
  final double maxHeight;
  final double fadeEffectHeight;

  const HeightLimiter({Key key, this.maxHeight, this.child, this.fadeEffectHeight = 72}) : super(key: key);

  @override
  _HeightLimiterState createState() => _HeightLimiterState();
}

class _HeightLimiterState extends State<HeightLimiter> {
  var _size = Size.zero;

  @override
  Widget build(BuildContext context) {
    return ConstrainedBox(
      constraints: BoxConstraints(
        maxHeight: widget.maxHeight,
      ),
      child: Stack(
        clipBehavior: Clip.hardEdge,
        children: [
          Positioned(
            top: 0,
            left: 0,
            right: 0,
            child: MeasureSize(
              onChange: (size) => setState(() => _size = size),
              child: widget.child,
            ),
          ),
          if (_size.height >= widget.maxHeight)
            Positioned(
              bottom: 0,
              left: 0,
              width: _size.width,
              child: _buildOverflowIndicator(),
            ),
        ],
      ),
    );
  }

  Widget _buildOverflowIndicator() {
    return Container(
      height: widget.fadeEffectHeight,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.bottomCenter,
          end: Alignment.topCenter,
          colors: [
            Colors.white.withAlpha(200),
            Colors.white.withAlpha(0),
          ],
          tileMode: TileMode.clamp,
        ),
      ),
    );
  }
}

And the MeasureSize just comes from https://stackoverflow.com/a/60868972/4619958.

Result looks like the following (the green children overflow the blue parent) when child too high; When child is short enough, nothing special is shown.

enter image description here

Thanks @Aleksandr and @Dpedrinha for their answer!

ch271828n
  • 15,854
  • 5
  • 53
  • 88