v_story_viewer
A production-ready Flutter package for displaying WhatsApp/Instagram-style stories with intuitive gesture controls, smart media caching, and extensive customization options. Built for performance with 60 FPS animations and full cross-platform support.
📸 Showcase
✨ Key Features
- 📱 Multiple Media Types - Images, videos, text, and custom widgets
- 🎮 Intuitive Gestures - Tap to navigate, swipe to dismiss, long press to pause
- ⚡ High Performance - 60 FPS animations with efficient caching
- 🎨 Fully Customizable - Themes, headers, footers, and custom content
- 🌍 Cross-Platform - iOS, Android, Web, macOS, Windows, Linux
- 🔄 Smart Caching - Automatic media caching with offline support
- 🌐 Internationalization - RTL support and multi-language ready
📋 Roadmap (Upcoming Features)
- 🎙️ Voice Stories - Support for audio/voice recordings as stories
- Video trimming capabilities
- Advanced filters and effects
- Story analytics and metrics
📦 Installation
Add to your pubspec.yaml:
dependencies:
v_story_viewer: ^1.0.0
Then run:
flutter pub get
🚀 Quick Start
import 'package:flutter/material.dart';
import 'package:v_story_viewer/v_story_viewer.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Story Viewer Demo',
home: const StoryViewerScreen(),
);
}
}
class StoryViewerScreen extends StatelessWidget {
const StoryViewerScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final storyGroups = [
VStoryGroup(
id: 'user1',
user: VStoryUser(
name: 'John Doe',
profileUrl: 'https://example.com/profile1.jpg',
verified: true,
),
stories: [
VImageStory(
id: 'story1',
url: 'https://example.com/image1.jpg',
duration: const Duration(seconds: 5),
caption: 'Beautiful sunset 🌅',
),
VVideoStory(
id: 'story2',
url: 'https://example.com/video1.mp4',
caption: 'Amazing moments 🎥',
),
],
),
VStoryGroup(
id: 'user2',
user: VStoryUser(
name: 'Jane Smith',
profileUrl: 'https://example.com/profile2.jpg',
),
stories: [
VTextStory(
id: 'story3',
text: 'Hello World! 👋',
backgroundColor: Colors.blue,
duration: const Duration(seconds: 3),
),
],
),
];
return Scaffold(
body: VStoryViewer(
storyGroups: storyGroups,
onComplete: () => Navigator.pop(context),
),
);
}
}
📖 Story Types
Image Stories
Display images from various sources:
// Network URL
VImageStory(
id: 'img1',
url: 'https://example.com/image.jpg',
duration: const Duration(seconds: 5),
caption: 'Optional caption',
)
// Asset
VImageStory(
id: 'img2',
url: 'assets/images/story.jpg',
duration: const Duration(seconds: 5),
)
// Local file
VImageStory(
id: 'img3',
url: '/path/to/image.jpg',
duration: const Duration(seconds: 5),
)
Video Stories
Play videos automatically:
VVideoStory(
id: 'vid1',
url: 'https://example.com/video.mp4',
caption: 'Video story',
autoPlay: true,
muted: false,
)
Text Stories
Create text-based stories:
VTextStory(
id: 'txt1',
text: 'Hello World!',
backgroundColor: Colors.blue,
textStyle: const TextStyle(
fontSize: 32,
color: Colors.white,
fontWeight: FontWeight.bold,
),
duration: const Duration(seconds: 5),
)
Custom Stories
Display any Flutter widget:
VCustomStory(
id: 'custom1',
duration: const Duration(seconds: 5),
builder: (context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.star, size: 100, color: Colors.yellow),
const SizedBox(height: 20),
const Text(
'Custom Content',
style: TextStyle(fontSize: 24, color: Colors.white),
),
],
),
),
)
🎮 Gesture Controls
Built-in WhatsApp-style gestures:
| Gesture | Action |
|---|---|
| Tap left | Previous story |
| Tap right | Next story |
| Swipe down | Dismiss viewer |
| Swipe left/right | Navigate between groups |
| Double tap | Send reaction |
| Long press | Pause story |
Customize gesture behavior:
VStoryViewer(
storyGroups: storyGroups,
gestureConfig: VGestureConfig(
leftTapZoneWidth: 0.4, // 40% of screen
rightTapZoneWidth: 0.6, // 60% of screen
enableVerticalSwipe: true,
enableHorizontalSwipe: true,
enableDoubleTap: true,
enableLongPress: true,
),
)
🎛️ Programmatic Control
Use VStoryController for programmatic control:
final controller = VStoryController();
// Playback control
controller.play();
controller.pause();
controller.stop();
controller.reset();
// Navigation
controller.nextStory();
controller.previousStory();
controller.jumpToStory(groupIndex: 0, storyIndex: 2);
// Listen to changes
controller.addListener(() {
print('Current state: ${controller.state}');
});
// Cleanup
controller.dispose();
Use the controller with VStoryViewer:
VStoryViewer(
storyGroups: storyGroups,
controller: controller,
onComplete: () => Navigator.pop(context),
)
🎨 Customization
Theme Configuration
VStoryViewer(
storyGroups: storyGroups,
theme: VStoryTheme(
progressBarStyle: VProgressBarStyle(
activeColor: Colors.white,
inactiveColor: Colors.white30,
height: 2.0,
),
headerStyle: VHeaderStyle(
backgroundColor: Colors.black26,
textStyle: const TextStyle(color: Colors.white),
),
footerStyle: VFooterStyle(
backgroundColor: Colors.black54,
textStyle: const TextStyle(color: Colors.white),
),
),
)
Custom Header and Footer
VStoryViewer(
storyGroups: storyGroups,
headerBuilder: (context, group, story) {
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
backgroundImage: NetworkImage(group.user.profileUrl ?? ''),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(group.user.name),
Text(
'Just now',
style: Theme.of(context).textTheme.caption,
),
],
),
],
),
);
},
footerBuilder: (context, group, story) {
return Padding(
padding: const EdgeInsets.all(16),
child: Text(
story.caption ?? '',
style: const TextStyle(color: Colors.white),
),
);
},
)
Reply System
VStoryViewer(
storyGroups: storyGroups,
replyConfig: VReplyConfig(
enabled: true,
placeholder: 'Reply to story...',
maxLength: 280,
onReply: (message, storyId) {
// Handle reply
print('Reply: $message to story: $storyId');
},
),
)
Reaction System
VStoryViewer(
storyGroups: storyGroups,
reactionConfig: VReactionConfig(
enabled: true,
reactions: ['❤️', '😂', '😮', '😢', '👏', '🔥'],
onReaction: (emoji, storyId) {
// Handle reaction
print('Reaction: $emoji on story: $storyId');
},
),
)
📡 Event Callbacks
Track story viewer events:
VStoryViewer(
storyGroups: storyGroups,
onStoryChanged: (groupIndex, storyIndex) {
print('Story changed: group $groupIndex, story $storyIndex');
},
onGroupChanged: (groupIndex) {
print('Group changed: $groupIndex');
},
onComplete: () {
print('All stories viewed');
Navigator.pop(context);
},
onDispose: () {
print('Viewer disposed');
},
)
🌍 Localization & RTL
VStoryViewer(
storyGroups: storyGroups,
locale: const Locale('ar'), // Arabic
textDirection: TextDirection.rtl, // Right-to-left
)
Supported languages: English, Arabic, Spanish, and more.
💾 Caching
The package automatically uses flutter_cache_manager for efficient media caching:
// Use default cache manager (automatic)
VStoryViewer(
storyGroups: storyGroups,
)
// Or provide custom cache manager
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
class CustomCacheManager extends BaseCacheManager {
static const key = 'customStoryCache';
CustomCacheManager() : super(key);
}
VStoryViewer(
storyGroups: storyGroups,
cacheManager: CustomCacheManager(),
)
📋 API Reference
Core Classes
VStoryViewer
Main widget for displaying stories.
Properties:
storyGroups: List<VStoryGroup>- Story groups to displaycontroller: VStoryController?- Optional controller for programmatic controlonComplete: VoidCallback?- Called when all stories completeonDispose: VoidCallback?- Called when widget disposedonStoryChanged: Function(int, int)?- Called on story changeonGroupChanged: Function(int)?- Called on group changetheme: VStoryTheme?- Customize appearancegestureConfig: VGestureConfig?- Configure gesturesreplyConfig: VReplyConfig?- Enable repliesreactionConfig: VReactionConfig?- Enable reactionsheaderBuilder: Function?- Custom header widgetfooterBuilder: Function?- Custom footer widgetlocale: Locale?- Language/region settingtextDirection: TextDirection?- RTL/LTR support
VStoryController
Programmatic control over story playback.
Methods:
play()- Resume playbackpause()- Pause playbackstop()- Stop playbackreset()- Reset to startnextStory()- Go to next storypreviousStory()- Go to previous storyjumpToStory({required int groupIndex, required int storyIndex})- Jump to specific storyaddListener(VoidCallback)- Listen to state changesdispose()- Cleanup resources
Data Models
VStoryGroup
VStoryGroup({
required String id,
required VStoryUser user,
required List<VBaseStory> stories,
})
VStoryUser
VStoryUser({
required String name,
String? profileUrl,
bool verified = false,
})
Story Types
- VImageStory - Display images from network, assets, or files
- VVideoStory - Play videos with auto-play and controls
- VTextStory - Text with custom background and styling
- VCustomStory - Any Flutter widget as story content
Configuration Classes
- VStoryTheme - Progress bar, header, and footer styling
- VGestureConfig - Gesture behavior customization
- VReplyConfig - Reply system setup
- VReactionConfig - Reaction emoji configuration
For detailed API documentation, see API_REFERENCE.md.
🔧 Troubleshooting
Videos not playing
Ensure the video URL is accessible and in a supported format (MP4, WebM). Check platform permissions for video playback.
Images not loading
Verify image URLs are correct and accessible. For local files, ensure paths are absolute. For assets, check pubspec.yaml configuration.
Gestures not responding
Verify gestureConfig properties are correctly set. Ensure the device has proper touch capabilities.
Performance issues
- Reduce story count per session
- Use cached URLs instead of streaming
- Enable caching with
flutter_cache_manager - Use
constconstructors for static stories
Memory leaks
Always dispose the controller when done:
@override
void dispose() {
_controller.dispose();
super.dispose();
}
📱 Platform Support
| Platform | Status |
|---|---|
| iOS | ✅ Fully supported |
| Android | ✅ Fully supported |
| Web | ✅ Fully supported |
| macOS | ✅ Fully supported |
| Windows | ✅ Fully supported |
| Linux | ✅ Fully supported |
📋 Requirements
- Flutter: >=3.0.0
- Dart: ^3.9.0
📦 Dependencies
flutter_cache_manager: ^3.4.1- Media cachingvideo_player: ^2.10.0- Video playbackcached_network_image: ^3.4.1- Image cachingv_platform: ^2.1.4- Cross-platform file handling
📚 Examples
Complete examples are available in the example directory:
- Basic story viewer
- Multiple story types
- Custom themes and styling
- Reply and reaction systems
- Programmatic navigation
- Error handling
Run the example:
cd example
flutter run
🤝 Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
📄 License
MIT License - See LICENSE file for details.
🎯 Need Help?
- Documentation: Full API reference available in API_REFERENCE.md
- Issues: Report bugs on GitHub Issues
- Discussions: Ask questions in GitHub Discussions
✍️ Author
Created and maintained by Hatem Ragap
Made with ❤️ for the Flutter community
Libraries
- v_story_viewer
- V Story Viewer - WhatsApp/Instagram-style story viewing package