I'm trying to build a local media gallery kind of app with FlutterFlow, I want to display photos and videos from local storage. I did some research and I got this code but I'm new to this and I wanna learn how can I implement all this code into FlutterFlow the best way possible or if there's anoher way to dot it using FF widgets with some custom code for functionality ??
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
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:appinio_video_player/appinio_video_player.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Album>? _albums;
bool _loading = false;
@override
void initState() {
super.initState();
_loading = true;
initAsync();
}
Future<void> initAsync() async {
if (await _promptPermissionSetting()) {
List<Album> albums = await PhotoGallery.listAlbums();
setState(() {
_albums = albums;
_loading = false;
});
}
setState(() {
_loading = false;
});
}
Future<bool> _promptPermissionSetting() async {
if (Platform.isIOS) {
if (await Permission.photos.request().isGranted || await Permission.storage.request().isGranted) {
return true;
}
}
if (Platform.isAndroid) {
if (await Permission.storage.request().isGranted ||
await Permission.photos.request().isGranted &&
await Permission.videos.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: <Widget>[
...?_albums?.map(
(album) => GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => AlbumPage(album)),
),
child: Column(
children: <Widget>[
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(
album: album,
highQuality: true,
),
),
), //Image thumbnail
),
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,
),
),
), //Album name
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,
),
),
), //Iterms count
],
),
),
),
],
),
);
},
),
),
);
}
}
class AlbumPage extends StatefulWidget {
final Album album;
AlbumPage(Album album) : album = album;
@override
State<StatefulWidget> createState() => AlbumPageState();
}
class AlbumPageState extends State<AlbumPage> {
List<Medium>? _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: <Widget>[
...?_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
? GestureDetector(
onTap: () async {
PhotoGallery.deleteMedium(mediumId: medium.id);
},
child: FadeInImage(
fit: BoxFit.cover,
placeholder: MemoryImage(kTransparentImage),
image: PhotoProvider(mediumId: medium.id),
),
)
: Center(
child: VideoProvider(
mediumId: medium.id,
),
),
),
),
);
}
}
class VideoProvider extends StatefulWidget {
final String mediumId;
const VideoProvider({
required this.mediumId,
});
@override
_VideoProviderState createState() => _VideoProviderState();
}
class _VideoProviderState extends State<VideoProvider> {
late VideoPlayerController _videoPlayerController;
late CustomVideoPlayerController _customVideoPlayerController;
File? _file;
bool initialized = false;
final CustomVideoPlayerSettings _customVideoPlayerSettings =
const CustomVideoPlayerSettings(showSeekButtons: false);
@override
void initState() {
super.initState();
initAsync(); // Initialize the video player in initState
}
Future<void> initAsync() async {
try {
_file = await PhotoGallery.getFile(mediumId: widget.mediumId);
_videoPlayerController = VideoPlayerController.file(_file!);
await _videoPlayerController.initialize();
_customVideoPlayerController = CustomVideoPlayerController(
context: context,
videoPlayerController: _videoPlayerController,
customVideoPlayerSettings: _customVideoPlayerSettings,
additionalVideoSources: {
"Max": _videoPlayerController,
},
);
setState(() {
initialized = true;
});
} catch (e) {
print("Failed : $e");
}
}
@override
void dispose() {
_customVideoPlayerController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!initialized) {
return CupertinoActivityIndicator(); // Show loading indicator until initialized
}
return CupertinoPageScaffold(
child: SafeArea(
child: Column(
children: [
CustomVideoPlayer(
customVideoPlayerController: _customVideoPlayerController,
),
],
),
),
);
}
}
I tried to do it but cuz I'm new to flutter and flutterflow I wasn't able to do it
You need to make a custom widget.
Every widget in FlutterFlow is stateful, so you can pretty much reuse all the code there. You just need to return the widgets returned by the
Widget build(BuildContext context)
functions and they will be available for use.You can write whatever additional code you need to in the custom widget.
Here is an example of a custom widget I wrote to display version info from the package_info_plus library on a card, maybe it can inspire you: