Never give up

Flutter - LayoutBuilder with Text widget width 본문

Flutter

Flutter - LayoutBuilder with Text widget width

대기만성 개발자 2021. 9. 3. 23:26
반응형

화면을 구성하다보면 텍스트 길이 혹은 위젯 크기에 따라 유동적으로 변경하고 싶을 때

 

LayoutBuilder를 쓰면 간단하게 해결할 수 있습니다

 

먼저 LayoutBuilder의 builder 내부는 BuildContext와 BoxConstraints로 구성되어 있는데

 

우리가 사용할 부분은 BoxConstraints부분입니다

 

어떤 역할을 하는지 정말 간단하게 알아 보자면

 

For boxes, the constraints are BoxConstraints, which, as described herein, consist of four numbers: a minimum width minWidth, a maximum width maxWidth, a minimum height minHeight, and a maximum height maxHeight.

 

간단하게 위젯의 최소, 최대값 정보를 가지고 있는 녀석(?)입니다

 

조금 더 자세한 설명은 링크를 참고해주세요

(링크 : https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html)

 

간단한 예제로 한번 보면서 이해를 해보면 좋을거 같습니다

class _LayoutBuilderExampleState extends State<LayoutBuilderExample> {
  final List<String> list1 = List.generate(20, (index) => 'Num - $index');
  final List<String> list2 = List.generate(3, (index) => 'Num - $index');
  final GlobalKey _paddingKey = GlobalKey();
  bool _showMore = false;

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      debugPrint('body size : ${_paddingKey.getWidgetSize}');
    });
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        key: _paddingKey,
        padding: const EdgeInsets.all(10.0),
        child: LayoutBuilder(
          builder: (_, constraints) {
            debugPrint('builder size : ${constraints.biggest}');
            return Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _textListWidgets(
                    indexList: list1, maxWidth: constraints.maxWidth),
                _textListWidgets(
                    indexList: list2, maxWidth: constraints.maxWidth),
              ],
            );
          },
        ),
      ),
    );
  }

  Widget _textListWidgets(
      {required List<String> indexList, required double maxWidth}) {
    int lastIndex = 0;
    double textWidth = 0;
    bool isOverSize = false;

    TextStyle style = TextStyle(fontSize: 14, color: Colors.white);
    for (int i = 0; i < indexList.length; i++) {
      TextPainter painter = TextPainter();
      painter.text = TextSpan(text: indexList[i], style: style);
      painter.textDirection = painting.TextDirection.ltr;

      painter.layout();

      textWidth += painter.width + 32;
      //32 = 8 * 2 + 8 * 2 (horizontal padding + margin)

      if (textWidth > maxWidth) {
        lastIndex = i;
        isOverSize = true;
        break;
      }
    }

    if (isOverSize && _showMore) {
      return Wrap(
          children: List.generate(
        indexList.length,
        (index) => _textWidget(indexList[index], style),
      )..add(Padding(
              padding: const EdgeInsets.symmetric(horizontal: 8),
              child: TextButton(
                child: Text('- Hide'),
                onPressed: () {
                  setState(() {
                    _showMore = !_showMore;
                  });
                },
              ),
            )));
    }

    return Wrap(
      crossAxisAlignment: WrapCrossAlignment.center,
      children: [
        ...List.generate(
          isOverSize ? lastIndex : indexList.length,
          (index) => _textWidget(indexList[index], style),
        ),
        isOverSize
            ? Padding(
                padding: const EdgeInsets.symmetric(horizontal: 8),
                child: TextButton(
                  child: Text('+ More'),
                  onPressed: () {
                    setState(() {
                      _showMore = !_showMore;
                    });
                  },
                ),
              )
            : Container()
      ],
    );
  }

  Widget _textWidget(String text, TextStyle style) {
    return Container(
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20), color: Colors.blue),
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      child: Text(
        text,
        style: style,
      ),
    );
  }
}

사용한 extension

extension KeyExtension on GlobalKey {
  Size? get getWidgetSize {
    RenderObject? renderObject = this.currentContext?.findRenderObject();
    return renderObject?.paintBounds.size;
  }
}

< 간단한 위젯! >

먼저 LayoutBuilder의 constraints를 이용해서 위젯크기와 비교할 maxWidth를 넘겨줍니다

 

그리고 text위젯이 그려질 때 width가 얼마나 되는지 계산이 필요한데

 

여기서 TextPainter를 이용하면 알아낼 수 있고

 

추가로 Text위젯 주변에 들어가는 padding 혹은 margin값을 계산해서 넣어준 후

 

maxWidth와 계산된 width값을 비교해줘서 다른 위젯형태로 출력을 해줍니다

 

추가로 global key를 이용해서 사이즈 값을 가지고 올 수도 있는데

 

필자는 extension을 이용해서 간단하게 콜을 할 수 있도록 만들어놨습니다

 

결과값을 확인해보면 패딩 준 20만큼 차이가 나는것을 확인할 수 있습니다

 

print 결과

I/flutter ( 4224): builder size : Size(372.7, 677.5)
I/flutter ( 4224): body size : Size(392.7, 697.5)
반응형
Comments