metalink_flutter 1.0.0 copy "metalink_flutter: ^1.0.0" to clipboard
metalink_flutter: ^1.0.0 copied to clipboard

Flutter UI components for link previews using the MetaLink package

MetaLink Flutter

Pub Stars License Platform

A Flutter package for beautiful, highly customizable link preview widgets, built on top of the MetaLink package.

✨ Features #

  • 🔗 Rich link previews with images, favicon, title, and description
  • 🎨 Multiple styles: Card, Compact, Large, and custom
  • 🎭 Fully themeable with Material 3 integration
  • 🔄 Smart image optimization and responsive sizing
  • 💾 Built-in caching for faster loading
  • 👆 Tap handling with URL launching or custom callbacks
  • 🚧 Loading skeleton placeholders with shimmer effects
  • 🧩 Highly customizable components
  • 📱 RTL support using Flutter's logical directional properties

📸 Screenshots #

Card Style Compact Style Large Style Large Style

🚀 Getting Started #

Installation #

Add the package to your pubspec.yaml:

dependencies:
  metalink_flutter: ^<LATEST VERSION>

Run the installation command:

flutter pub get

Basic Usage #

Import the package:

import 'package:metalink_flutter/metalink_flutter.dart';

Add a simple link preview widget:

LinkPreview(
  url: 'https://flutter.dev',
)

That's it! The widget will automatically fetch metadata and display a card-style preview of the link.

MetaLink Flutter comes with three built-in styles and the ability to create custom styles.

Card Style (Default) #

Displays a card with the link's image on top, and title, description, and site information below.

LinkPreview.card(
  url: 'https://flutter.dev',
  titleMaxLines: 2,
  descriptionMaxLines: 3,
)

Compact Style #

A horizontal layout suitable for inline previews in chat interfaces or lists.

LinkPreview.compact(
  url: 'https://flutter.dev',
  titleMaxLines: 1,
  descriptionMaxLines: 1,
)

Large Style #

A prominent display with a large image and detailed content, suitable for featured links.

LinkPreview.large(
  url: 'https://flutter.dev',
  titleMaxLines: 2,
  descriptionMaxLines: 4,
)

Custom Style #

Create your own unique link preview style:

LinkPreview.custom(
  url: 'https://flutter.dev',
  builder: (context, data) {
    return Card(
      child: Column(
        children: [
          if (data.hasImage) 
            Image.network(data.imageUrl!),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  data.title ?? 'No Title',
                  style: Theme.of(context).textTheme.titleLarge,
                ),
                if (data.description != null)
                  Text(data.description!),
                Text(data.hostname, style: TextStyle(color: Colors.blue)),
              ],
            ),
          ),
        ],
      ),
    );
  },
)

🔧 Advanced Configuration #

Configuration Options #

The LinkPreview widget accepts a config parameter for customizing its behavior:

LinkPreview(
  url: 'https://flutter.dev',
  config: LinkPreviewConfig(
    style: LinkPreviewStyle.card,
    titleMaxLines: 2,
    descriptionMaxLines: 3,
    showImage: true,
    showFavicon: true,
    handleNavigation: true,
    animateLoading: true,
    cacheDuration: Duration(hours: 24),
  ),
  onTap: () {
    print('Link tapped!');
  },
)

Error and Loading Handling #

Customize the appearance of loading and error states:

LinkPreview(
  url: 'https://flutter.dev',
  errorBuilder: (context, error) {
    return Container(
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        border: Border.all(color: Colors.red),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Text('Failed to load preview: $error'),
    );
  },
  loadingBuilder: (context) {
    return Container(
      padding: EdgeInsets.all(16),
      child: Row(
        children: [
          CircularProgressIndicator(),
          SizedBox(width: 16),
          Text('Loading preview...'),
        ],
      ),
    );
  },
)

🎮 Using Controllers #

The LinkPreviewController allows you to programmatically control link previews:

