是否可以在不渲染的情况下获取子部件的大小?

问题描述 投票:0回答:2

我正在尝试创建一个需要大小参数的可调整大小的小部件。我想根据它的子大小使高度和宽度字段动态化,这样我们就不必给出小部件的静态大小。请帮帮我!

注意:请通过带有高度和宽度参数的 ResizebleWidget 类。我想根据它的子部件大小使这些参数动态化

import 'package:flutter/material.dart';

class ResizeWidget extends StatefulWidget {
@override
_ResizeWidgetState createState() => _ResizeWidgetState();
}

class _ResizeWidgetState extends State<ResizeWidget> {
@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        // padding: EdgeInsets.only(top: 50),
        child: ResizebleWidget(
          child: Container(
            padding: EdgeInsets.all(10),
            child: Text(
              'Waao!! you can really dance.',
              style: TextStyle(
                  color: Colors.white,
                  fontStyle: FontStyle.italic,
                  fontSize: 18),
            ),
          ),
        ),
      ),
    ),
  );
}
}

**HOW to create this ResizebleWidget size dynamic according to its children**
class ResizebleWidget extends StatefulWidget {
ResizebleWidget({this.child});

final Widget child;
@override
_ResizebleWidgetState createState() => _ResizebleWidgetState();
}

const ballDiameter = 10.0;

class _ResizebleWidgetState extends State<ResizebleWidget> {
double height = 100;
double width = 200;
bool isCorner = false;

double top = 0;
double left = 0;

@override
Widget build(BuildContext context) {
  return Stack(
    children: <Widget>[
      Positioned(
        top: top,
        left: left,
        child: Container(
          height: height,
          width: width,

          decoration: BoxDecoration(
            color: Colors.blueGrey,
            border: Border.all(
              width: 2,
              color: Colors.white70,
            ),
            borderRadius: BorderRadius.circular(0.0),
          ),

          // need tp check if draggable is done from corner or sides
          child: isCorner
              ? FittedBox(
                  child: widget.child,
                )
              : Center(
                  child: widget.child,
                ),
        ),
      ),
      // top left
      Positioned(
        top: top - ballDiameter / 2,
        left: left - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var mid = (dx + dy) / 2;
            var newHeight = height - 2 * mid;
            var newWidth = width - 2 * mid;

            setState(() {
              isCorner = true;
              height = newHeight > 0 ? newHeight : 0;
              width = newWidth > 0 ? newWidth : 0;
              top = top + mid;
              left = left + mid;
            });
          },
          handlerWidget: HandlerWidget.VERTICAL,
        ),
      ),
      // top middle
      Positioned(
        top: top - ballDiameter / 2,
        left: left + width / 2 - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var newHeight = height - dy;

            setState(() {
              isCorner = false;

              height = newHeight > 0 ? newHeight : 0;
              top = top + dy;
            });
          },
          handlerWidget: HandlerWidget.HORIZONTAL,
        ),
      ),
      // top right
      Positioned(
        top: top - ballDiameter / 2,
        left: left + width - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var mid = (dx + (dy * -1)) / 2;

            var newHeight = height + 2 * mid;
            var newWidth = width + 2 * mid;

            setState(() {
              isCorner = true;
              height = newHeight > 0 ? newHeight : 0;
              width = newWidth > 0 ? newWidth : 0;
              top = top - mid;
              left = left - mid;
            });
          },
          handlerWidget: HandlerWidget.VERTICAL,
        ),
      ),
      // center right
      Positioned(
        top: top + height / 2 - ballDiameter / 2,
        left: left + width - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var newWidth = width + dx;

            setState(() {
              isCorner = false;

              width = newWidth > 0 ? newWidth : 0;
            });
          },
          handlerWidget: HandlerWidget.HORIZONTAL,
        ),
      ),
      // bottom right
      Positioned(
        top: top + height - ballDiameter / 2,
        left: left + width - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var mid = (dx + dy) / 2;

            var newHeight = height + 2 * mid;
            var newWidth = width + 2 * mid;

            setState(() {
              isCorner = true;

              height = newHeight > 0 ? newHeight : 0;
              width = newWidth > 0 ? newWidth : 0;
              top = top - mid;
              left = left - mid;
            });
          },
          handlerWidget: HandlerWidget.VERTICAL,
        ),
      ),
      // bottom center
      Positioned(
        top: top + height - ballDiameter / 2,
        left: left + width / 2 - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var newHeight = height + dy;

            setState(() {
              isCorner = false;

              height = newHeight > 0 ? newHeight : 0;
            });
          },
          handlerWidget: HandlerWidget.HORIZONTAL,
        ),
      ),
      // bottom left
      Positioned(
        top: top + height - ballDiameter / 2,
        left: left - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var mid = ((dx * -1) + dy) / 2;

            var newHeight = height + 2 * mid;
            var newWidth = width + 2 * mid;

            setState(() {
              isCorner = true;

              height = newHeight > 0 ? newHeight : 0;
              width = newWidth > 0 ? newWidth : 0;
              top = top - mid;
              left = left - mid;
            });
          },
          handlerWidget: HandlerWidget.VERTICAL,
        ),
      ),
      //left center
      Positioned(
        top: top + height / 2 - ballDiameter / 2,
        left: left - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            var newWidth = width - dx;

            setState(() {
              isCorner = false;

              width = newWidth > 0 ? newWidth : 0;
              left = left + dx;
            });
          },
          handlerWidget: HandlerWidget.HORIZONTAL,
        ),
      ),
      // center center
      Positioned(
        top: top + height / 2 - ballDiameter / 2,
        left: left + width / 2 - ballDiameter / 2,
        child: ManipulatingBall(
          onDrag: (dx, dy) {
            setState(() {
              isCorner = false;

              top = top + dy;
              left = left + dx;
            });
          },
          handlerWidget: HandlerWidget.VERTICAL,
        ),
      ),
    ],
  );
}
}

