Flutter has emerged as a powerful tool for building visually rich and responsive applications. One of its many capabilities is creating interactive media-rich components like a video grid.
In this article, we’ll dive into how to create an Interactive Video Grid in Flutter that responds to user interactions for seamless playback experiences.
What Is an Interactive Flutter Video Grid?
An interactive video grid is a collection of videos arranged in a grid layout where users can interact with individual video items.
In this example, tapping or hovering over a video starts its playback, while others pause.
This dynamic interaction ensures that the interface is intuitive and engaging, especially for applications like media libraries, video tutorials, or e-commerce platforms showcasing product demos.
Features of Our Interactive Video Grid:
- Videos are displayed in a neat grid layout.
- Users can play a video by tapping or hovering on it.
- When a video finishes, a placeholder image is shown.
- Only one video plays at a time to conserve resources and maintain clarity.
Implementation
Add dependencies: To implement an Interactive Flutter Video Grid, you’ll need these packages. Add the following to your pubspec.yaml file:
1 2 |
video_player: ^2.9.2 flutter_bloc: ^9.0.0 |
Run flutter pub get
to fetch the dependencies.
1. Define the Video Item Model
First, we need a model to represent each video item:
1 2 3 4 5 6 7 |
class VideoItem { final String url; final String title; final String description; VideoItem({required this.url, required this.title, required this.description}); } |
2. Create the Video Grid Page
The VideoGridPage
displays the grid of video items:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class VideoGridPage extends StatelessWidget { final List<VideoItem> videoItems = List.generate( 1000, (index) => VideoItem( url: videoUrls[index % videoUrls.length], title: 'Video Title $index', description: 'This is a description for video $index.', ), ); static const List<String> videoUrls = [ 'https://www.w3schools.com/tags/mov_bbb.mp4', ]; @override Widget build(BuildContext context) { return BlocProvider( create: (_) => VideoCubit(), child: Scaffold( appBar: AppBar(title: const Text('Video Grid')), body: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8, mainAxisExtent: 300, // Consistent height for grid items ), itemCount: videoItems.length, itemBuilder: (context, index) { return VideoGridItem(index: index, videoItem: videoItems[index]); }, ), ), ); } } |
3. Implement the Video Grid Item
The VideoGridItem
widget handles the video playback and interaction:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
class VideoGridItem extends StatefulWidget { final int index; final VideoItem videoItem; VideoGridItem({required this.index, required this.videoItem}); @override _VideoGridItemState createState() => _VideoGridItemState(); } class _VideoGridItemState extends State<VideoGridItem> { VideoPlayerController? _controller; bool _isLoading = true; @override void initState() { super.initState(); _initializeController(); } void _initializeController() { _controller = VideoPlayerController.network(widget.videoItem.url) ..initialize().then((_) { if (mounted) { context.read<VideoCubit>().registerController(widget.index, _controller!); setState(() { _isLoading = false; }); } }); } @override void dispose() { _controller?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return BlocBuilder<VideoCubit, int?>( builder: (context, playingIndex) { final isPlaying = playingIndex == widget.index; return GestureDetector( onPanDown: (_) { context.read<VideoCubit>().playVideo(widget.index); }, child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 4, child: Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Stack( alignment: Alignment.center, children: [ if (_controller != null && _controller!.value.isInitialized && isPlaying) ClipRRect( borderRadius: const BorderRadius.vertical( top: Radius.circular(12)), child: SizedBox( width: double.infinity, height: double.infinity, child: VideoPlayer(_controller!), ), ) else ClipRRect( borderRadius: const BorderRadius.vertical( top: Radius.circular(12)), child: Image.network( 'https://demo.bagisto.com/mobikul-common/cache/large/product/257/zBQMHTNayuES5B5IyDhOJKdTqrcd0mQDERhAhGHE.webp', fit: BoxFit.cover, width: double.infinity, height: double.infinity, ), ), ], ), ), Padding( padding: const EdgeInsets.all(8.0), child: Text( widget.videoItem.title, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text( widget.videoItem.description, style: const TextStyle( fontSize: 14, color: Colors.grey, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), ), ), ); }, ); } } |
4. Manage State with Bloc
Using flutter_bloc
, we ensure only one video plays at a time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class VideoCubit extends Cubit<int?> { final Map<int, VideoPlayerController> _controllers = {}; VideoCubit() : super(null); void registerController(int index, VideoPlayerController controller) { _controllers[index] = controller; } void playVideo(int index) { for (var entry in _controllers.entries) { if (entry.key == index) { entry.value.play(); } else { entry.value.pause(); } } emit(index); } void stopVideo() { for (var controller in _controllers.values) { controller.pause(); } emit(null); } } |
Output
Below is the output of the complete example code.
Interactive Flutter Video Grid
Conclusion
Thanks for reading this article ❤️
I hope this blog will help you learn about how to Implement an Interactive Flutter Video Grid and you will be able to implement it.
For more updates, make sure to keep following Mobikul Blogs to learn more about mobile app development.
Happy Learning ✍️
Other blogs you may like…