프로젝트를 진행하면서 텍스트가 overflow 되어 말줄임표가 되었을때 textSize를 동적으로 변경하는 기능을 넣으려고 했습니다.
플러터 라이브러리 중 자동으로 텍스트 사이즈를 변경해주는 라이브러리가 있는것으로 아는데 그것을 사용하지 않고 직접 변경할 수 있도록 했습니다. (size가 두가지 뿐이라서..)
다음은 텍스트 영역이 overflow되는지 확인하는 Code Snippet을 잠깐 소개하려고 합니다.
우선 테스트 하고자 하는 화면은 다음과 같습니다
A와 B의 텍스트 영역을 width: 300으로 잡아두고 A에는 "동해물과 백두산이", B에는 "남산 위에 저 소나무 철갑을 두른 듯" 이라는 text를 표시하려합니다.
위 이미지에 대해 잠깐 설명하면 A text는 overflow: false이고 말줄임표(...)가 표시되지 않고, B text는 overflow: true로 말줄임표(...)가 표시되고 있습니다.
위 이미지와 함께 하단의 전체 코드를 실행한 로그 결과를 대조하면서 보면 좀 더 이해가 쉬울것 같네요.
[log] LOG textPainterA.size.width: 236.0, textSizeA.width: 300.0
[log] LOG textPainterB.size.width: 447.0, textSizeB.width: 300.0
[log] A 텍스트 사이즈 초과 안됨
[log] B 텍스트 사이즈 초과 (... ellipsis 표시)
우선 전체 코드를 공개하면 다음과 같습니다. (프로젝트의 main.dart 파일에 복사하고 실행하시면 되요)
import 'dart:developer';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GlobalKey keyA = GlobalKey(); // Global Key 선언
GlobalKey keyB = GlobalKey(); // Global Key 선언
String textA = '동해물과 백두산이';
String textB = '남산 위에 저 소나무 철갑을 두른 듯';
bool isOverflowA = false;
bool isOverflowB = false;
final TextStyle titleStyle = const TextStyle(
color: Color(0xFF222222),
fontSize: 20,
fontFamily: 'Pretendard',
fontWeight: FontWeight.w400,
);
final TextStyle textStyle = const TextStyle(
color: Color(0xFF222222),
fontSize: 32,
fontFamily: 'Pretendard',
fontWeight: FontWeight.w700,
);
@override
Widget build(BuildContext context) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
// 텍스트의 위치와 크기 정보 가져오기
final RenderBox textRenderBoxA = keyA.currentContext!.findRenderObject() as RenderBox;
final RenderBox textRenderBoxB = keyB.currentContext!.findRenderObject() as RenderBox;
final Size textSizeA = textRenderBoxA.size;
final Size textSizeB = textRenderBoxB.size;
// 텍스트의 실제 크기 가져오기
final textPainterA = TextPainter(
text: TextSpan(
text: textA,
style: textStyle,
),
textDirection: ui.TextDirection.ltr,
)..layout();
final textPainterB = TextPainter(
text: TextSpan(
text: textB,
style: textStyle,
),
textDirection: ui.TextDirection.ltr,
)..layout();
log('LOG textPainterA.size.width: ${textPainterA.size.width}, textSizeA.width: ${textSizeA.width}');
log('LOG textPainterB.size.width: ${textPainterB.size.width}, textSizeB.width: ${textSizeB.width}');
// A 텍스트의 실제 크기와 텍스트 위젯의 크기 비교
if (textPainterA.size.width > textSizeA.width) {
log('A 텍스트 사이즈 초과 (... ellipsis 표시)');
isOverflowA = true;
} else {
log('A 텍스트 사이즈 초과 안됨');
isOverflowA = false;
}
// B 텍스트의 실제 크기와 텍스트 위젯의 크기 비교
if (textPainterB.size.width > textSizeB.width) {
log('B 텍스트 사이즈 초과 (... ellipsis 표시)');
isOverflowB = true;
} else {
log('A 텍스트 사이즈 초과 안됨');
isOverflowB = false;
}
});
return MaterialApp(
home: SafeArea(
child: Scaffold(
body: renderBody(),
),
),
);
}
Widget renderBody() {
return Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
renderTitle('A text (overflow: $isOverflowA)'),
Container(
width: 300,
color: Colors.yellow,
child: renderText(keyA, textA),
),
const SizedBox(height: 50),
renderTitle('B text (overflow: $isOverflowB)'),
Container(
width: 300,
color: Colors.green,
child: renderText(keyB, textB),
),
],
),
);
}
renderTitle(text) {
return Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: titleStyle,
);
}
renderText(key, text) {
return SizedBox(
// padding: EdgeInsets.symmetric(horizontal: 24.0),
height: 40,
child: Text(
text,
key: key,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textStyle,
),
);
}
}
여기에서 가장 핵심적인 부분은 1. Text wiget에 GlobalKey 지정하기, 2. build 함수 내부의 addPostFrameCallback 입니다.
addPostFrameCallback 부분은 다음과 같고, 주요 로직은 이렇습니다.
1. GlobalKey를 이용해 Text Widget의 RenderBox Size 정보를 알아오기
2. TextPainter를 이용해 (Text와 TextStyle) Text를 그리는데 필요한 실제 크기 계산하기
3. Text Widget의 size보다 TextPainter의 사이즈가 더 크다면 overflow 발생!!
WidgetsBinding.instance?.addPostFrameCallback((_) {
// 텍스트의 위치와 크기 정보 가져오기
final RenderBox textRenderBoxA = keyA.currentContext!.findRenderObject() as RenderBox;
final RenderBox textRenderBoxB = keyB.currentContext!.findRenderObject() as RenderBox;
final Size textSizeA = textRenderBoxA.size;
final Size textSizeB = textRenderBoxB.size;
// 텍스트의 실제 크기 가져오기
final textPainterA = TextPainter(
text: TextSpan(
text: textA,
style: textStyle,
),
textDirection: ui.TextDirection.ltr,
)..layout();
final textPainterB = TextPainter(
text: TextSpan(
text: textB,
style: textStyle,
),
textDirection: ui.TextDirection.ltr,
)..layout();
log('LOG textPainterA.size.width: ${textPainterA.size.width}, textSizeA.width: ${textSizeA.width}');
log('LOG textPainterB.size.width: ${textPainterB.size.width}, textSizeB.width: ${textSizeB.width}');
// A 텍스트의 실제 크기와 텍스트 위젯의 크기 비교
if (textPainterA.size.width > textSizeA.width) {
log('A 텍스트 사이즈 초과 (... ellipsis 표시)');
isOverflowA = true;
} else {
log('A 텍스트 사이즈 초과 안됨');
isOverflowA = false;
}
// B 텍스트의 실제 크기와 텍스트 위젯의 크기 비교
if (textPainterB.size.width > textSizeB.width) {
log('B 텍스트 사이즈 초과 (... ellipsis 표시)');
isOverflowB = true;
} else {
log('A 텍스트 사이즈 초과 안됨');
isOverflowB = false;
}
});
저는 이렇게 해서 체크한 isOverflow 값을 이용해서 TextStyle의 fontSize를 동적으로 변경하려고 합니다.
이상으로 텍스트 영역의 오버플로우 체크 로직에 대한 간략한 설명을 마침니다.
'Development > Flutter' 카테고리의 다른 글
[해결방법] 카카오로그인 릴리즈 키해시 오류 (0) | 2023.12.15 |
---|---|
[해결방법] android.permission.SCHEDULE_EXACT_ALARM 오류의 원인과 수정 방법 (0) | 2023.11.30 |
[Flutter] tabbar background color in flutter (0) | 2023.09.01 |
[해결방법] 플러터 오류 - [CP] Embed Pods Frameworks (0) | 2023.05.22 |
[해결방법] BGTaskSchedulerPermittedIdentifiers 오류, iOS ERROR ITMS-90771 (0) | 2023.04.29 |