How to add hyperlinks to Flutter's RichText widget
1 min read

How to add hyperlinks to Flutter's RichText widget

How I built Clearful's terms of service and privacy policy widget using Flutter's RichText class, with clickable hyperlinks.

Before building out the above terms of service and privacy policy widget, I hadn't delved much into Flutter's RichText class. Figuring out how to add the hyperlinks was a bit more involved than expected, but thanks to this StackOverflow thread, the widget was built quickly and worked well.

Note that opening the URLs requires using the url_launcher package and, in my case, a service class that wraps its functionality (not shown below).

Here's my implementation:

class TermsAndPrivacy extends StatefulWidget {
  @override
  _TermsAndPrivacyState createState() => _TermsAndPrivacyState();
}

class _TermsAndPrivacyState extends State<TermsAndPrivacy> {
  TapGestureRecognizer _termsOfServiceLink;
  TapGestureRecognizer _privacyPolicyLink;

  @override
  void initState() {
    super.initState();
    _termsOfServiceLink = TapGestureRecognizer()..onTap = _termsOfServiceOnTap;
    _privacyPolicyLink = TapGestureRecognizer()..onTap = _privacyPolicyOnTap;
  }

  @override
  void dispose() {
    _termsOfServiceLink.dispose();
    _privacyPolicyLink.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(left: 20, right: 20),
      child: Center(
        child: Column(
          children: [
            Text(
              'By using Clearful, you agree to our ',
              style: Theme.of(context).textTheme.overline,
            ),
            RichText(
              textAlign: TextAlign.center,
              text: TextSpan(
                children: [
                  TextSpan(
                    text: 'Terms of Service',
                    style: Theme.of(context)
                        .textTheme
                        .overline
                        .copyWith(fontWeight: FontWeight.w800),
                    recognizer: _termsOfServiceLink
                      ..onTap = _termsOfServiceOnTap,
                  ),
                  TextSpan(
                    text: ' and ',
                    style: Theme.of(context).textTheme.overline,
                  ),
                  TextSpan(
                    text: 'Privacy Policy',
                    style: Theme.of(context)
                        .textTheme
                        .overline
                        .copyWith(fontWeight: FontWeight.w800),
                    recognizer: _privacyPolicyLink..onTap = _privacyPolicyOnTap,
                  ),
                  TextSpan(
                    text: '.',
                    style: Theme.of(context).textTheme.overline,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _termsOfServiceOnTap() async {
    await locator<UrlLauncher>().launchUrl(url: termsOfServiceUrl);
  }

  Future<void> _privacyPolicyOnTap() async {
    await locator<UrlLauncher>().launchUrl(url: privacyPolicyUrl);
  }
}