class _MyWidgetState extends State<MyWidget> {
  late LinkPreviewController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = LinkPreviewController();
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          decoration: InputDecoration(labelText: 'Enter URL'),
          onSubmitted: (url) {
            _controller.setUrl(url);
          },
        ),
        SizedBox(height: 16),
        LinkPreview(
          url: '', // Will be set by controller
          controller: _controller,
        ),
        Row(
          children: [
            ElevatedButton(
              onPressed: () => _controller.fetchData(forceRefresh: true),
              child: Text('Refresh'),
            ),
            SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => _controller.clear(),
              child: Text('Clear'),
            ),
          ],
        ),
      ],
    );
  }
}

🎭 Theming #

Adding Theme Extension #

MetaLink Flutter integrates with your app's theme system using Theme Extensions:

final myTheme = ThemeData.light().copyWith(
  extensions: [
    LinkPreviewTheme(
      data: LinkPreviewThemeData(
        backgroundColor: Colors.grey[100],
        titleStyle: TextStyle(
          fontSize: 16,
          fontWeight: FontWeight.bold,
          color: Colors.indigo,
        ),
        descriptionStyle: TextStyle(
          fontSize: 14,
          color: Colors.black87,
        ),
        urlStyle: TextStyle(
          fontSize: 12,
          color: Colors.blue,
        ),
        borderRadius: BorderRadius.circular(12),
        elevation: 2.0,
        imageHeight: 150.0,
        faviconSize: 16.0,
        cardShape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
          side: BorderSide(color: Colors.grey.withOpacity(0.2)),
        ),
      ),
    ),
  ],
);

// Apply the theme
MaterialApp(
  theme: myTheme,
  // ...
)

Theme Extension Method #

You can also use the extension method on ThemeData:

final myTheme = ThemeData.light().withLinkPreviewTheme(
  LinkPreviewThemeData(
    backgroundColor: Colors.grey[100],
    titleStyle: TextStyle(fontWeight: FontWeight.bold),
    borderRadius: BorderRadius.circular(12),
    // ...other properties
  ),
);

🔍 URL Detection #

Automatically detect URLs in text:

final text = "Check out this cool site: https://flutter.dev and this one www.example.com";
final urls = UrlDetector.detectUrls(text);

for (final match in urls) {
  print('URL: ${match.url}, Position: ${match.start}-${match.end}');
  
  // Create a preview for each detected URL
  LinkPreview.compact(url: match.url);
}

📦 MetadataProvider #

The MetadataProvider class handles caching and fetching metadata:

final provider = MetadataProvider(
  cacheDuration: Duration(hours: 24),
  enableCache: true,
);

// Get metadata for a URL
final metadata = await provider.getMetadata('https://flutter.dev');

// Get metadata for multiple URLs in parallel
final metadataList = await provider.getMultipleMetadata([
  'https://flutter.dev',
  'https://pub.dev',
  'https://material.io',
]);

// Clear the cache
await provider.clearCache();

📄 API Documentation #

Main Classes #

  • LinkPreview - The main widget for displaying link previews
  • LinkPreviewData - UI-specific data model for link previews
  • LinkPreviewController - Controls the state of link previews
  • MetadataProvider - Handles caching and fetching metadata
  • LinkPreviewTheme - Theme extension for customizing appearance
  • UrlDetector - Utility for finding and analyzing URLs in text
  • ImageResolver - Utility for optimizing images

For complete API documentation, please see the API reference.

🙋 FAQ #

Q: Does this work with any URL?
A: Yes, the package attempts to extract metadata from any valid URL. The quality of the preview depends on the metadata available on the target website.

Q: How is caching handled?
A: The package caches metadata in memory and optionally on disk using hive_ce. You can configure the cache duration and clear the cache programmatically.

Q: Does it support RTL languages?
A: Yes, the package uses Flutter's logical directional properties (start/end instead of left/right) for proper RTL support.

Q: Can I customize the loading animation?
A: Yes, you can provide your own loading widget using the loadingBuilder parameter.

👨‍💻 Contributing #

Contributions are welcome! If you find a bug or want a feature, please open an issue. If you want to contribute code, please fork the repository and submit a pull request.

📄 License #

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