Strapi is an open-source, Node.js-based, Headless Content Management System(CMS) that allows you to streamline the process of managing content, giving you the freedom to use your favorite tools and frameworks. It has a Media Library plugin that allows you to upload, save, and manage different media files such as images, videos, and documents.
In this tutorial, you'll learn how to build a media gallery application using Flutter and Strapi's Media Library plugin.
Prerequisites
You will need the following to get started:
- Node.js v18 or later.
- A code editor on your computer.
- Node.js package manager v6 and above.
- Python
- Set up your Flutter environment if you haven't already.
The code for this tutorial is available on my GitHub repository. Feel free to clone and follow along.
Setting up Strapi with the Media Library
Strapi Installation
Start by creating a new Strapi backend project by running the command below:
npx create-strapi-app@latest media-app --quickstart
The above command will scaffold a new Strapi content management system project and install the required Node.js dependencies.
Fill out the forms to create your administrator user account.
Install Strapi Media Library Plugin
Go back to your terminal, and quit the server with Ctrl+ C
. Navigate to the project directory and enable the Media Libray Plugin by running the command below:
cd media-app
npm i @strapi/plugin-upload
Update your config/plugin.js
file to configure the upload plugin. There are many plugins in the Strapi marketplace for uploads including Firebase storage, Cloudflare, etc. But there are only 3 providers maintained by Strapi which are Local, Amazon S3, and Cloudinary. For this demonstration, you'll use the Local plugin:
module.exports = ({ env }) => ({
upload: {
config: {
providerOptions: {
localServer: {
maxage: 300000,
},
},
},
},
});
Enable Public Access for Upload API
In order to be able to perform some operations on the upload plugin, we need to enable public access.
From the left sidebar, click on Settings. Again, on the left panel under USERS & PERMISSIONS PLUGIN, click on Roles, then click on Public from the table on the right. Now scroll down, click on Upload
, and tick Select all. After that click the "Save" button.
Uploading Media Files to Strapi
Back to your admin panel, in the left sidebar, click on the Media Library section. Then click Add new assets -> FROM COMPUTER -> Browse Files and you'll be prompted to upload files from your local computer. Select at least three files and click Upload assets to library to upload them.
Creating a Flutter Project
Open a new tab on your terminal and create a new Flutter application by running the command below:
flutter create media_library_app
Once the project is created, open it in your IDE or code editor. Change the directory to the project folder and run the application with the following commands:
cd media_library_app
flutter run
Retrieving Media Files from Flutter
In your projects lib
directory, create a features/services
directory. Create a strapi_service.dart
file in the services
directory and add the code to fetch all the assets you added to your Media Library:
import 'dart:convert';
import 'package:http/http.dart' as http;
class StrapiService {
static const String baseUrl = 'http://localhost:1337';
Future<List<dynamic>> getMediaFiles() async {
final response = await http.get(Uri.parse('$baseUrl/api/upload/files'));
if (response.statusCode == 200) {
final jsonData = json.decode(response.body);
return jsonData;
} else {
throw Exception('Failed to load media files');
}
}
}
This service class provides a getMediaFiles
method that makes a GET
request to the Strapi server's /upload/files
endpoint to return the list of media files.
For the above code to work, open the pubspec.yaml
file and add the http
, file_picker
and http_parser
module in the dependency section to allow you send API requests to your Strapi backend, select images from your device file system and to parse the HTTP response:
dependencies:
flutter:
sdk: flutter
http: ^0.13.5
file_picker: ^8.0.3
http_parser: ^4.0.2
Then run the command below to install the dependency:
flutter pub get
Displaying Media Files in Flutter
Create a new directory called screens
in the lib/features
directory to create our Flutter UI. Then create a home_screen.dart
file inside the screens
folder and add the code snippets below:
import 'package:flutter/material.dart';
import 'package:media_library_app/features/services/strapi_service.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<dynamic> mediaFiles = [];
StrapiService strapiService = StrapiService();
@override
void initState() {
super.initState();
fetchMediaFiles();
}
Future<void> fetchMediaFiles() async {
try {
final files = await strapiService.getMediaFiles();
setState(() {
mediaFiles = files;
});
} catch (e) {
print('Error fetching media files: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Strapi Media Library'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: mediaFiles.length,
itemBuilder: (context, index) {
final file = mediaFiles[index];
final imageUrl = StrapiService.baseUrl + file['url'];
return ListTile(
title: Text(file['name']),
subtitle: Text(file['mime']),
leading: Image.network(imageUrl),
);
},
),
),
);
}
}
The above code will:
- Create a
HomePage StatefulWidget
class to allow you to handle the application state on this screen. - Create
mediaFiles
empty array to store the media files data and create an instance of theStrapService
class to allow you access to the methods in the class. - Add a
fetchMediaFiles()
method to call thestrapiService.getMediaFiles()
method to retrieve the media from Strapi Media Library and call thesetState
function to modify the app's state and set the retrieved files data to themediaFiles
array. - Call the
initState
method to execute the code infetchMediaFiles()
when the widget is first created. - Render the media data using the
ListView
widget.
Now update your main.dart
file to render the HomePage
widget in your Flutter UI:
import 'package:flutter/material.dart';
import 'package:strapi_media_app/features/screens/home_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
Handling Media Uploads from Flutter
Next, update the code in your lib/features/services/strapi_service.dart
file to add a new method to send a POST request to the /upload
endpoint to upload new files to the Media Library.
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart'; // Import the http_parser package
class StrapiService {
static const String baseUrl = 'http://localhost:1337';
Future<List<dynamic>> getMediaFiles() async {
try {
final response = await http.get(Uri.parse('$baseUrl/api/upload/files'));
if (response.statusCode == 200) {
final jsonData = json.decode(response.body);
return jsonData;
} else {
throw Exception('Failed to load media files');
}
} catch (e) {
throw Exception(e);
}
}
Future<dynamic> uploadMediaFile(File file) async {
var request = http.MultipartRequest(
'POST',
Uri.parse('$baseUrl/api/upload'),
);
var stream = http.ByteStream(file.openRead());
stream.cast();
var length = await file.length();
var multipartFileSign = http.MultipartFile(
'files',
stream,
length,
filename: file.path.split('/').last,
contentType: MediaType('image', 'jpeg'),
);
request.files.add(multipartFileSign);
var response = await request.send();
if (response.statusCode == 200) {
return response.stream.bytesToString();
} else {
throw Exception('Failed to upload media file');
}
}
}
This code will:
- Create an HTTP request to allow you upload files using
http.MultipartRequest
to the/upload
endpoint using the POST method. - Prepare the selected files for your upload by opening a byte stream using
http.ByteStream
from the file and specifying the length. - Creates a multipart file using
http.MultipartFile
from the byte stream, specifying the file name, length, content type, and field name. - Add the multipart file to the request. If the response status code is 200 (OK), it reads the response stream and converts it to a string using
response.stream.bytesToString()
, then returns the result.
Then in your lib/features/screens/home_screen.dart
file, add a new method to the _HomePageState
class to handle file uploads.
import 'dart:convert';
import 'dart:io';
// import 'package:file_picker/file_picker.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:strapi_media_app/features/services/strapi_service.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<dynamic> mediaFiles = [];
StrapiService strapiService = StrapiService();
@override
void initState() {
super.initState();
fetchMediaFiles();
}
Future<void> fetchMediaFiles() async {
try {
final files = await strapiService.getMediaFiles();
setState(() {
mediaFiles = files;
});
} catch (e) {
print('Error fetching media files: $e');
}
}
Future<void> uploadMediaFile(context) async {
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
File file = File(result.files.single.path!);
try {
final response = await strapiService.uploadMediaFile(file);
List<dynamic> jsonResponse = jsonDecode(response);
setState(() {
mediaFiles.add(jsonResponse[0]);
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('File uploaded successfully'),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error uploading media file: $e'),
),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('No file selected'),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Strapi Media Library'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: mediaFiles.length,
itemBuilder: (context, index) {
final file = mediaFiles[index];
final imageUrl = StrapiService.baseUrl + file['url'];
return ListTile(
title: Text(file['name']),
subtitle: Text(file['mime']),
leading: Image.network(imageUrl),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => uploadMediaFile(context),
child: const Icon(Icons.add),
),
);
}
}
The above code will:
- Create an
uploadMediaFile
method in the_HomePageState
class to call theuploadMediaFile
method from theStrapiService
class to upload the selected files to the Strapi Media Library. - Uses the
FilePicker
package to allow the user to select a file from their device. If the file selected is not null, it will retrieve the selected file and prepares it for upload, else it shows a snackbar with a success or error message.
Demo
Below is a demonstration of what our application looks like.
Conclusion
Throughout this tutorial, you've learned how to build a media gallery application using Flutter and Strapi Media Library. You have set up a Strapi project, installed the Strapi upload plugin in the project, and uploaded media data to the Strapi Media Library. You created a Flutter application to make API requests to the Media Libray to fetch, display, and upload new files.