Spancraft

A powerful Flutter package for rendering richly formatted text with markdown-like syntax and interactive elements. Spancraft automatically detects and styles bold text, italic text, links, and hashtags with customizable styles and tap callbacks.

Features

  • Markdown-like Syntax: Apply bold and italic formatting using customizable delimiters (default: * for bold, _ for italic)
  • Auto-linked URLs: Automatically detects and highlights HTTP/HTTPS URLs and domain-based links
  • Hashtag Detection: Recognizes and styles hashtags (text starting with #)
  • Interactive Elements: Tap callbacks for links and hashtags with full URL/hashtag text passed to handlers
  • Customizable Styling: Define separate SmartTextStyle objects for normal, bold, italic, link, and hashtag text
  • Text Layout Control: Full support for text alignment, direction (LTR/RTL), overflow handling, and max lines
  • Touch Slop Detection: Intelligent tap detection that differentiates between taps and scrolling/panning gestures

Getting started

Prerequisites

  • Flutter SDK >= 2.0.0
  • Dart >= 2.12.0

Installation

Add this to your pubspec.yaml:

dependencies:
  spancraft: ^1.0.0

Then run:

flutter pub get

Usage

Basic Example

import 'package:spancraft/spancraft.dart';
import 'package:flutter/material.dart';

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RichText(
      text: TextSpan(
        children: [
          SmartTextSpan(
            text: 'Check out *Google* and visit _github.com_ for #awesome code!',
            normalStyle: SmartTextStyle(
              style: TextStyle(color: Colors.black),
            ),
            boldStyle: SmartTextStyle(
              style: TextStyle(
                color: Colors.black,
                fontWeight: FontWeight.bold,
              ),
              symbol: '*',
            ),
            italicStyle: SmartTextStyle(
              style: TextStyle(
                color: Colors.black,
                fontStyle: FontStyle.italic,
              ),
              symbol: '_',
            ),
            linkStyle: SmartTextStyle(
              style: TextStyle(
                color: Colors.blue,
                decoration: TextDecoration.underline,
              ),
            ),
            hashtagsStyle: SmartTextStyle(
              style: TextStyle(
                color: Colors.purple,
                fontWeight: FontWeight.bold,
              ),
            ),
            onLinkTap: (url) {
              print('Link tapped: $url');
              // Handle link tap, e.g., launch URL
            },
            onHashtagTap: (tag) {
              print('Hashtag tapped: $tag');
              // Handle hashtag tap, e.g., search for hashtag
            },
          ),
        ],
      ),
    );
  }
}

SmartTextStyle

Each text element type can be styled using SmartTextStyle:

SmartTextStyle(
  style: TextStyle(
    color: Colors.blue,
    fontSize: 16.0,
    fontWeight: FontWeight.bold,
  ),
  symbol: '*',  // Optional: symbol for bold/italic text
)

Customizing Formatting Symbols

By default, bold uses * and italic uses _. You can customize these:

boldStyle: SmartTextStyle(
  style: TextStyle(fontWeight: FontWeight.bold),
  symbol: '**',  // Use ** instead of *
),
italicStyle: SmartTextStyle(
  style: TextStyle(fontStyle: FontStyle.italic),
  symbol: '__',  // Use __ instead of _
),

Text Configuration

SmartTextSpan(
  text: 'Your formatted text here',
  normalStyle: normalStyle,
  boldStyle: boldStyle,
  italicStyle: italicStyle,
  linkStyle: linkStyle,
  hashtagsStyle: hashtagStyle,
  textDirection: TextDirection.ltr,  // or RTL
  textAlign: TextAlign.left,         // left, center, right, etc.
  overflow: TextOverflow.ellipsis,   // How to handle overflow
  maxLines: 3,                       // null for unlimited
)

How It Works

Spancraft uses regular expressions to parse text and identify formatted elements:

  1. Bold Text: Detected by wrapping with the bold symbol (default: *bold*)
  2. Italic Text: Detected by wrapping with the italic symbol (default: _italic_)
  3. Links: Detected by:
    • URLs starting with http://, https://, or www.
    • Domain-based links like example.com or sub.example.com/path
  4. Hashtags: Detected by # prefix followed by word characters (e.g., #flutter)

Each detected element is positioned and styled independently, and tap events are accurately mapped to the correct element.

Advanced Features

The onLinkTap callback receives the full URL or domain:

onLinkTap: (url) {
  // url contains the detected link (e.g., "https://example.com")
  launchUrl(Uri.parse(url));
}

Handling Hashtag Taps

The onHashtagTap callback receives the hashtag including the # symbol:

onHashtagTap: (tag) {
  // tag contains the hashtag (e.g., "#flutter")
  navigateToHashtagPage(tag);
}

Fine-tuning Touch Detection

The touch slop threshold (18.0 logical pixels by default) determines when a pointer movement cancels a tap. This prevents accidental taps while scrolling.

Example App

See the example/ folder for a complete working example with various use cases.

API Reference

SmartTextStyle

class SmartTextStyle {
  final TextStyle style;
  final String? symbol;  // Optional formatting symbol
}

SmartTextSpan

Create styled text spans with automatic element detection and interactive callbacks.

Limitations

  • Formatting symbols must not overlap or nest (e.g., *_bold and italic_* may not render as expected)
  • Regular expression limitations apply to very complex text patterns
  • Custom regex patterns are not currently supported (feature for future versions)

Contributing

Contributions are welcome! Please feel free to open issues and submit pull requests on GitHub.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

For questions or issues, please open an issue on the GitHub repository.