TSS Video Glimpse 🎬

pub package popularity likes pub points

A powerful, elegant, and highly efficient cross-platform Flutter package for generating stunning video previews and thumbnails from multiple sources. Built with TSS (Thumbnail Snapshot Service) technology, this package provides seamless integration with network URLs, local assets, and Firebase Storage paths.

🌟 Why TSS Video Glimpse?

TSS Video Glimpse stands out from other video preview packages with its:

  • 🚀 Lightning-fast performance with intelligent caching
  • 🎨 Beautiful, customizable UI components
  • 🌐 True cross-platform support (Android, iOS, Web, Linux, macOS, Windows)
  • 🔥 Firebase Storage integration out of the box
  • 📦 Smart memory management to prevent leaks
  • 🎯 Simple, intuitive API that just works
  • 🛡️ Production-ready with comprehensive error handling

✨ Features

📹 Multi-Source Support

  • Network URLs (HTTP/HTTPS) - Stream from any web source
  • Local Assets - Bundle videos with your app
  • Firebase Storage - Direct integration with Firebase Storage paths
  • YouTube - Play YouTube videos directly via URL

🎯 Cross-Platform Excellence

  • ✅ Android (API 21+)
  • ✅ iOS (iOS 12+)
  • ✅ Web (All modern browsers)
  • ✅ Linux
  • ✅ macOS
  • ✅ Windows

🚀 Performance Features

  • Smart Caching - Automatic caching for network videos
  • Memory Optimization - Efficient memory management
  • Lazy Loading - Load previews only when needed
  • Configurable Cache Duration - Control cache lifetime

🎨 Customization Options

  • Custom placeholder widgets
  • Custom error handling widgets
  • Loading indicators
  • Aspect ratio control
  • Quality settings (Low, Medium, High)
  • BoxFit options for perfect layouts

📦 Installation

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

dependencies:
  tss_video_glimpse: ^0.1.6

Then run:

flutter pub get

🚀 Quick Start

Basic Usage

The simplest way to display a video preview:

import 'package:tss_video_glimpse/tss_video_glimpse.dart';

// Network URL
TssVideoGlimpse(
  videoUrl: 'https://example.com/video.mp4',
  width: 200,
  height: 150,
)

Asset Video

Display a preview from a local asset:

TssVideoGlimpse.asset(
  assetPath: 'assets/videos/sample.mp4',
  width: 200,
  height: 150,
)

Firebase Storage

Load video directly from Firebase Storage:

TssVideoGlimpse.firebase(
  firebasePath: 'videos/sample.mp4',
  width: 200,
  height: 150,
)

YouTube Video

Display a YouTube video preview and player:

TssVideoGlimpse(
  youtubeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
  width: 200,
  height: 150,
)

📚 Detailed Usage Examples

Example 1: Network Video with Custom Styling

TssVideoGlimpse(
  videoUrl: 'https://example.com/video.mp4',
  width: 300,
  height: 200,
  fit: BoxFit.cover,
  quality: TssVideoQuality.high,
  placeholder: Container(
    decoration: BoxDecoration(
      gradient: LinearGradient(
        colors: [Colors.blue.shade300, Colors.purple.shade300],
      ),
      borderRadius: BorderRadius.circular(12),
    ),
    child: const Center(
      child: CircularProgressIndicator(color: Colors.white),
    ),
  ),
  errorWidget: Container(
    decoration: BoxDecoration(
      color: Colors.red.shade50,
      borderRadius: BorderRadius.circular(12),
    ),
    child: const Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(Icons.error_outline, size: 48, color: Colors.red),
        SizedBox(height: 8),
        Text('Failed to load video', style: TextStyle(color: Colors.red)),
      ],
    ),
  ),
  onError: (error) {
    print('Video loading error: $error');
    // Handle error (e.g., show snackbar, log to analytics)
  },
)

Example 2: Grid of Video Previews

