그냥 한줄의 Inline
텍스트를 작성하는 것은 무지 쉽지만, 아래와 텍스트 사이에 링크를 집어넣어야 하는 경우들이 있다. 웹에서는 간단하게 <a>
태그로 감쌀 수 있었는데, Flutter 에서는 어떻게 처리할 수 있을지 궁금했다.
기본적으로 Flutter
에서는 다양한 스타일이 추가된 텍스트를 사용하고 싶을 때를 위해 RichText 위젯을 제공한다.
아래와 같은 예시처럼 가운데 글자만 Bold 처리를 원할 경우, 유용하게 쓰이는 위젯이다.
이 위젯을 사용해서 위 구글세팅 이미지 혹은 아래 회원가입 처럼 글자 사이에 링크를 걸어보자.
위 예제대로 간단하게 구현해보자
기본 텍스트 생성 방식
Column(
children: const [
Text('Don\'t have an account? Sign Up'),
],
)
RichText
의 경우, 텍스트는 TextSpan
개체의 트리를 사용하며, 각 TextSpan
에는 children
이 존재해서 그 밑으로 계속해서 TextSpan
을 지정이 가능하다.
기본 스타일이 있기에 비교를 위해서 스타일 지정
Column(
children: [
const Text('Don\'t have an account? Sign Up'),
RichText(
text: const TextSpan(
text: 'Don\'t have an account? ',
style: TextStyle(
fontSize: 15,
color: Colors.black,
),
),
),
],
)
이제 TextSpan
으로 이루어진 children
을 TextSpan
아래에 넣어주면 된다.
RichText(
text: const TextSpan(
text: 'Don\'t have an account? ',
style: TextStyle(
fontSize: 15,
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'Sign Up',
style: TextStyle(
color: Colors.blueAccent,
fontSize: 15,
),
),
],
),
)
아래와 같이 recognizer
로 사용자 탭하였을 때 어떤 이벤트를 실행시킬지 정할 수 있다.
TextSpan(
text: 'Sign Up',
style: const TextStyle(
color: Colors.blueAccent,
fontSize: 15,
),
recognizer: TapGestureRecognizer()..onTap = () {
// onTap Event
},
)
단순히 한줄의 안내에서만 사용한다면 문제 없겠지만, 한줄에 2개 이상의 링크가 포함이 되어야 한다고 하면 코드가 복잡해질 것이다. 이것을 LinkText
라 하여 모듈화해보자.
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart';
class LinkText extends StatelessWidget {
const LinkText({super.key});
Widget build(BuildContext context) {
return const Placeholder();
}
}
받아야 할 children
의 타입을 지정해주자.
class LinkTextItem {
String text; /// 보여질 텍스트
bool isLink; /// 링크 유무
Function()? onTap; /// 링크 클릭 시 이벤트
LinkTextItem({
required this.text,
this.isLink = false,
this.onTap,
});
}
위에서 지정한 타입의 children
들을 배열로 받는다.
const LinkText({
super.key,
required this.items,
});
final List<LinkTextItem> items;
일반 텍스트와 링크의 스타일을 build
안에 지정
Widget build(BuildContext context) {
TextStyle mainTextStyle = const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w500,
);
TextStyle linkTextStyle = mainTextStyle.copyWith(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w600,
);
...
위에서 구현한 RichText
를 Row
로 감싼다.
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RichText(
text: TextSpan(
text: 'Don\'t have an account? ',
style: const TextStyle(
fontSize: 15,
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'Sign Up',
style: const TextStyle(
color: Colors.blueAccent,
fontSize: 15,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// 선택처리
},
),
],
),
),
],
);
다시 메인으로 와서 구현한 RichText
아래에 LinkText
를 불러본다.
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...,
const SizedBox(
height: 30,
),
const LinkText(
items: [],
),
],
),
위에 Don't have an account?
에서 an
을 링크로 걸어보자.
build
아래에 아래와 같이 함수를 정의해준다.
링크유무에 따라 다르게 TextSpan
을 처리해서 return
해준다.
TextSpan getTextSpan(LinkTextItem item) {
if (item.isLink) {
return TextSpan(
text: item.text,
style: linkTextStyle,
recognizer: TapGestureRecognizer()..onTap = item.onTap,
);
} else {
return TextSpan(
text: item.text,
style: mainTextStyle,
);
}
}
위 함수를 사용하기 전에 RichText
를 아래와 같이 비워준다.
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RichText(
text: const TextSpan(
children: <TextSpan>[
/// getTextSpan
],
),
),
],
);
메인페이지 build
아래에 코드처럼 데이터를 설정해준다.
얼마든지 링크를 걸고 싶은 텍스트에 설정이 가능하다.
build(BuildContext context) {
List<LinkTextItem> linkTextItems = [
LinkTextItem(text: 'Don\'t have '),
LinkTextItem(
text: 'an',
isLink: true,
onTap: () => print('an'),
),
LinkTextItem(text: ' account? '),
LinkTextItem(
text: 'Sign Up',
isLink: true,
onTap: () => print('Sign Up'),
),
];
Widget
이제 아래에 import
해두었던 LinkText
에 값을 적용시켜주면 끝이다.
const SizedBox(
height: 30,
),
LinkText(
items: linkTextItems,
),
전체 소스코드
GitHub