BigRedSwitch
BigRedSwitch

Reputation: 31

Resizing parent widget to fit child post 'Transform' in Flutter

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.

Upvotes: 1

Views: 3074

Answers (2)

ncardez
ncardez

Reputation: 197

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

Upvotes: 2

ANUP SAJJAN
ANUP SAJJAN

Reputation: 1968

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!

Upvotes: 0

Related Questions