tss_video_glimpse 0.1.6
tss_video_glimpse: ^0.1.6 copied to clipboard
Cross-platform Flutter package for video previews from network, assets, and Firebase Storage with smart caching.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:tss_video_glimpse/tss_video_glimpse.dart';
void main() {
runApp(const TssVideoGlimpseExampleApp());
}
/// Comprehensive example app demonstrating all features of TSS Video Glimpse.
///
/// This app showcases:
/// - Network video previews
/// - Different quality settings
/// - Grid layouts
/// - Custom styling
/// - Error handling
/// - Controller usage
/// - Responsive design
class TssVideoGlimpseExampleApp extends StatelessWidget {
/// Creates the example app.
const TssVideoGlimpseExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TSS Video Glimpse Examples',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
useMaterial3: true,
cardTheme: const CardThemeData(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
),
),
home: const VideoGlimpseHomePage(),
);
}
}
/// Home page with navigation to different example screens
class VideoGlimpseHomePage extends StatelessWidget {
const VideoGlimpseHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TSS Video Glimpse Examples'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildWelcomeCard(context),
const SizedBox(height: 16),
_buildExampleCard(
context,
title: 'Basic Examples',
description: 'Simple video previews with different sources',
icon: Icons.video_library,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const BasicExamplesPage(),
),
),
),
_buildExampleCard(
context,
title: 'Advanced Styling',
description: 'Custom placeholders, error widgets, and styling',
icon: Icons.palette,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AdvancedStylingPage(),
),
),
),
_buildExampleCard(
context,
title: 'Grid Layouts',
description: 'Video galleries and grid views',
icon: Icons.grid_view,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const GridLayoutsPage(),
),
),
),
_buildExampleCard(
context,
title: 'Controller Usage',
description: 'Advanced control with TssVideoGlimpseController',
icon: Icons.control_camera,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ControllerExamplePage(),
),
),
),
_buildExampleCard(
context,
title: 'Quality Comparison',
description: 'Compare different quality settings',
icon: Icons.high_quality,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const QualityComparisonPage(),
),
),
),
_buildExampleCard(
context,
title: 'YouTube Examples',
description: 'YouTube video previews and playback',
icon: Icons.play_circle_filled,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const YoutubeExamplesPage(),
),
),
),
],
),
);
}
Widget _buildWelcomeCard(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.video_collection,
size: 32,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 12),
const Text(
'TSS Video Glimpse',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
Text(
'A powerful cross-platform Flutter package for generating stunning video previews.',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
_buildFeatureChip(Icons.cloud, 'Network URLs'),
_buildFeatureChip(Icons.folder, 'Local Assets'),
_buildFeatureChip(Icons.cloud_upload, 'Firebase Storage'),
_buildFeatureChip(Icons.cached, 'Smart Caching'),
_buildFeatureChip(Icons.devices, 'Cross-Platform'),
],
),
],
),
),
);
}
Widget _buildFeatureChip(IconData icon, String label) {
return Chip(
avatar: Icon(icon, size: 16),
label: Text(label, style: const TextStyle(fontSize: 12)),
padding: const EdgeInsets.symmetric(horizontal: 4),
);
}
Widget _buildExampleCard(
BuildContext context, {
required String title,
required String description,
required IconData icon,
required VoidCallback onTap,
}) {
return Card(
child: ListTile(
leading: Icon(
icon,
size: 32,
color: Theme.of(context).colorScheme.primary,
),
title: Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(description),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: onTap,
),
);
}
}
/// Basic examples page showing simple video previews
class BasicExamplesPage extends StatelessWidget {
const BasicExamplesPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Basic Examples'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildSection(
'Network Video',
'Load video from a network URL with default settings',
const TssVideoGlimpse(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
width: double.infinity,
height: 200,
),
),
const SizedBox(height: 24),
_buildSection(
'Different Aspect Ratio',
'Video preview with 16:9 aspect ratio',
const TssVideoGlimpse(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
width: double.infinity,
height: 250,
fit: BoxFit.contain,
),
),
const SizedBox(height: 24),
_buildSection(
'Compact Preview',
'Smaller preview for thumbnails',
const Row(
children: [
Expanded(
child: TssVideoGlimpse(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
width: 150,
height: 100,
),
),
SizedBox(width: 12),
Expanded(
child: TssVideoGlimpse(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
width: 150,
height: 100,
),
),
],
),
),
const SizedBox(height: 24),
_buildInfoCard(),
],
),
);
}
Widget _buildSection(String title, String description, Widget child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 12),
child,
],
);
}
Widget _buildInfoCard() {
return Card(
color: Colors.blue.shade50,
child: const Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info_outline, color: Colors.blue),
SizedBox(width: 8),
Text(
'Pro Tip',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
),
SizedBox(height: 8),
Text(
'Videos are automatically cached for faster loading on subsequent views. '
'The default cache duration is 7 days, but you can customize it using the cacheMaxAge parameter.',
style: TextStyle(fontSize: 14),
),
],
),
),
);
}
}
/// Advanced styling examples page
class AdvancedStylingPage extends StatelessWidget {
const AdvancedStylingPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Advanced Styling'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildSection(
'Custom Placeholder',
'Beautiful gradient placeholder with custom loading indicator',
TssVideoGlimpse(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
width: double.infinity,
height: 200,
placeholder: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.blue.shade300,
Colors.purple.shade300,
],
),
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
SizedBox(height: 12),
Text(
'Loading amazing content...',
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
),
const SizedBox(height: 24),
_buildSection(
'Custom Error Widget',
'Friendly error message with retry suggestion',
TssVideoGlimpse(
videoUrl: 'https://invalid-url-for-demo.com/video.mp4',
width: double.infinity,
height: 200,
errorWidget: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 12),
Text(
'Oops! Video unavailable',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'Please check your connection',
style: TextStyle(fontSize: 12),
),
],
),
),
onError: (error) {
debugPrint('Video loading error: $error');
},
),
),
const SizedBox(height: 24),
_buildSection(
'Rounded Corners',
'Video preview with custom border radius',
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: const TssVideoGlimpse(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
width: double.infinity,
height: 200,
fit: BoxFit.cover,
),
),
),
],
),
);
}
Widget _buildSection(String title, String description, Widget child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 12),
child,
],
);
}
}
/// Grid layouts page showing video galleries
class GridLayoutsPage extends StatelessWidget {
const GridLayoutsPage({super.key});
static const videoUrls = [
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4',
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Grid Layouts'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text(
'2-Column Grid',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 16 / 9,
),
itemCount: 4,
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,
),
);
},
),
const SizedBox(height: 24),
const Text(
'3-Column Grid',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1,
),
itemCount: 6,
itemBuilder: (context, index) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: TssVideoGlimpse(
videoUrl: videoUrls[index],
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
quality: TssVideoQuality.low,
),
);
},
),
],
),
);
}
}
/// Controller example page showing advanced control
class ControllerExamplePage extends StatefulWidget {
const ControllerExamplePage({super.key});
@override
State<ControllerExamplePage> createState() => _ControllerExamplePageState();
}
class _ControllerExamplePageState extends State<ControllerExamplePage> {
late TssVideoGlimpseController _controller;
bool _isInitialized = false;
@override
void initState() {
super.initState();
_controller = TssVideoGlimpseController(
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
quality: TssVideoQuality.high,
);
_initializeController();
}
Future<void> _initializeController() async {
final success = await _controller.initialize();
if (mounted) {
setState(() {
_isInitialized = success;
});
if (!success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to load video: ${_controller.error}'),
backgroundColor: Colors.red,
),
);
}
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Controller Usage'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Column(
children: [
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: TssVideoGlimpse.controller(
controller: _controller,
width: double.infinity,
height: 300,
),
),
),
),
if (_isInitialized) ...[
Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: () => _controller.play(),
icon: const Icon(Icons.play_arrow),
label: const Text('Play'),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: () => _controller.pause(),
icon: const Icon(Icons.pause),
label: const Text('Pause'),
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: () =>
_controller.seekTo(const Duration(seconds: 0)),
icon: const Icon(Icons.skip_previous),
label: const Text('Start'),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: () =>
_controller.seekTo(const Duration(seconds: 10)),
icon: const Icon(Icons.forward_10),
label: const Text('10s'),
),
],
),
const SizedBox(height: 16),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Controller Info',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text('Source: ${_controller.sourceType.name}'),
Text('Quality: ${_controller.quality.name}'),
Text('Initialized: ${_controller.isInitialized}'),
],
),
),
),
],
),
),
],
],
),
);
}
}
/// Quality comparison page
class QualityComparisonPage extends StatelessWidget {
const QualityComparisonPage({super.key});
@override
Widget build(BuildContext context) {
const videoUrl =
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
return Scaffold(
appBar: AppBar(
title: const Text('Quality Comparison'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildQualitySection(
'Low Quality',
'Faster loading, lower memory usage. Best for lists and grids.',
const TssVideoGlimpse(
videoUrl: videoUrl,
width: double.infinity,
height: 200,
quality: TssVideoQuality.low,
),
),
const SizedBox(height: 24),
_buildQualitySection(
'Medium Quality',
'Balanced performance and quality. Default setting.',
const TssVideoGlimpse(
videoUrl: videoUrl,
width: double.infinity,
height: 200,
quality: TssVideoQuality.medium,
),
),
const SizedBox(height: 24),
_buildQualitySection(
'High Quality',
'Best quality, higher memory usage. Best for detail views.',
const TssVideoGlimpse(
videoUrl: videoUrl,
width: double.infinity,
height: 200,
quality: TssVideoQuality.high,
),
),
],
),
);
}
Widget _buildQualitySection(String title, String description, Widget child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 12),
child,
],
);
}
}
/// YouTube examples page
class YoutubeExamplesPage extends StatelessWidget {
const YoutubeExamplesPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('YouTube Examples'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildSection(
'Standard YouTube Video',
'Shows preview (no logo) and plays on tap',
const TssVideoGlimpse(
youtubeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
width: double.infinity,
height: 200,
),
),
const SizedBox(height: 24),
_buildSection(
'Preview Only',
'Shows preview but does not play video (Preview Only Mode)',
const TssVideoGlimpse(
youtubeUrl: 'https://www.youtube.com/watch?v=L_jWHffIx5E',
width: double.infinity,
height: 200,
showPreviewOnly: true,
),
),
const SizedBox(height: 24),
_buildSection(
'Shorts URL Support',
'Works with YouTube Shorts URLs too',
const TssVideoGlimpse(
youtubeUrl: 'https://youtube.com/shorts/5qap5aO4i9A',
width: 200,
height: 350,
fit: BoxFit.cover,
),
),
],
),
);
}
Widget _buildSection(String title, String description, Widget child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 12),
child,
],
);
}
}