opengraph
一个全面的 Flutter 包,用于 OpenGraph 集成,具备双重功能——从 URL 提取元数据(opengraph_fetch)并显示美观的链接预览(OpengraphPreview 小部件)。支持 OpenGraph、Twitter 卡片、HTML 元标签和 JSON-LD 格式。
一个全面的 Flutter 包,用于 OpenGraph 集成,具备双重功能——从 URL 提取元数据(opengraph_fetch)并显示美观的链接预览(OpengraphPreview 小部件)。支持 OpenGraph、Twitter 卡片、HTML 元标签和 JSON-LD 格式。
{"sdk":"flutter"}^0.15.4^1.2.0^1.0.0^0.4.0+2{"sdk":"flutter"}^2.0.0^5.4.2^2.4.9以下为英文项目原文快照,最新内容请访问 GitHub。
pub package pub points likes codecov Tests License: MIT
OpenGraph is a protocol that enables any web page to become a rich object in a social graph. It was originally created by Facebook to enable web pages to have the same functionality as other Facebook objects. Today, OpenGraph is used by most social media platforms, search engines, and messaging apps to create rich previews of shared links.
The Flutter OpenGraph package provides a comprehensive solution for working with OpenGraph metadata in your Flutter applications. It offers dual functionality:
@graph) and favicon fallbackog:image/og:video/og:audio object with width/height/alt, plus article:*/book:* tagswww.example.com just works), controlled redirects with relative images resolved against the final URL, charset detection beyond UTF-8, configurable timeout, headers and CORS proxypackage:http networking — works on Android, iOS, web, Windows, macOS and LinuxOpenGraph Preview
Add the package to your pubspec.yaml file:
dependencies:
opengraph: ^1.2.0
Then run:
flutter pub get
The package offers two main components that can be used independently or together:
This widget displays a rich preview of any URL, showing the title, description, and image extracted from the OpenGraph metadata.
This function extracts OpenGraph metadata from a URL and returns it as a structured object, allowing you to use the data in your own custom UI.
No initialization is required: OpengraphPreview and opengraph_fetch work out of the box, with results cached in memory automatically.
The widget allows you to preview OpenGraph data from a URL with various customization options:
OpengraphPreview(
url: "https://www.youtube.com/watch?v=6g4dkBF5anU",
height: 200, // Optional: Custom height for the preview
borderRadius: 10, // Optional: Rounded corners radius
backgroundColor: Colors.black87, // Optional: Background color
progressColor: Colors.white54, // Optional: Loading indicator color
)
The OpengraphPreview widget supports the following customization options:
| Parameter | Type | Description |
|---|---|---|
url |
String | The URL to fetch OpenGraph data from (required) |
height |
double | Height of the preview card (default: 200) |
borderRadius |
double | Radius for the card corners (default: 10) |
backgroundColor |
Color | Background color of the card (default: black87) |
progressColor |
Color | Color of the loading indicator (default: white54) |
hideOnError |
bool | Render nothing when the fetch fails (default: false) |
childError |
Widget? | Custom widget shown when the fetch fails, replaces the whole card |
childPreview |
Widget? | Custom widget shown while loading, instead of the progress indicator |
showReloadButton |
bool | Show a retry button on error that invalidates the cache (default: false) |
refresh |
String | Label of the retry button (default: "Refresh") |
error |
String | Message shown when the fetch fails |
fallbackImage |
Widget? | Custom widget shown when the page has no og:image, instead of the default image |
enableBlur |
bool | Blur effect behind the text overlay; disable in long lists for better performance (default: true) |
titleStyle |
TextStyle? | Merged over the default title style (white, bold) |
descriptionStyle |
TextStyle? | Merged over the default description style (white) |
hostStyle |
TextStyle? | Merged over the default host style (white54) |
titleMaxLines |
int | Maximum lines for the title (default: 1) |
descriptionMaxLines |
int | Maximum lines for the description (default: 2) |
overlayColor |
Color | Color of the panel behind the texts (default: 50% black) |
imageFit |
BoxFit | How the image fits its box (default: fitWidth) |
onTap |
VoidCallback? | Called when the loaded preview is tapped, e.g. to open the URL |
layout |
OpenGraphLayout | overlay (image with texts on top, default) or horizontal (side image + texts) |
lazyLoad |
bool | Defer the fetch until the widget is visible (default: false) |
visibilityThreshold |
double | Visible fraction that starts a lazy fetch (default: 0.1) |
Fetched URLs are cached in memory, so scrolling through lists does not refetch. You can tune or bypass it:
OpengraphCache.maxEntries = 500; // default: 200
OpengraphCache.ttl = const Duration(hours: 1); // default: 24h; null = session-long
OpengraphCache.evict("https://a.com"); // drop a single URL
OpengraphCache.clear(); // drop everything
OpengraphCache.enabled = false; // disable caching entirely
// Per-call freshness override:
await opengraph_fetch(url, maxAge: Duration.zero); // force a refetch
Network failures are not cached, so transient errors recover on the next attempt.
Previews can survive app restarts by plugging a storage backend into
OpengraphCache.store. The package ships no storage dependency — implement
the small OpengraphCacheStore interface over whatever your app already
uses. With shared_preferences:
import 'dart:convert';
import 'package:opengraph/opengraph.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PrefsCacheStore implements OpengraphCacheStore {
static const _prefix = 'og_cache:';
@override
Future<OpengraphCacheEntry?> read(String url) async {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString('$_prefix$url');
if (raw == null) return null;
return OpengraphCacheEntry.fromJson(jsonDecode(raw));
}
@override
Future<void> write(String url, OpengraphCacheEntry entry) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('$_prefix$url', jsonEncode(entry.toJson()));
}
@override
Future<void> delete(String url) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('$_prefix$url');
}
@override
Future<void> clear() async {
final prefs = await SharedPreferences.getInstance();
for (final key in prefs.getKeys().where((k) => k.startsWith(_prefix))) {
await prefs.remove(key);
}
}
}
void main() {
OpengraphCache.store = PrefsCacheStore();
runApp(const MyApp());
}
Memory stays the source of truth: successful fetches write through to the
store, memory misses are answered from it (validated against the same
TTL/maxAge rules) before hitting the network, and any error thrown by the
store is swallowed — a broken store never breaks fetching. Use
OpengraphCache.clear(memoryOnly: true) to free memory without wiping the
persisted entries.
OpengraphFetch.timeout = const Duration(seconds: 5); // whole redirect chain
OpengraphFetch.maxRedirects = 3; // default: 7
OpengraphFetch.requestHeaders = {
'User-Agent': 'MyApp/1.0',
};
// Per-call headers and timeout, merged over / overriding the defaults:
await opengraph_fetch(url, headers: {'Accept-Language': 'es'});
await opengraph_fetch(url, timeout: const Duration(seconds: 3));
URLs without a scheme (www.example.com) are normalized to https://
before fetching, redirects are followed manually so relative images resolve
against the destination URL, and non-UTF-8 pages (latin1, windows-1252…)
are decoded from the charset declared in the response.
On the web the browser enforces CORS, so most third-party sites refuse a
direct fetch. Route the requests through a proxy with
OpengraphFetch.proxyUrl — either a {url} template or a plain prefix
that gets the encoded URL appended:
OpengraphFetch.proxyUrl = 'https://corsproxy.io/?url={url}';
// or
OpengraphFetch.proxyUrl = 'https://my-proxy.example.com/fetch?u=';
Relative images keep resolving against the target site, not the proxy.
Note the browser also sets its own User-Agent, custom headers are
subject to CORS preflight, and the preview images themselves still load
from their original hosts.
With lazyLoad: true the fetch is deferred until the preview scrolls into
the viewport, so a long list does not fire every request at once:
ListView.builder(
itemBuilder: (context, index) => OpengraphPreview(
url: urls[index],
lazyLoad: true,
enableBlur: false, // cheaper scrolling
),
)
Results land in the cache (with TTL), in-flight requests are deduplicated, and requests already started keep running to completion so the cache warms up even if the user scrolls past.
OpenGraphEntity exposes the full structured protocol beyond the basic
fields:
final entity = await opengraph_fetch("https://example.com/article");
entity!.images; // List<OgImage>: every og:image with width/height/alt/secureUrl
entity.images.first.width;
entity.videos; // List<OgVideo>: og:video objects
entity.audios; // List<OgAudio>: og:audio objects
entity.structuredTags; // article:*, book:*, profile:*, music:*, video:* tags
entity.structuredTags['article:tag']; // e.g. ["flutter", "opengraph"]
entity.faviconUrl; // <link rel="icon"> resolved absolute, if any
When a page has no image in any metadata format, the favicon is used as a last-resort image, so previews still show something recognizable.
Dark Theme:
OpengraphPreview(
url: "https://flutter.dev",
height: 200,
borderRadius: 16,
backgroundColor: Colors.black87,
progressColor: Colors.white54,
)
Rounded Corners:
OpengraphPreview(
url: "https://pub.dev/packages/opengraph",
height: 180,
borderRadius: 24,
backgroundColor: Color(0xFFE0F7FA),
progressColor: Colors.teal,
)
You can directly fetch OpenGraph metadata from any URL and use it in your own custom UI:
// Fetch OpenGraph data
final openGraphData = await opengraph_fetch("https://github.com/baldomerocho/flutter_opengraph");
// Access the structured data
print(openGraphData?.title); // Title of the page
print(openGraphData?.description); // Description of the page
print(openGraphData?.image); // Featured image URL
print(openGraphData?.url); // Canonical URL
print(openGraphData?.siteName); // Site name
print(openGraphData?.type); // Content type (e.g., "website", "article")
print(openGraphData?.locale); // Content locale (e.g., "en_US")
For advanced use cases, you can also access the raw metadata:
// Fetch raw OpenGraph data
final rawData = await opengraph_fetch_raw("https://datogedon.com");
// Access the raw data
print(rawData?.title);
print(rawData?.description);
// ... other properties
The package can extract metadata from multiple formats:
og: meta tags (Facebook, most social platforms)The extraction process follows a priority order, with OpenGraph tags taking precedence when available, followed by Twitter Cards, then JSON-LD, and finally standard HTML meta tags.
import 'package:flutter/material.dart';
import 'package:opengraph/opengraph.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('OpenGraph Preview'),
),
body: Column(
children: [
// Example of using the OpengraphPreview widget
const Padding(
padding: EdgeInsets.all(8.0),
child: OpengraphPreview(
url: "https://www.youtube.com/watch?v=6g4dkBF5anU",
),
),
// Example of using the opengraph_fetch functionality
FutureBuilder(
future: opengraph_fetch("https://github.com/baldomerocho/flutter_opengraph"),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError || !snapshot.hasData) {
return const Text("Error fetching data");
}
final data = snapshot.data!;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text("Title: ${data.title}", style: Theme.of(context).textTheme.titleMedium),
Text("Description: ${data.description}"),
if (data.image.isNotEmpty)
Image.network(data.image, height: 100),
],
),
),
),
);
},
),
],
),
),
);
}
}
## Advanced Usage
### Combining Both Features
You can use both the widget and fetch functionality together in your app. For example, you might want to display a preview widget but also use the metadata for other purposes:
```dart
FutureBuilder(
future: opengraph_fetch("https://flutter.dev"),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
// Display the preview widget
return Column(
children: [
// Use the widget for visual preview
const OpengraphPreview(url: "https://flutter.dev"),
// Also use the fetched data for other purposes
if (snapshot.hasData)
Text("This content is from: ${snapshot.data!.siteName}"),
],
);
},
)
The package includes built-in error handling to ensure your app remains stable even when URLs are invalid or content cannot be fetched:
try {
final data = await opengraph_fetch("https://invalid-url.example");
// Handle success case
} catch (e) {
// Handle error case
print("Error fetching OpenGraph data: $e");
}
OpengraphCache.maxEntries value based on your app's memory constraintsenableBlur: false — BackdropFilter is expensive per itemopengraph_fetch(url)) for important links — results land in the cache and the widget picks them up instantlyOpenGraphRequest / OpenGraphConfiguration (the pre-1.0 singleton API) are deprecated as of 1.3.0 and will be removed in 2.0.0. They keep working — and now send the configured request headers and decode non-UTF-8 charsets — but only parse og: tags. New code should use opengraph_fetch and OpengraphCache, which also parse Twitter Card, JSON-LD and HTML meta fallbacks.
Contributions are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request on the GitHub repository.
This package is available under the MIT License.