class ManipulatingBall extends StatefulWidget {
ManipulatingBall({Key key, this.onDrag, this.handlerWidget});

final Function onDrag;
final HandlerWidget handlerWidget;

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

enum HandlerWidget { HORIZONTAL, VERTICAL }

class _ManipulatingBallState extends State<ManipulatingBall> {
double initX;
double initY;

_handleDrag(details) {
  setState(() {
    initX = details.globalPosition.dx;
    initY = details.globalPosition.dy;
  });
}

_handleUpdate(details) {
  var dx = details.globalPosition.dx - initX;
  var dy = details.globalPosition.dy - initY;
  initX = details.globalPosition.dx;
  initY = details.globalPosition.dy;
  widget.onDrag(dx, dy);
}

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onPanStart: _handleDrag,
    onPanUpdate: _handleUpdate,
    child: Container(
      width: ballDiameter,
      height: ballDiameter,
      decoration: BoxDecoration(
        color: Colors.white,
        shape: this.widget.handlerWidget == HandlerWidget.VERTICAL
            ? BoxShape.circle
            : BoxShape.rectangle,
      ),
    ),
  );
}
}
flutter flutter-layout flutter-dependencies flutter-animation flutter-test
2个回答
1
投票

小部件的大小在渲染之前无法确定。但是我可以看到您想知道文本的大小,以便您可以创建自己的小部件。您可以使用

TextPainter
来完成此操作。在此示例中,我使用它来创建某种文本容器,您可以通过
CustomPainter
.

以任何可能的方式设置样式
import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.white,
        body: Center(child: TextContainer()),
      ),
    );
  }
}

class TextContainer extends StatelessWidget {
  static String text = "TextPainter is amazing!!!!!!!!!!!!!!!!!";
  static double padding =
      20.0; // in case you want you text container to have padding.

  @override
  Widget build(BuildContext context) {
    // to calculate the size of the text you need to create a TextPainter based on the text and
    // its style
    final TextPainter textPainter = TextPainter(
        textDirection: TextDirection.ltr,
        text: TextSpan(
            text: text,
            style: TextStyle(
              color: Colors.white,
              fontStyle: FontStyle.italic,
              fontSize: 50,
            )))
      ..layout(maxWidth: 300);
    // in the layout of the TexPainter you set the maxWidth, layout is required.
    return Container(
        width: textPainter.width + padding,
        height: textPainter.height + padding,
        child: CustomPaint(
          painter:
              TextContainerPainter(textPainter: textPainter, padding: padding),
        ));
  }
}

class TextContainerPainter extends CustomPainter {
  TextContainerPainter({required this.textPainter, required this.padding});

  final TextPainter textPainter;
  final double padding;

  @override
  void paint(Canvas canvas, Size size) {

    // ============================================== Container

    final paint = Paint()..color = Colors.greenAccent;
    final rect = Offset.zero &
        Size(textPainter.width + padding, textPainter.height + padding);

    canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(15)), paint);

    // ========================================== TextPainter

    textPainter.paint(canvas, Offset(padding / 2, padding / 2));
    // this offset is necessary so that the text is at the center of its container
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

如下图所示,小部件具有我们指定的文本大小和填充。


0
投票

在不渲染的情况下无法获取小部件的高度,但我们可以使用叠加小部件技巧来获取高度。

使用这些步骤:

  1. 使用叠加层来呈现小部件。 (不透明度为零,因此不可见)
  2. 随着小部件的渲染,我们可以计算出小部件的高度

这里是实际的实现:

import 'dart:async';

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

Future<double> getHeightOfWidget(Widget widget, BuildContext context) {
    Completer<double> completer = new Completer<double>();
    late OverlayEntry entry; 
  entry = OverlayEntry(
    builder: (_) => Center(
      child: WidgetSize(
                onChange: (Size s) {
                    entry.remove();
                    completer.complete(s.height);
                },
        child: Opacity(opacity: 0, child: widget),
      ),
    ),
  );
    Overlay.of(context)?.insert(entry);
    return completer.future;
}

class WidgetSize extends StatefulWidget {
  final Widget child;
  final Function onChange;

  const WidgetSize({
    Key? key,
    required this.onChange,
    required this.child,
  }) : super(key: key);

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

class _WidgetSizeState extends State<WidgetSize> {
  @override
  Widget build(BuildContext context) {
    SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
    return Container(
      key: widgetKey,
      child: widget.child,
    );
  }

  var widgetKey = GlobalKey();
  var oldSize;

  void postFrameCallback(_) {
    var context = widgetKey.currentContext;
    if (context == null) return;

    var newSize = context.size;
    if (oldSize == newSize) return;

    oldSize = newSize;
    widget.onChange(newSize);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.