v_story_viewer 1.0.3
v_story_viewer: ^1.0.3 copied to clipboard
Flutter package for WhatsApp/Instagram-style story viewing with gestures, caching, and cross-platform support.
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