vyuh_plugin_content_provider_sanity 1.12.1 copy "vyuh_plugin_content_provider_sanity: ^1.12.1" to clipboard
vyuh_plugin_content_provider_sanity: ^1.12.1 copied to clipboard

A Content Provider for the Vyuh Framework that reads documents from Sanity.io.

Vyuh Logo

Vyuh Framework

Build Modular, Scalable, CMS-driven Flutter Apps

Docs | Website

Vyuh Content Provider for Sanity 🔌 #

vyuh_plugin_content_provider_sanity

A powerful content provider plugin that seamlessly integrates Sanity.io with the Vyuh framework. This plugin enables your Vyuh application to fetch, cache, and manage content from Sanity.io with minimal configuration.

Features ✨ #

  • Content Management

    • Fetch single or multiple documents
    • Support for all Sanity content types
    • Automatic content type mapping
    • Built-in caching with configurable duration
  • Asset Support

    • Image assets with transformations
    • File assets with metadata
    • Reference handling
    • Asset caching
  • Routing Integration

    • Automatic route generation from content
    • Support for Vyuh framework schema
    • Dynamic route parameters
    • Nested route handling
  • Performance

    • Efficient content caching
    • CDN support
    • Optimized network requests
    • Parallel content fetching

Installation 📦 #

Add this to your package's pubspec.yaml file:

dependencies:
  vyuh_plugin_content_provider_sanity: ^1.0.0
copied to clipboard

Usage 💡 #

Basic Setup #

Create and configure the Sanity content provider:

import 'package:vyuh_core/vyuh_core.dart' as vc;
import 'package:sanity_client/sanity_client.dart';
import 'package:vyuh_plugin_content_provider_sanity/vyuh_plugin_content_provider_sanity.dart';

final contentProvider = SanityContentProvider.withConfig(
  config: SanityConfig(
    projectId: '<project-id>',
    dataset: 'production',
    perspective: Perspective.published,  // or previewDrafts for draft content
    useCdn: true,                       // enable CDN for better performance
  ),
  cacheDuration: const Duration(minutes: 5),
);

void main() async {
  vc.runApp(
    features: () => [
      // your features here
    ],
    plugins: [
      contentProvider,
      // other plugins
    ],
  );
}
copied to clipboard

Fetching Content #

// Fetch a single document
final post = await contentProvider.fetchSingle<Post>(
  '*[_type == "post" && _id == $id][0]',
  fromJson: Post.fromJson,
  queryParams: {'id': 'post-123'},
);

// Fetch multiple documents
final posts = await contentProvider.fetchMultiple<Post>(
  '*[_type == "post" && category == $category]',
  fromJson: Post.fromJson,
  queryParams: {'category': 'tech'},
);

// Fetch by ID
final author = await contentProvider.fetchById<Author>(
  'author-123',
  fromJson: Author.fromJson,
);
copied to clipboard

Working with Assets #

// Get an ImageProvider for use in Image widget
final imageProvider = contentProvider.image(
  ImageReference(
    asset: Asset(ref: 'image-Tb9Ew8CXIwaY6R1kjMvI0uRR-2000x3000-jpg'),
  ),
  width: 800,
  height: 600,
  devicePixelRatio: 2,
  quality: 80,
  format: 'webp',
);

// Use in an Image widget
if (imageProvider != null) {
  Image(image: imageProvider)
}

// Get a file URL
final fileUrl = contentProvider.fileUrl(fileRef);
copied to clipboard

Route Handling #

// Fetch route by path
final route = await contentProvider.fetchRoute(
  path: '/blog/my-post',
);

// Fetch route by ID
final route = await contentProvider.fetchRoute(
  routeId: 'route-123',
);

if (route != null) {
  // Handle route content
}
copied to clipboard

Live Query Integration [NEW ✨] #

SanityContentProvider now includes support for the LiveContentProvider API. It offers real-time updates for your Sanity content, allowing your app to stay in sync with your dataset.

The API is almost identical with the standard ContentProvider, barring the fact that the live API returns a Stream<T?> instead of a Future<T?>.

Here are examples of how to use the live query methods:

// Create a LiveProvider instance
final liveProvider = SanityContentProvider(
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  useCdn: true,
  perspective: 'published',
).live; // Notice the call to get the LiveContentProvider instance

// Fetch real-time updates for a single document
final singleStream = liveProvider.fetchSingle<Post>(
  '*[_type == "post" && _id == $id][0]',
  fromJson: Post.fromJson,
  queryParams: {'id': 'post-123'},
);

singleStream.listen((post) {
// Handle updated post
});

// Fetch real-time updates for multiple documents
final multipleStream = liveProvider.fetchMultiple<Post>(
  '*[_type == "post" && category == $category]',
  fromJson: Post.fromJson,
  queryParams: {'category': 'tech'},
);

multipleStream.listen((posts) {
// Handle updated list of posts
});

// Fetch real-time updates by ID
final idStream = liveProvider.fetchById<Author>(
  'author-123',
  fromJson: Author.fromJson,
);

idStream.listen((author) {
// Handle updated author
});

// Use StreamBuilder in your widget tree
StreamBuilder<Post>(
  stream: singleStream,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Text(snapshot.data!.title);
    } else if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }

    return CircularProgressIndicator();
  },
);
copied to clipboard

Configuration 🔧 #

The content provider can be configured with various options:

final provider = SanityContentProvider.withConfig(
  config: SanityConfig(
    projectId: '<project-id>',
    dataset: 'production',

    // Content delivery options
    useCdn: true,                    // Use CDN for better performance
    perspective: Perspective.published, // Content perspective

    // Cache configuration
    cacheDuration: Duration(minutes: 5),

    // Optional authentication
    token: '<your-token>',           // For accessing private datasets
  ),

  // Additional options
  debug: true,                       // Enable debug logging
  retryOptions: RetryOptions(        // Configure retry behavior
    maxAttempts: 3,
    delayFactor: Duration(seconds: 1),
  ),
);
copied to clipboard

Error Handling 🚨 #

The provider includes proper error handling for various scenarios:

try {
  final post = await contentProvider.fetchSingle<Post>(
    '*[_type == "post" && _id == $id][0]',
    fromJson: Post.fromJson,
    queryParams: {'id': 'post-123'},
  );
  // Handle post
} on InvalidResultTypeException catch (e) {
  print('Type mismatch: Expected ${e.expectedType}, got ${e.actualType}');
} on SanityError catch (e) {
  print('Sanity API error: ${e.message}');
} on Exception catch (e) {
  print('Unexpected error: $e');
}

// When fetching route
try {
  final route = await contentProvider.fetchRoute(path: '/blog/my-post');
  if (route == null) {
    print('Route not found');
    return;
  }
  // Handle route
} on SanityError catch (e) {
  print('Error fetching route: ${e.message}');
}
copied to clipboard

Contributing 🤝 #

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Learn More 📚 #


Made with ❤️ by Vyuh