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.
You will need the following to get started:
The code for this tutorial is available on my GitHub repository. Feel free to clone and follow along.
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.
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:
1module.exports = ({ env }) => ({
2 upload: {
3 config: {
4 providerOptions: {
5 localServer: {
6 maxage: 300000,
7 },
8 },
9 },
10 },
11});
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.
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.
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
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:
1import 'dart:convert';
2import 'package:http/http.dart' as http;
3
4class StrapiService {
5 static const String baseUrl = 'http://localhost:1337';
6
7 Future<List<dynamic>> getMediaFiles() async {
8 final response = await http.get(Uri.parse('$baseUrl/api/upload/files'));
9 if (response.statusCode == 200) {
10 final jsonData = json.decode(response.body);
11 return jsonData;
12 } else {
13 throw Exception('Failed to load media files');
14 }
15 }
16}
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:
1dependencies:
2 flutter:
3 sdk: flutter
4 http: ^0.13.5
5 file_picker: ^8.0.3
6 http_parser: ^4.0.2
Then run the command below to install the dependency:
flutter pub get
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:
1import 'package:flutter/material.dart';
2import 'package:media_library_app/features/services/strapi_service.dart';
3
4class HomePage extends StatefulWidget {
5 const HomePage({super.key});
6
7 @override
8 _HomePageState createState() => _HomePageState();
9}
10
11class _HomePageState extends State<HomePage> {
12 List<dynamic> mediaFiles = [];
13 StrapiService strapiService = StrapiService();
14
15 @override
16 void initState() {
17 super.initState();
18 fetchMediaFiles();
19 }
20
21 Future<void> fetchMediaFiles() async {
22 try {
23 final files = await strapiService.getMediaFiles();
24 setState(() {
25 mediaFiles = files;
26 });
27 } catch (e) {
28 print('Error fetching media files: $e');
29 }
30 }
31
32 @override
33 Widget build(BuildContext context) {
34 return Scaffold(
35 appBar: AppBar(
36 title: const Text('Strapi Media Library'),
37 ),
38 body: Padding(
39 padding: const EdgeInsets.all(8.0),
40 child: ListView.builder(
41 itemCount: mediaFiles.length,
42 itemBuilder: (context, index) {
43 final file = mediaFiles[index];
44 final imageUrl = StrapiService.baseUrl + file['url'];
45 return ListTile(
46 title: Text(file['name']),
47 subtitle: Text(file['mime']),
48 leading: Image.network(imageUrl),
49 );
50 },
51 ),
52 ),
53 );
54 }
55}
The above code will:
HomePage StatefulWidget
class to allow you to handle the application state on this screen.mediaFiles
empty array to store the media files data and create an instance of the StrapService
class to allow you access to the methods in the class.fetchMediaFiles()
method to call the strapiService.getMediaFiles()
method to retrieve the media from Strapi Media Library and call the setState
function to modify the app's state and set the retrieved files data to the mediaFiles
array. initState
method to execute the code in fetchMediaFiles()
when the widget is first created.ListView
widget.Now update your main.dart
file to render the HomePage
widget in your Flutter UI:
1import 'package:flutter/material.dart';
2import 'package:strapi_media_app/features/screens/home_screen.dart';
3
4void main() {
5 runApp(const MyApp());
6}
7
8class MyApp extends StatelessWidget {
9 const MyApp({super.key});
10
11 // This widget is the root of your application.
12 @override
13 Widget build(BuildContext context) {
14 return MaterialApp(
15 title: 'Flutter Demo',
16 theme: ThemeData(
17 colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
18 useMaterial3: true,
19 ),
20 home: const HomePage(),
21 );
22 }
23}
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.
1import 'dart:convert';
2import 'dart:io';
3import 'package:http/http.dart' as http;
4import 'package:http_parser/http_parser.dart'; // Import the http_parser package
5
6class StrapiService {
7 static const String baseUrl = 'http://localhost:1337';
8
9 Future<List<dynamic>> getMediaFiles() async {
10 try {
11 final response = await http.get(Uri.parse('$baseUrl/api/upload/files'));
12 if (response.statusCode == 200) {
13 final jsonData = json.decode(response.body);
14 return jsonData;
15 } else {
16 throw Exception('Failed to load media files');
17 }
18 } catch (e) {
19 throw Exception(e);
20 }
21 }
22
23 Future<dynamic> uploadMediaFile(File file) async {
24 var request = http.MultipartRequest(
25 'POST',
26 Uri.parse('$baseUrl/api/upload'),
27 );
28 var stream = http.ByteStream(file.openRead());
29 stream.cast();
30 var length = await file.length();
31 var multipartFileSign = http.MultipartFile(
32 'files',
33 stream,
34 length,
35 filename: file.path.split('/').last,
36 contentType: MediaType('image', 'jpeg'),
37 );
38
39 request.files.add(multipartFileSign);
40
41 var response = await request.send();
42 if (response.statusCode == 200) {
43 return response.stream.bytesToString();
44 } else {
45 throw Exception('Failed to upload media file');
46 }
47 }
48}
This code will:
http.MultipartRequest
to the /upload
endpoint using the POST method.http.ByteStream
from the file and specifying the length. http.MultipartFile
from the byte stream, specifying the file name, length, content type, and field name. 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.
1 import 'dart:convert';
2import 'dart:io';
3// import 'package:file_picker/file_picker.dart';
4import 'package:file_picker/file_picker.dart';
5import 'package:flutter/material.dart';
6import 'package:strapi_media_app/features/services/strapi_service.dart';
7
8class HomePage extends StatefulWidget {
9 const HomePage({super.key});
10
11 @override
12 _HomePageState createState() => _HomePageState();
13}
14
15class _HomePageState extends State<HomePage> {
16 List<dynamic> mediaFiles = [];
17 StrapiService strapiService = StrapiService();
18
19 @override
20 void initState() {
21 super.initState();
22 fetchMediaFiles();
23 }
24
25 Future<void> fetchMediaFiles() async {
26 try {
27 final files = await strapiService.getMediaFiles();
28 setState(() {
29 mediaFiles = files;
30 });
31 } catch (e) {
32 print('Error fetching media files: $e');
33 }
34 }
35
36 Future<void> uploadMediaFile(context) async {
37 FilePickerResult? result = await FilePicker.platform.pickFiles();
38 if (result != null) {
39 File file = File(result.files.single.path!);
40 try {
41 final response = await strapiService.uploadMediaFile(file);
42 List<dynamic> jsonResponse = jsonDecode(response);
43 setState(() {
44 mediaFiles.add(jsonResponse[0]);
45 });
46 ScaffoldMessenger.of(context).showSnackBar(
47 const SnackBar(
48 content: Text('File uploaded successfully'),
49 ),
50 );
51 } catch (e) {
52 ScaffoldMessenger.of(context).showSnackBar(
53 SnackBar(
54 content: Text('Error uploading media file: $e'),
55 ),
56 );
57 }
58 } else {
59 ScaffoldMessenger.of(context).showSnackBar(
60 const SnackBar(
61 content: Text('No file selected'),
62 ),
63 );
64 }
65 }
66
67 @override
68 Widget build(BuildContext context) {
69 return Scaffold(
70 appBar: AppBar(
71 title: const Text('Strapi Media Library'),
72 ),
73 body: Padding(
74 padding: const EdgeInsets.all(8.0),
75 child: ListView.builder(
76 itemCount: mediaFiles.length,
77 itemBuilder: (context, index) {
78 final file = mediaFiles[index];
79 final imageUrl = StrapiService.baseUrl + file['url'];
80 return ListTile(
81 title: Text(file['name']),
82 subtitle: Text(file['mime']),
83 leading: Image.network(imageUrl),
84 );
85 },
86 ),
87 ),
88 floatingActionButton: FloatingActionButton(
89 onPressed: () => uploadMediaFile(context),
90 child: const Icon(Icons.add),
91 ),
92 );
93 }
94}
The above code will:
uploadMediaFile
method in the _HomePageState
class to call the uploadMediaFile
method from the StrapiService
class to upload the selected files to the Strapi Media Library. 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.Below is a demonstration of what our application looks like.
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.
I am Software Engineer and Technical Writer. Proficient Server-side scripting and Database setup. Agile knowledge of Python, NodeJS, ReactJS, and PHP. When am not coding, I share my knowledge and experience with the other developers through technical articles