[flutter] 하이퍼링크 처리

JunKim.dev·2023년 2월 2일
1

Flutter

목록 보기
2/5
post-thumbnail

0. 하이퍼링크란?

그냥 한줄의 Inline 텍스트를 작성하는 것은 무지 쉽지만, 아래와 텍스트 사이에 링크를 집어넣어야 하는 경우들이 있다. 웹에서는 간단하게 <a> 태그로 감쌀 수 있었는데, Flutter 에서는 어떻게 처리할 수 있을지 궁금했다.

1. RichText

기본적으로 Flutter 에서는 다양한 스타일이 추가된 텍스트를 사용하고 싶을 때를 위해 RichText 위젯을 제공한다.

아래와 같은 예시처럼 가운데 글자만 Bold 처리를 원할 경우, 유용하게 쓰이는 위젯이다.

이 위젯을 사용해서 위 구글세팅 이미지 혹은 아래 회원가입 처럼 글자 사이에 링크를 걸어보자.

2. 기본사용법

위 예제대로 간단하게 구현해보자

2-1. Text 생성

기본 텍스트 생성 방식

Column(
  children: const [
    Text('Don\'t have an account? Sign Up'),
  ],
)

2-2. RichText 생성

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 으로 이루어진 childrenTextSpan 아래에 넣어주면 된다.

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
  },
)

3. 모듈화

단순히 한줄의 안내에서만 사용한다면 문제 없겠지만, 한줄에 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();
  }
}

3-2. LinkTextItem

받아야 할 children 의 타입을 지정해주자.

class LinkTextItem {
  String text;  /// 보여질 텍스트
  bool isLink;  /// 링크 유무
  Function()? onTap;  /// 링크 클릭 시 이벤트

  LinkTextItem({
    required this.text,
    this.isLink = false,
    this.onTap,
  });
}

3-3. 받아야 하는 값 정의

위에서 지정한 타입의 children 들을 배열로 받는다.

const LinkText({
  super.key,
  required this.items,
});

final List<LinkTextItem> items;

3-4. 스타일 지정

일반 텍스트와 링크의 스타일을 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,
  );
  ...

3-5. RichText 가져오기

위에서 구현한 RichTextRow 로 감싼다.

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 = () {
                // 선택처리
              },
          ),
        ],
      ),
    ),
  ],
);

3-6. LinkText 불러오기

다시 메인으로 와서 구현한 RichText 아래에 LinkText 를 불러본다.

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
  	...,
    const SizedBox(
      height: 30,
    ),
    const LinkText(
      items: [],
    ),
  ],
),

4. 꾸미기

위에 Don't have an account? 에서 an 을 링크로 걸어보자.

4-1. getTextSpan 정의

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,
    );
  }
}

4-2. RichText 비우기

위 함수를 사용하기 전에 RichText 를 아래와 같이 비워준다.

return Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    RichText(
      text: const TextSpan(
        children: <TextSpan>[
          /// getTextSpan
        ],
      ),
    ),
  ],
);

4-3. 데이터 설정

메인페이지 build 아래에 코드처럼 데이터를 설정해준다.
얼마든지 링크를 걸고 싶은 텍스트에 설정이 가능하다.


Widget 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'),
    ),
  ];

4-4. 데이터 적용

이제 아래에 import 해두었던 LinkText 에 값을 적용시켜주면 끝이다.

const SizedBox(
  height: 30,
),
LinkText(
  items: linkTextItems,
),

전체 소스코드
GitHub

profile
아는 코드도 다시보자 🫠

0개의 댓글