1

I'm using Transforms in Flutter to create a scrolling carousel for selecting from various options.

This uses standard elements such as ListView.builder, which all works fine, aside from the fact that the parent widget of the Transform doesn't scale down to fit the content as seen here:

notice the gaps around the perspective transforms Here's the code used to generate the 'card' (there was actually a Card in there, but I've stripped it out in an attempt to get everything to scale correctly):

    return Align(
      child: Transform(
        alignment: Alignment.center,
        transform: mat,
        child: Container(
          height: 220,
          color: color,
          width: MediaQuery.of(context).size.width * 0.7,
          child: Text(
            offset.toString(),
            style: TextStyle(color: Colors.white, fontSize: 12.0),
          ),
        ),
      ),
    );
  }

Even if I remove the 'height' parameter of the Container (so everything scales to fit the 'Text' widget), the boxes containing the Transform widgets still have the gaps around them.

Flutter doesn't seem to have any documentation to show how to re-scale the parent if the object within is transformed - anyone here knows or has any idea of a workaround?

EDIT: The widget returned from this is used within a build widget in a Stateful widget. The stack is Column > Container > ListView.builder.

If I remove the Transform, the Containers fit together as I'd like - it seems that performing a perspective transform on the Container 'shrinks' it's content (in this case, the color - check the linked screen grab), but doesn't re-scale the Container itself, which is what I'm trying to achieve.

5
  • what is the parent widget, where you are returning this whole code? Commented Jun 4, 2020 at 12:45
  • Thanks @Chinky for adding the image. I couldn't due to my current rating. Commented Jun 5, 2020 at 14:24
  • @BigRedSwitch it's not related to rating, just simply put a '!' before the image link :D Commented Jun 6, 2020 at 15:37
  • @Chinky Sight - I tried. Denied! D-: Commented Jun 7, 2020 at 19:51
  • did you ever find a solution? ran into exactly the same problem and do not understand why "setState" does not make Listview.builder recalculate the height of the transformed tile Commented Jul 12, 2020 at 9:02

2 Answers 2

2

I have a tricky solution for this: addPostFrameCallback + overlay.

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

// ignore: must_be_immutable
class ChildSizeWidget extends HookWidget {
  final Widget Function(BuildContext context, Widget child, Size size) builder;
  final Widget child;
  final GlobalKey _key = GlobalKey();
  OverlayEntry _overlay;

  ChildSizeWidget({ this.child, this.builder });

  @override
  Widget build(BuildContext context) {
    final size = useState<Size>(null);

    useEffect(() {
      WidgetsBinding.instance.addPostFrameCallback((timestamp) {
        _overlay = OverlayEntry(
          builder: (context) => Opacity(
            child: SingleChildScrollView(
              child: Container(
                child: child,
                key: _key,
              ),
            ),
            opacity: 0.0,
          ),
        );

        Overlay.of(context).insert(_overlay);

        WidgetsBinding.instance.addPostFrameCallback((timestamp) {
          size.value = _key.currentContext.size;
          _overlay.remove();
        });
      });
      return () => null;
    }, [child]);

    if (size == null || size.value == null) {
      return child;
    } else {
      return builder(context, child, size.value);
    }
  }
}

Usage:

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

class HomeView extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final change = useState<bool>(false);
    final normal = Container(
      color: Colors.blueAccent,
      height: 200.0,
      width: 200.0,
    );
    final big = Container(
      color: Colors.redAccent,
      height: 300.0,
      width: 200.0,
    );

    return Column(
      children: [
        Container(
          alignment: Alignment.center,
          child: ChildSizeWidget(
            child: change.value ? big : normal,
            builder: (context, child, size) => AnimatedContainer(
              alignment: Alignment.center,
              child: SingleChildScrollView(child: child),
              duration: Duration(milliseconds: 250),
              height: size.height,
            ),
          ),
          color: Colors.grey,
        ),
        FlatButton(
          child: Text('Toggle child'),
          onPressed: () => change.value = !change.value,
          color: Colors.green,
        ),
      ],
    );
  }
}

I have a menu with several options, they have different height and with the help of the animations this is ok, it's working really nice for me.

Demo

Sign up to request clarification or add additional context in comments.

Comments

0

Why are you using Align, as much as I can see in your code, there is no property set or used, to align anything. So try removing Align widget around Transform. Because according to the documentation, Transform is such a widget that tries to be the same size as their children. So that would satisfy your requirement.

For more info check out this documentation: https://flutter.dev/docs/development/ui/layout/box-constraints

I hope it helps!

1 Comment

The align is just to prevent the parent widgets being scaled across the whole screen. It's not really relevant to the problem here. I've already tried removing it - made no difference. See 'Edit' above for more details.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.