Perfect for video galleries or content browsing:

GridView.builder(
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    crossAxisSpacing: 12,
    mainAxisSpacing: 12,
    childAspectRatio: 16 / 9,
  ),
  itemCount: videoUrls.length,
  itemBuilder: (context, index) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(12),
      child: TssVideoGlimpse(
        videoUrl: videoUrls[index],
        width: double.infinity,
        height: double.infinity,
        fit: BoxFit.cover,
        quality: TssVideoQuality.medium,
      ),
    );
  },
)

Example 3: Using the Controller for Advanced Control

For scenarios where you need more control over the video preview:

class VideoPreviewPage extends StatefulWidget {
  @override
  _VideoPreviewPageState createState() => _VideoPreviewPageState();
}

class _VideoPreviewPageState extends State<VideoPreviewPage> {
  late TssVideoGlimpseController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TssVideoGlimpseController(
      videoUrl: 'https://example.com/video.mp4',
      quality: TssVideoQuality.high,
    );
    _initializeController();
  }

  Future<void> _initializeController() async {
    final success = await _controller.initialize();
    if (!success) {
      // Handle initialization failure
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to load video: ${_controller.error}')),
      );
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Video Preview')),
      body: Column(
        children: [
          TssVideoGlimpse.controller(
            controller: _controller,
            width: double.infinity,
            height: 300,
          ),
          const SizedBox(height: 16),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton.icon(
                onPressed: () => _controller.play(),
                icon: const Icon(Icons.play_arrow),
                label: const Text('Play'),
              ),
              const SizedBox(width: 16),
              ElevatedButton.icon(
                onPressed: () => _controller.pause(),
                icon: const Icon(Icons.pause),
                label: const Text('Pause'),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

Example 4: Firebase Storage with Authentication

import 'package:firebase_core/firebase_core.dart';
import 'package:tss_video_glimpse/tss_video_glimpse.dart';

class FirebaseVideoGallery extends StatelessWidget {
  final List<String> firebasePaths = [
    'videos/intro.mp4',
    'videos/tutorial.mp4',
    'videos/demo.mp4',
  ];

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      itemCount: firebasePaths.length,
      itemBuilder: (context, index) {
        return Card(
          margin: const EdgeInsets.only(bottom: 16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              TssVideoGlimpse.firebase(
                firebasePath: firebasePaths[index],
                width: double.infinity,
                height: 200,
                fit: BoxFit.cover,
                quality: TssVideoQuality.high,
              ),
              Padding(
                padding: const EdgeInsets.all(12),
                child: Text(
                  'Video ${index + 1}',
                  style: const TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

Example 5: YouTube Video with Preview Only

Show only the thumbnail without the play button overlay:

TssVideoGlimpse(
  youtubeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
  width: 300,
  height: 200,
  showPreviewOnly: true, // Only shows thumbnail
  fit: BoxFit.cover,
)

Example 6: Responsive Video Preview

Adapts to different screen sizes:

class ResponsiveVideoPreview extends StatelessWidget {
  final String videoUrl;

  const ResponsiveVideoPreview({required this.videoUrl});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final width = constraints.maxWidth;
        final height = width * 9 / 16; // 16:9 aspect ratio
        
        return TssVideoGlimpse(
          videoUrl: videoUrl,
          width: width,
          height: height,
          fit: BoxFit.cover,
          quality: width > 600 
            ? TssVideoQuality.high 
            : TssVideoQuality.medium,
        );
      },
    );
  }
}

⚙️ Configuration Options

Parameter Type Default Description
videoUrl String? null Network URL of the video
assetPath String? null Local asset path
firebasePath String? null Firebase Storage path
youtubeUrl String? null YouTube video URL
width double 200 Preview width in pixels
height double 150 Preview height in pixels
fit BoxFit BoxFit.cover How the preview fits in the box
placeholder Widget? null Widget shown while loading
errorWidget Widget? null Widget shown on error
onError Function(dynamic)? null Error callback function
showPreviewOnly bool false Only shows thumbnail
showControls bool true Show player controls
cacheMaxAge Duration 7 days Maximum cache duration
quality TssVideoQuality medium Preview quality level

Quality Levels

  • TssVideoQuality.low - Faster loading, lower memory usage (recommended for lists)
  • TssVideoQuality.medium - Balanced performance and quality (default)
  • TssVideoQuality.high - Best quality, higher memory usage (recommended for detail views)

🔥 Firebase Setup

To use Firebase Storage paths, initialize Firebase in your app:

1. Add Firebase to your project

Follow the FlutterFire documentation to add Firebase to your Flutter app.

2. Initialize Firebase

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(MyApp());
}

3. Use Firebase Storage paths

TssVideoGlimpse.firebase(
  firebasePath: 'videos/my-video.mp4',
  width: 300,
  height: 200,
)

🛠️ Platform-Specific Setup

Android

Add to android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
    <application
        android:usesCleartextTraffic="true">
        <!-- Your app configuration -->
    </application>
</manifest>

iOS

Add to ios/Runner/Info.plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
<key>io.flutter.embedded_views_preview</key>
<true/>

Web

No additional setup required! Works out of the box.

Desktop (Linux, macOS, Windows)

No additional setup required! Works out of the box.

🎯 Best Practices

1. Use Appropriate Quality Settings

// For lists and grids
TssVideoGlimpse(
  videoUrl: url,
  quality: TssVideoQuality.low, // Faster, less memory
)

// For detail views
TssVideoGlimpse(
  videoUrl: url,
  quality: TssVideoQuality.high, // Better quality
)

2. Handle Errors Gracefully

TssVideoGlimpse(
  videoUrl: url,
  onError: (error) {
    // Log to analytics
    FirebaseAnalytics.instance.logEvent(
      name: 'video_load_error',
      parameters: {'error': error.toString()},
    );
    
    // Show user-friendly message
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Unable to load video preview')),
    );
  },
)

3. Dispose Controllers Properly

@override
void dispose() {
  _controller.dispose(); // Always dispose controllers
  super.dispose();
}

4. Use Const Constructors When Possible

const TssVideoGlimpse(
  videoUrl: 'https://example.com/video.mp4',
  width: 200,
  height: 150,
)

🔧 Advanced Features

Cache Management

import 'package:tss_video_glimpse/tss_video_glimpse.dart';

// Clear all cached videos
await TssVideoLoaderService().clearCache();

// Remove specific video from cache
await TssVideoLoaderService().removeFromCache(videoUrl);

Custom Cache Duration

TssVideoGlimpse(
  videoUrl: url,
  cacheMaxAge: const Duration(days: 30), // Cache for 30 days
)

📱 Complete Example App

Check out the example directory for a complete sample app demonstrating:

  • Network video previews
  • Asset video previews
  • Firebase Storage integration
  • Grid layouts
  • Custom styling
  • Error handling
  • Controller usage
  • Responsive design

🐛 Troubleshooting

Videos not loading on Android

Make sure you have internet permission in AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Videos not loading on iOS

Check that NSAppTransportSecurity is configured in Info.plist.

Firebase videos not loading

Ensure Firebase is properly initialized and you have the correct storage rules.

🤝 Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

How to Contribute

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

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

💖 Support

If you find this package helpful, please:

  • ⭐ Star the repository on GitHub
  • 👍 Like it on pub.dev
  • 🐛 Report issues on our issue tracker
  • 💬 Share your feedback and suggestions

🙏 Acknowledgments

Built with ❤️ using:


Made with 💙 by the TSS Team

Libraries

tss_video_glimpse
TSS Video Glimpse - A powerful cross-platform Flutter package for generating stunning video previews and thumbnails from network URLs, asset paths, and Firebase Storage paths.