import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_gallery/photo_gallery.dart'; import 'package:transparent_image/transparent_image.dart'; import 'package:video_player/video_player.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { List? _albums; bool _loading = false; @override void initState() { super.initState(); _loading = true; initAsync(); } Future initAsync() async { if (await _promptPermissionSetting()) { List albums = await PhotoGallery.listAlbums(); setState(() { _albums = albums; _loading = false; }); } setState(() { _loading = false; }); } Future _promptPermissionSetting() async { if (Platform.isIOS && await Permission.storage.request().isGranted && await Permission.photos.request().isGranted || Platform.isAndroid && await Permission.storage.request().isGranted) { return true; } return false; } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Photo gallery example'), ), body: _loading ? Center( child: CircularProgressIndicator(), ) : LayoutBuilder( builder: (context, constraints) { double gridWidth = (constraints.maxWidth - 20) / 3; double gridHeight = gridWidth + 33; double ratio = gridWidth / gridHeight; return Container( padding: EdgeInsets.all(5), child: GridView.count( childAspectRatio: ratio, crossAxisCount: 3, mainAxisSpacing: 5.0, crossAxisSpacing: 5.0, children: [ ...?_albums?.map( (album) => GestureDetector( onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => AlbumPage(album))), child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(5.0), child: Container( color: Colors.grey[300], height: gridWidth, width: gridWidth, child: FadeInImage( fit: BoxFit.cover, placeholder: MemoryImage(kTransparentImage), image: AlbumThumbnailProvider( albumId: album.id, mediumType: album.mediumType, highQuality: true, ), ), ), ), Container( alignment: Alignment.topLeft, padding: EdgeInsets.only(left: 2.0), child: Text( album.name ?? "Unnamed Album", maxLines: 1, textAlign: TextAlign.start, style: TextStyle( height: 1.2, fontSize: 16, ), ), ), Container( alignment: Alignment.topLeft, padding: EdgeInsets.only(left: 2.0), child: Text( album.count.toString(), textAlign: TextAlign.start, style: TextStyle( height: 1.2, fontSize: 12, ), ), ), ], ), ), ), ], ), ); }, ), ), ); } } class AlbumPage extends StatefulWidget { final Album album; AlbumPage(Album album) : album = album; @override State createState() => AlbumPageState(); } class AlbumPageState extends State { List? _media; @override void initState() { super.initState(); initAsync(); } void initAsync() async { MediaPage mediaPage = await widget.album.listMedia(); setState(() { _media = mediaPage.items; }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( leading: IconButton( icon: Icon(Icons.arrow_back_ios), onPressed: () => Navigator.of(context).pop(), ), title: Text(widget.album.name ?? "Unnamed Album"), ), body: GridView.count( crossAxisCount: 3, mainAxisSpacing: 1.0, crossAxisSpacing: 1.0, children: [ ...?_media?.map( (medium) => GestureDetector( onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (context) => ViewerPage(medium))), child: Container( color: Colors.grey[300], child: FadeInImage( fit: BoxFit.cover, placeholder: MemoryImage(kTransparentImage), image: ThumbnailProvider( mediumId: medium.id, mediumType: medium.mediumType, highQuality: true, ), ), ), ), ), ], ), ), ); } } class ViewerPage extends StatelessWidget { final Medium medium; ViewerPage(Medium medium) : medium = medium; @override Widget build(BuildContext context) { DateTime? date = medium.creationDate ?? medium.modifiedDate; return MaterialApp( home: Scaffold( appBar: AppBar( leading: IconButton( onPressed: () => Navigator.of(context).pop(), icon: Icon(Icons.arrow_back_ios), ), title: date != null ? Text(date.toLocal().toString()) : null, ), body: Container( alignment: Alignment.center, child: medium.mediumType == MediumType.image ? FadeInImage( fit: BoxFit.cover, placeholder: MemoryImage(kTransparentImage), image: PhotoProvider(mediumId: medium.id), ) : VideoProvider( mediumId: medium.id, ), ), ), ); } } class VideoProvider extends StatefulWidget { final String mediumId; const VideoProvider({ required this.mediumId, }); @override _VideoProviderState createState() => _VideoProviderState(); } class _VideoProviderState extends State { VideoPlayerController? _controller; File? _file; @override void initState() { WidgetsBinding.instance.addPostFrameCallback((_) { initAsync(); }); super.initState(); } Future initAsync() async { try { _file = await PhotoGallery.getFile(mediumId: widget.mediumId); _controller = VideoPlayerController.file(_file!); _controller?.initialize().then((_) { // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. setState(() {}); }); } catch (e) { print("Failed : $e"); } } @override Widget build(BuildContext context) { return _controller == null || !_controller!.value.isInitialized ? Container() : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AspectRatio( aspectRatio: _controller!.value.aspectRatio, child: VideoPlayer(_controller!), ), TextButton( onPressed: () { setState(() { _controller!.value.isPlaying ? _controller!.pause() : _controller!.play(); }); }, child: Icon( _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow, ), ), ], ); } }