Flutter Firebase Storage
How to implement Firebase Storage with Flutter
Pengenalan
Seperti yang kita ketahui bahwa Firebase merupakan salah satu mBaaS yang cukup terkenal dikalangan mobile developer karena fitur-fiturnya yang cukup lengkap dan bagus diantaranya adalah sebagai berikut:
- Firebase Authentication
- Firebase Database
- Firebase Storage
- Firebase Crashlytics
- Firebase Cloud Messaging
Diatas merupakan beberapa fitur yang sering saya pakai dan sebenarnya masih banyak lagi fitur-fitur lainnya dari Firebase yang tidak bisa saya sebutkan pada tulisan ini. Selain itu, saya juga pernah membahas beberapa fitur Firebase di Flutter yang bisa kamu baca di link berikut ya.
Pada tulisan ini yang akan saya bahas adalah Firebase Storage. Firebase Storage berfungsi untuk melakukan penyimpanan file seperti gambar, video, audio, dokumen, dan lain-lainnya.
Design App
Adapun referensi design dari contoh app yang akan kita buat pada tulisan ini adalah sebagai berikut.
Dan berikut adalah output dari app yang akan kita buat.
Contoh Projek
Buat Projek
Silakan kita buat projek baru dengan nama flutter_firebase_storage.
Persiapan Aset
Adapun beberapa aset yang perlu kita persiapkan pada pembuatan contoh projek kali ini yaitu sebagai berikut.
- Gambar foto profil atau pakai foto kamu sendiri juga boleh.
- Foto yang akan diupload ke Firebase Storage dan
- Font Mont Extra Light.
Jika kamu ingin mengikuti tulisan ini menggunakan aset yang sama maka kamu bisa unduh aset-aset tersebut di sini ya.
Persiapan Projek Firebase Console
Perlu diketahui bahwa untuk bisa memulai contoh projek kali ini kita memerlukan sebuah projek di Firebase Console. Jadi, bagi kamu yang belum ada atau belum pernah buat projek di Firebase Console silakan buat dulu ya. Ditulisan ini saya tidak akan menjelaskannya karena menurut saya langkah-langkah pembuatannya cukup gampang. Pastikan saja pada langkah ini kamu semua sudah bisa mengunduh file google-service.json untuk Android dan GoogleService-Info.plist untuk iOS dan masukkan file tersebut kedalam projek kita.
Persiapan pubspec.yaml
Sekarang kita perlu buka file pubspec.yaml dan ubah bagian dependencies-nya menjadi seperti berikut.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.3
# Flutter plugin for Firebase Core, enabling connecting to multiple Firebase apps.
firebase_core: ^0.4.4+3
# Flutter plugin for Firebase Cloud Storage, a powerful, simple, and cost-effective object
# storage service for Android and iOS.
firebase_storage: ^3.1.5
# A flutter plugin for adapting screen and font size.
flutter_screenutil: ^1.1.0
# The Font Awesome Icon pack available as Flutter Icons.
font_awesome_flutter: ^8.8.1
# Flutter plugin for selecting images from the Android and iOS image library, and taking new
# pictures with the camera.
image_picker: ^0.6.4
# Flutter plugin for reading and writing simple key-value pairs
shared_preferences: ^0.5.6+3
# Flutter library to load and cache network images. Can also be used with placeholder and
# error widgets.
cached_network_image: ^2.0.0
Dan ubah juga bagian aset dan font-nya.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/images/
fonts:
- family: Mont
fonts:
- asset: assets/fonts/mont_extralight.otf
Sebelum menjalankan perintah flutter pub get
pastikan terlebih dahulu bahwa kamu sudah memasukkan file-file aset yang diperlukan kedalam direktori assets. File-file ini berasal dari file aset yang sudah kamu unduh diawal tadi ya.
Setelah itu baru jalankan perintah flutter pub get
.
Pembuatan UI
Sebelum kita masuk ke fiturnya kita perlu membuat UI-nya terlebih dahulu agar nanti pada saat pembuatan fiturnya kita lebih gampang.
Buat Tampilan Dasar
Silakan buka file main.dart dan ubah menjadi seperti berikut.
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
enum TypeOperation {
upload,
download,
delete,
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final GlobalKey<ScaffoldState> scaffoldState = GlobalKey<ScaffoldState>();
final String keyMyPosts = 'keyMyPosts';
final List<String> myPosts = [];
final FirebaseStorage firebaseStorage = FirebaseStorage.instance;
SharedPreferences sharedPreferences;
TypeOperation typeOperation = TypeOperation.download;
bool isLoading = true;
bool isSuccess = true;
bool isGridView = true;
@override
void initState() {
// TODO: lakukan sesuatu di initState
super.initState();
}
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return Scaffold(
key: scaffoldState,
body: Stack(
children: <Widget>[
Center(
child: Text('Hello World'),
),
],
)
);
}
}
Ingat ya, pada tulisan ini kita cuma menggunakan satu file saja yaitu file main.dart.
Buat Widget Header
Selanjutnya, kita akan buat bagian AppBar-nya. Kali ini kita buatnya custom ya tidak menggunakan widget AppBar. Silakan buat fungsi berikut ya.
Widget _buildWidgetBackgroundHeader() {
return Stack(
children: <Widget>[
Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/woman_holding_guitar.jpg'),
fit: BoxFit.cover,
),
),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 10,
sigmaY: 10,
),
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.0),
),
),
),
),
SafeArea(
child: SafeArea(
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setHeight(48),
right: ScreenUtil().setWidth(48),
left: ScreenUtil().setHeight(48),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
onTap: () {
// TODO: lakukan sesuatu ketika icon camera di-tap
},
child: Icon(
Icons.camera,
color: Color(0xFF251F1F),
),
),
GestureDetector(
onTap: () {
// TODO: lakukan sesuatu ketika icon delete di-tap
},
child: Icon(
Icons.delete,
color: Color(0xFF251F1F),
),
),
],
),
),
),
),
],
);
}
Dan jangan lupa panggil fungsi tersebut didalam fungsi build ya.
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return Scaffold(
key: scaffoldState,
body: Stack(
children: <Widget>[
_buildWidgetBackgroundHeader(),
],
),
);
}
Buat Widget Container Content Profile
Selanjutnya kita akan membuat fungsi untuk membuat widget container content profile-nya.
Widget _buildWidgetContentProfile() {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(256),
),
decoration: BoxDecoration(
color: Color(0xFF251F1F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
ScreenUtil().setWidth(72),
),
topRight: Radius.circular(
ScreenUtil().setWidth(72),
),
),
),
// TODO: buat content profile
);
}
Dan panggil fungsi tersebut didalam fungsi build.
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return Scaffold(
key: scaffoldState,
body: Stack(
children: <Widget>[
_buildWidgetBackgroundHeader(),
_buildWidgetContentProfile(),
],
),
);
}
Buat Widget Foto Profil
Selanjutnya, kita akan membuat fungsi yang akan menampilkan widget foto profil.
Widget _buildWidgetPhotoProfile() {
return Container(
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(160),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: ScreenUtil().setWidth(256),
height: ScreenUtil().setWidth(256),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
ScreenUtil().setWidth(48),
),
image: DecorationImage(
image: AssetImage('assets/images/woman_holding_guitar.jpg'),
fit: BoxFit.cover,
),
),
)
],
)
);
}
Dan jangan lupa panggil fungsi tersebut didalam fungsi build ya.
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return Scaffold(
key: scaffoldState,
body: Stack(
children: <Widget>[
_buildWidgetBackgroundHeader(),
_buildWidgetContentProfile(),
_buildWidgetPhotoProfile(),
],
),
);
}
Buat Widget Nama dan Profesinya
Langkah selanjutnya adalah kita akan membuat widget yang akan menampilkan info profil-nya seperti nama dan job title. Hal pertama yang perlu kita buat sebelum lanjut buat widget info profil adalah kita perlu membuat WidgetTextMont seperti berikut.
class WidgetTextMont extends StatelessWidget {
final String text;
final double fontSize;
final FontWeight fontWeight;
final Color textColor;
final TextAlign textAlign;
WidgetTextMont(
this.text, {
this.fontSize = 36,
this.fontWeight = FontWeight.normal,
this.textColor = Colors.white,
this.textAlign = TextAlign.left,
});
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(
color: textColor,
fontSize: ScreenUtil().setSp(fontSize),
fontWeight: fontWeight,
fontFamily: 'Mont',
),
textAlign: textAlign,
);
}
}
Lalu, silakan kita ubah kode komentar berikut // TODO: buat content profile
menjadi seperti berikut.
Widget _buildWidgetContentProfile() {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(256),
),
decoration: BoxDecoration(
color: Color(0xFF251F1F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
ScreenUtil().setWidth(72),
),
topRight: Radius.circular(
ScreenUtil().setWidth(72),
),
),
),
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setWidth(128 + 64),
right: ScreenUtil().setWidth(64),
left: ScreenUtil().setWidth(64),
),
child: Column(
children: <Widget>[
WidgetTextMont(
'Angelica Mayuko',
fontSize: 48,
fontWeight: FontWeight.bold,
),
SizedBox(
height: ScreenUtil().setHeight(16),
),
WidgetTextMont(
'Software Engineer focused on iOS',
textColor: Colors.grey,
fontWeight: FontWeight.w400,
),
SizedBox(
height: ScreenUtil().setHeight(72),
),
// TODO: buat widget untuk menampilkan jumlah posts, followers dan following
],
),
),
);
}
Buat Widget Jumlah Post, Follower dan Following
Selanjutnya, kita akan membuat widget yang akan menampilkan info jumlah post, follower, dan following. Silakan kita ubah kode komentar // TODO: buat widget untuk menampilkan jumlah posts, followers dan following
menjadi seperti berikut.
Widget _buildWidgetContentProfile() {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(256),
),
decoration: BoxDecoration(
color: Color(0xFF251F1F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
ScreenUtil().setWidth(72),
),
topRight: Radius.circular(
ScreenUtil().setWidth(72),
),
),
),
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setWidth(128 + 64),
right: ScreenUtil().setWidth(64),
left: ScreenUtil().setWidth(64),
),
child: Column(
children: <Widget>[
WidgetTextMont(
'Angelica Mayuko',
fontSize: 48,
fontWeight: FontWeight.bold,
),
SizedBox(
height: ScreenUtil().setHeight(16),
),
WidgetTextMont(
'Software Engineer focused on iOS',
textColor: Colors.grey,
fontWeight: FontWeight.w400,
),
SizedBox(
height: ScreenUtil().setHeight(72),
),
_buildWidgetPostsFollowersFollowing(),
SizedBox(
height: ScreenUtil().setHeight(32),
),
// TODO: buat widget untuk menampilkan button follow dan chat
],
),
),
);
}
Seperti yang kamu lihat pada kode diatas kita ada memanggil fungsi _buildWidgetPostsFollowersFollowing()
maka, kita perlu membuat fungsi tersebut.
Widget _buildWidgetPostsFollowersFollowing() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
WidgetTextMont(
'${myPosts.length}',
fontSize: 42,
fontWeight: FontWeight.bold,
),
WidgetTextMont(
'Posts',
textColor: Colors.grey,
fontWeight: FontWeight.bold,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
WidgetTextMont(
'1.2M',
fontSize: 42,
fontWeight: FontWeight.bold,
),
WidgetTextMont(
'Followers',
textColor: Colors.grey,
fontWeight: FontWeight.bold,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
WidgetTextMont(
'304',
fontSize: 42,
fontWeight: FontWeight.bold,
),
WidgetTextMont(
'Following',
textColor: Colors.grey,
fontWeight: FontWeight.bold,
),
],
),
],
);
}
Buat Widget Button Follow dan Chat
Langkah berikutnya ialah kita akan membuat widget untuk menampilkan button follow dan chat. Silakan kita ubah kode komentar berikut
// TODO: buat widget untuk menampilkan button follow dan chat
Menjadi seperti berikut.
Widget _buildWidgetContentProfile() {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(256),
),
decoration: BoxDecoration(
color: Color(0xFF251F1F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
ScreenUtil().setWidth(72),
),
topRight: Radius.circular(
ScreenUtil().setWidth(72),
),
),
),
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setWidth(128 + 64),
right: ScreenUtil().setWidth(64),
left: ScreenUtil().setWidth(64),
),
child: Column(
children: <Widget>[
WidgetTextMont(
'Angelica Mayuko',
fontSize: 48,
fontWeight: FontWeight.bold,
),
SizedBox(
height: ScreenUtil().setHeight(16),
),
WidgetTextMont(
'Software Engineer focused on iOS',
textColor: Colors.grey,
fontWeight: FontWeight.w400,
),
SizedBox(
height: ScreenUtil().setHeight(72),
),
_buildWidgetPostsFollowersFollowing(),
SizedBox(
height: ScreenUtil().setHeight(32),
),
_buildWidgetButtonFollowAndChat(),
SizedBox(
height: ScreenUtil().setHeight(48),
),
// TODO: buat widget untuk menampilkan header my posts
],
),
),
);
}
Selanjutnya, kita buat fungsi baru bernama _buildWidgetButtonFollowAndChat()
.
Widget _buildWidgetButtonFollowAndChat() {
return Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text(
'Follow',
style: TextStyle(
fontFamily: 'Mont',
fontWeight: FontWeight.bold,
),
),
textColor: Colors.white,
color: Color(0xFFFC8157),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
onPressed: () {
/* Nothing to do in here */
},
),
),
SizedBox(
width: ScreenUtil().setWidth(48),
),
Expanded(
child: RaisedButton(
child: Icon(
Icons.chat,
color: Colors.white,
size: 20,
),
color: Color(0xFFFFD4BE),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
onPressed: () {
/* Nothing to do in here */
},
),
),
],
);
}
Buat Widget Header My Posts dan Switch View
Selanjutnya kita akan membuat widget yang menampilkan label My Posts dan icon untuk berganti view dari mode list ke grid dan sebaliknya. Silakan kita ubah kode komentar berikut
// TODO: buat widget untuk menampilkan header my posts
Menjadi seperti berikut.
Widget _buildWidgetContentProfile() {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(256),
),
decoration: BoxDecoration(
color: Color(0xFF251F1F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
ScreenUtil().setWidth(72),
),
topRight: Radius.circular(
ScreenUtil().setWidth(72),
),
),
),
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setWidth(128 + 64),
right: ScreenUtil().setWidth(64),
left: ScreenUtil().setWidth(64),
),
child: Column(
children: <Widget>[
WidgetTextMont(
'Angelica Mayuko',
fontSize: 48,
fontWeight: FontWeight.bold,
),
SizedBox(
height: ScreenUtil().setHeight(16),
),
WidgetTextMont(
'Software Engineer focused on iOS',
textColor: Colors.grey,
fontWeight: FontWeight.w400,
),
SizedBox(
height: ScreenUtil().setHeight(72),
),
_buildWidgetPostsFollowersFollowing(),
SizedBox(
height: ScreenUtil().setHeight(32),
),
_buildWidgetButtonFollowAndChat(),
SizedBox(
height: ScreenUtil().setHeight(48),
),
_buildWidgetHeaderMyPosts(),
// TODO: buat widget untuk menampilkan post dalam bentuk listview dan gridview
],
),
),
);
}
Kemudian, kita buat fungsi baru berikut ya.
Widget _buildWidgetHeaderMyPosts() {
return Row(
children: <Widget>[
Expanded(
child: WidgetTextMont(
'My Posts',
fontWeight: FontWeight.bold,
fontSize: 42,
),
),
GestureDetector(
onTap: () {
setState(() {
isGridView = false;
});
},
child: Icon(
FontAwesomeIcons.thList,
color: isGridView ? Colors.grey[800] : Colors.grey[100],
size: 16,
),
),
SizedBox(
width: ScreenUtil().setWidth(48),
),
GestureDetector(
onTap: () {
setState(() {
isGridView = true;
});
},
child: Icon(
FontAwesomeIcons.thLarge,
color: isGridView ? Colors.grey[100] : Colors.grey[800],
size: 16,
),
),
],
);
}
Pembuatan Fitur
Fitur Menampilkan Gambar Firebase Storage
Setelah kita selesai membuat UI-nya selanjutnya kita akan membuat fitur untuk menampilkan gambar dari Firebase Storage. Silakan kita ubah kode komentar berikut.
// TODO: buat widget untuk menampilkan post dalam bentuk listview dan gridview
Menjadi seperti berikut.
Widget _buildWidgetContentProfile() {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(256),
),
decoration: BoxDecoration(
color: Color(0xFF251F1F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
ScreenUtil().setWidth(72),
),
topRight: Radius.circular(
ScreenUtil().setWidth(72),
),
),
),
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setWidth(128 + 64),
right: ScreenUtil().setWidth(64),
left: ScreenUtil().setWidth(64),
),
child: Column(
children: <Widget>[
WidgetTextMont(
'Angelica Mayuko',
fontSize: 48,
fontWeight: FontWeight.bold,
),
SizedBox(
height: ScreenUtil().setHeight(16),
),
WidgetTextMont(
'Software Engineer focused on iOS',
textColor: Colors.grey,
fontWeight: FontWeight.w400,
),
SizedBox(
height: ScreenUtil().setHeight(72),
),
_buildWidgetPostsFollowersFollowing(),
SizedBox(
height: ScreenUtil().setHeight(32),
),
_buildWidgetButtonFollowAndChat(),
SizedBox(
height: ScreenUtil().setHeight(48),
),
_buildWidgetHeaderMyPosts(),
Expanded(
child: _buildWidgetMyPosts(),
),
],
),
),
);
}
Selanjutnya, kita buat satu fungsi baru bernama _buildWidgetMyPosts()
.
Widget _buildWidgetMyPosts() {
if (isLoading && typeOperation == TypeOperation.download) {
return Center(
child: CircularProgressIndicator(),
);
} else if (myPosts.isEmpty) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
WidgetTextMont(
'No post available',
fontWeight: FontWeight.bold,
fontSize: 56,
textColor: Colors.grey[600],
),
SizedBox(
height: ScreenUtil().setHeight(24),
),
WidgetTextMont(
'When you share photos and videos, they\'ll appear in here',
textAlign: TextAlign.center,
textColor: Colors.grey[700],
),
],
);
} else {
double paddingBottomScreen = MediaQuery.of(context).padding.bottom;
return isGridView
? Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setHeight(48),
bottom: paddingBottomScreen == 0 ? ScreenUtil().setHeight(48) : paddingBottomScreen,
),
child: GridView.count(
padding: EdgeInsets.zero,
crossAxisCount: 3,
children: myPosts.map(
(item) {
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16)),
child: CachedNetworkImage(
imageUrl: item,
fit: BoxFit.cover,
placeholder: (context, url) {
return Image.asset(
'assets/images/img_placeholder.png',
fit: BoxFit.cover,
);
},
errorWidget: (context, url, error) {
return Image.asset(
'assets/images/img_not_found.jpg',
fit: BoxFit.cover,
);
},
),
);
},
).toList(),
crossAxisSpacing: ScreenUtil().setWidth(48),
mainAxisSpacing: ScreenUtil().setHeight(48),
),
)
: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setHeight(48),
bottom: paddingBottomScreen == 0 ? ScreenUtil().setHeight(48) : paddingBottomScreen,
),
child: ListView.separated(
padding: EdgeInsets.zero,
itemCount: myPosts.length,
separatorBuilder: (context, index) {
return SizedBox(
height: ScreenUtil().setHeight(48),
);
},
itemBuilder: (context, index) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: CachedNetworkImage(
imageUrl: myPosts[index],
fit: BoxFit.cover,
placeholder: (context, url) {
return Image.asset(
'assets/images/img_placeholder.png',
fit: BoxFit.cover,
);
},
errorWidget: (context, url, error) {
return Image.asset(
'assets/images/img_not_found.jpg',
fit: BoxFit.cover,
);
},
),
);
},
),
);
}
}
Jadi, tekniknya ialah pada kode diatas kita menampilkan gambarnya dari collection yang bernama myPosts
kedalam widget ListView
dan GridView.
Lalu, langkah selanjutnya adalah kita perlu ubah juga kode komentar berikut
// TODO: lakukan sesuatu di initState
Menjadi seperti berikut.
WidgetsBinding.instance.addPostFrameCallback((_) async {
sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.containsKey(keyMyPosts)) {
myPosts.addAll(sharedPreferences.getStringList(keyMyPosts));
setState(() {
isLoading = false;
typeOperation = null;
});
} else {
setState(() {
isLoading = false;
typeOperation = null;
});
}
});
Fungsi dari kode diatas adalah untuk mengambil list url gambar dari SharedPreferences dan dimasukkan kedalam variable myPosts
. Setelah itu, kita jalankan hot restart pada app-nya.
Buat Fitur Upload Gambar
Untuk fitur upload gambarnya kita bisa menambahkannya didalam icon kamera yang ada dibagian header app-nya. Silakan kita ubah kode komentar berikut
// TODO: lakukan sesuatu ketika icon camera di-tap
Menjadi seperti berikut.
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
if (image == null) {
return;
}
List<String> splitPath = image.path.split('/');
String filename = splitPath[splitPath.length - 1];
StorageReference ref = firebaseStorage.ref().child('images').child(filename);
StorageUploadTask uploadTask = ref.putFile(image);
StreamSubscription streamSubscription = uploadTask.events.listen((event) async {
var eventType = event.type;
if (eventType == StorageTaskEventType.progress) {
setState(() {
typeOperation = TypeOperation.upload;
isLoading = true;
});
} else if (eventType == StorageTaskEventType.failure) {
scaffoldState.currentState.showSnackBar(SnackBar(
content: Text('Photo failed to upload'),
));
setState(() {
isLoading = false;
isSuccess = false;
typeOperation = null;
});
} else if (eventType == StorageTaskEventType.success) {
var downloadUrl = await event.snapshot.ref.getDownloadURL();
myPosts.add(downloadUrl);
sharedPreferences.setStringList(keyMyPosts, myPosts);
scaffoldState.currentState.showSnackBar(SnackBar(
content: Text('Photo uploaded successfully'),
));
setState(() {
isLoading = false;
isSuccess = true;
typeOperation = null;
});
}
});
await uploadTask.onComplete;
streamSubscription.cancel();
Jadi, pada kode tersebut kita menggunakan plugin ImagePicker untuk mengambil gambar dari gallery dan kemudian kita upload gambar tersebut ke Firebase Storage dengan lokasi gambarnya berada didalam direktori image. Lalu, kita juga ada meng-listen setiap progress upload-nya sehingga kita bisa mengubah setState()
-nya agar bisa menampilkan loading ketika upload sedang berjalan dan menampilkan snackbar ketika upload gagal atau berhasil. Kemudian, sebelum kita jalankan kode diatas kita juga perlu membuat fungsi berikut ya untuk menampilkan widget loading-nya.
Widget _buildWidgetLoading() {
if (isLoading && typeOperation == TypeOperation.upload || typeOperation == TypeOperation.delete) {
return Container(
width: double.infinity,
height: double.infinity,
color: Colors.grey[900].withOpacity(0.8),
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return Container();
}
}
Dan kita panggil fungsi tersebut didalam fungsi build.
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return Scaffold(
key: scaffoldState,
body: Stack(
children: <Widget>[
_buildWidgetBackgroundHeader(),
_buildWidgetContentProfile(),
_buildWidgetPhotoProfile(),
_buildWidgetLoading(),
],
),
);
}
Sekarang coba kita test fitur upload gambarnya.
Buat Fitur Hapus Gambar
Sekarang kita akan membuat fitur hapus gambar dari Firebase Storage. Silakan buka kembali file main.dart dan ubah kode komentar berikut.
// TODO: lakukan sesuatu ketika icon delete di-tap
Menjadi seperti berikut.
setState(() {
isLoading = true;
typeOperation = TypeOperation.delete;
});
myPosts.forEach((element) async {
StorageReference ref = await firebaseStorage.getReferenceFromUrl(element);
await ref.delete();
});
myPosts.clear();
await sharedPreferences.clear();
setState(() {
isLoading = false;
typeOperation = null;
});
Kode diatas berfungsi untuk menghapus semua gambar dari Firebase Storage dengan cara kita for each setiap url gambar yang akan dihapus. Sekarang coba kita test fitur tersebut ya.
Kesimpulan
Jadi kesimpulannya adalah kita telah berhasil membuat aplikasi yang melakukan proses upload, menampilkan gambar dan hapus gambar dari Firebase Storage di Flutter. Dan diharapkan dengan adanya Firebase Storage ini kita yang terbatasnya resource infrastruktur backend untuk menyimpan file-file dari client side bisa menjadi lebih terbantu khususnya untuk mobile developer. Untuk kode lengkapnya dari contoh projek kali ini bisa dilihat di Github ya.