Previously we did Flutter Firebase CRUD for text information. Flutter firebase image and file upload we will use three plugins.
firebase_storage:
image_picker:
path:
First create a project and then install the above three plugins.
Android settings
Android doesn't need much settings. Just in AndroidManifest.xml file do the follow
android:requestLegacyExternalStorage=”true”
iOS settings
iOS setting is also relatively easy. Just put the code in the Info.plist right after first <dict>
<key>NSCameraUsageDescription</key>
<string>Some Description</string>
<key>NSMicrophoneUsageDescription</key>
<string>Some Description</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Some Description</string>
Warnings: iOS simulator on Mac M1 chip doesn't support a stable image_picker plugin.
Core functions
≥ await picker.pickImage()
≥ await storage.ref(fileName).putFile()
Complete code
// main.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path/path.dart' as path;
import 'package:image_picker/image_picker.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize a new Firebase App instance
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// Remove the debug banner
debugShowCheckedModeBanner: false,
title: 'dbestech',
theme: ThemeData(primarySwatch: Colors.green),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
FirebaseStorage storage = FirebaseStorage.instance;
// Select and image from the gallery or take a picture with the camera
// Then upload to Firebase Storage
Future<void> _upload(String inputSource) async {
final picker = ImagePicker();
XFile? pickedImage;
try {
pickedImage = await picker.pickImage(
source: inputSource == 'camera'
? ImageSource.camera
: ImageSource.gallery,
maxWidth: 1920);
final String fileName = path.basename(pickedImage!.path);
File imageFile = File(pickedImage.path);
try {
// Uploading the selected image with some custom meta data
await storage.ref(fileName).putFile(
imageFile,
SettableMetadata(customMetadata: {
'uploaded_by': 'learners',
'description': 'learning the hacks'
}));
// Refresh the UI
setState(() {});
} on FirebaseException catch (error) {
if (kDebugMode) {
print(error);
}
}
} catch (err) {
if (kDebugMode) {
print(err);
}
}
}
// Retriew the uploaded images
// This function is called when the app launches for the first time or when an image is uploaded or deleted
Future<List<Map<String, dynamic>>> _loadImages() async {
List<Map<String, dynamic>> files = [];
final ListResult result = await storage.ref().list();
final List<Reference> allFiles = result.items;
await Future.forEach<Reference>(allFiles, (file) async {
final String fileUrl = await file.getDownloadURL();
final FullMetadata fileMeta = await file.getMetadata();
files.add({
"url": fileUrl,
"path": file.fullPath,
"uploaded_by": fileMeta.customMetadata?['uploaded_by'] ?? 'Nobody',
"description":
fileMeta.customMetadata?['description'] ?? 'No description'
});
});
return files;
}
// Delete the selected image
// This function is called when a trash icon is pressed
Future<void> _delete(String ref) async {
await storage.ref(ref).delete();
// Rebuild the UI
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dbestech),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton.icon(
onPressed: () => _upload('camera'),
icon: const Icon(Icons.camera),
label: const Text('camera')),
ElevatedButton.icon(
onPressed: () => _upload('gallery'),
icon: const Icon(Icons.library_add),
label: const Text('Gallery')),
],
),
Expanded(
child: FutureBuilder(
future: _loadImages(),
builder: (context,
AsyncSnapshot<List<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
final Map<String, dynamic> image =
snapshot.data![index];
return Card(
margin: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(
dense: false,
leading: Image.network(image['url']),
title: Text(image['uploaded_by']),
subtitle: Text(image['description']),
trailing: IconButton(
onPressed: () => _delete(image['path']),
icon: const Icon(
Icons.delete,
color: Colors.red,
),
),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
],
),
),
);
}
}