Halo teman-teman kita lanjutkan seri tutorialnya, sebelumnya kita kan sudah membuat tampilan daftar post dan get data post dari rest API, untuk tutorial kali ini kita akan belajar bagaimana caranya untuk insert data dan mengirim file berupa foto melalui Rest API.
Langkah 1 – Membuat Layout Insert
Pada langkah pertama kita membuat layout untuk insert, kita buat file dengan nama add_edit_post_screen.dart
pada folder screen
, kenapa saya kasih nama add edit, karena nanti kita gunakan juga layout tersebut untuk edit data post. Setelah kalian buat filenya kalian buat class dengan nama AddEdtiPostScreen
dengan extend StatelessWidget
, ketik kode berikut:
import 'package:flutter/material.dart';
class AddEditPostScreen extends StatelessWidget {
const AddEditPostScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
Selanjutnya kita buat layoutnya yang dimana layout tersebut ada beberapa widget, dimulai dari Scaffold.
Ketik kode berikut:
import 'package:flutter/material.dart';
class AddEditPostScreen extends StatelessWidget {
const AddEditPostScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Add New Post'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Center(
child: Text('No Image Selected'),
),
const SizedBox(
height: 8.0,
),
TextButton(
onPressed: () {},
child: const Text('Selected Image'),
),
const SizedBox(
height: 8.0,
),
TextFormField(
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {},
decoration: const InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Insert Title';
} else {
return null;
}
},
),
const SizedBox(
height: 16.0,
),
TextFormField(
textInputAction: TextInputAction.done,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: const InputDecoration(
labelText: 'Content',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Insert Some Text';
} else {
return null;
}
},
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () {},
child: const Text('Submit'),
),
),
],
),
),
);
}
}
Dari kode di atas ada perubahan yaitu
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Add New Post'),
),
body:,
.......
....
)
kode di atas yaitu widget Scaffold, widget ini merupakan strutur dasar atau kerangka aplikasi. Di dalam scaffold ini kita isi 2 paramerter:
appbar
: appbar ini yaitu tampilan toolbar yang ada di atas layout.body
: body ini digunakan untuk konten utama atau area utama.
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Center(
child: Text('No Image Selected'),
),
const SizedBox(
height: 8.0,
),
TextButton(
onPressed: () {},
child: const Text('Selected Image'),
),
const SizedBox(
height: 8.0,
),
TextFormField(
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {},
decoration: const InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Insert Title';
} else {
return null;
}
},
),
const SizedBox(
height: 16.0,
),
TextFormField(
textInputAction: TextInputAction.done,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: const InputDecoration(
labelText: 'Content',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Insert Some Text';
} else {
return null;
}
},
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () {},
child: const Text('Submit'),
),
),
],
),
),
Pada kode diatas kita membuat widget Padding()
dengan nilai padding semua sisi jaraknya 16, di dalam Widget padding ini kita buat tampilan untuk pilih gambar lalu, 2 widget TextFormField()
dan 1 tombol untuk action mengirim data ke rest API.
Sebelum kalian cek hasilnya kita beri action navigasi pada FloatingActionButton
yang ada pada layout Home
, tambahkan kode berikut pada tanda yang kita beri komentar //TODO action to add pada argument onPressed
.
Ketik kode berikut:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddEditPostScreen(),
),
);
sehingga menjadi berikut
.......
.........
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddEditPostScreen(),
),
);
},
child: const Icon(Icons.add),
),
..............
Setelah sudah kalian run lalu kalian tap pada floatingbutton, tampilan AddEditPostScreen
akan menjadi berikut:
Langkah 2 – Mengirim Data ke Rest API
Langkah selanjutnya kita akan mengirim data ke rest api, kita buka layout add_edit_post_screen.dart
, karena ada perubahan pada layout ini, kita rubah dulu yang awalnya StatelessWidget
kita rubah menjadi StatefulWidget
dengan megarahkan cursor pada tulisan statelessWidget
lalu gunakan shortcut untuk windows atau linux ctrl + ., sedangankan windows command + . .
setelah itu ketik kode berikut di dalam class _AddEditPostScreenState
final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController();
final _contentController = TextEditingController();
final _titleFocus = FocusNode();
final _contentFocus = FocusNode();
File? _image;
final picker = ImagePicker();
final Repository apiService = Repository();
Future getImage() async {
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No Image Selected')),
);
}
});
}
@override
void dispose() {
_titleController.dispose();
_contentController.dispose();
super.dispose();
}
Future<void> _submitData() async {
if (_formKey.currentState!.validate() && _image != null) {
bool success = await apiService.insertPost(
_image,
_titleController.text,
_contentController.text,
);
if (!mounted) return;
if (success) {
Navigator.pop(context, true);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Failed to Create Post')),
);
}
}
}
sehingga keseluruhan kode menjadi berikut:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_pemula/api/repository.dart';
import 'package:image_picker/image_picker.dart';
class AddEditPostScreen extends StatefulWidget {
const AddEditPostScreen({super.key});
@override
State<AddEditPostScreen> createState() => _AddEditPostScreenState();
}
class _AddEditPostScreenState extends State<AddEditPostScreen> {
final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController();
final _contentController = TextEditingController();
final _titleFocus = FocusNode();
final _contentFocus = FocusNode();
File? _image;
final picker = ImagePicker();
final Repository apiService = Repository();
Future getImage() async {
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No Image Selected')),
);
}
});
}
@override
void dispose() {
_titleController.dispose();
_contentController.dispose();
_titleFocus.dispose();
_contentFocus.dispose();
super.dispose();
}
Future<void> _submitData() async {
if (_formKey.currentState!.validate() && _image != null) {
bool success = await apiService.insertPost(
_image,
_titleController.text,
_contentController.text,
);
if (!mounted) return;
if (success) {
Navigator.pop(context, true);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Failed to Create Post')),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Add New Post'),
),
body: Form(
key: _formKey,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Center(
child: _image == null
? const Text('No Image Selected')
: Image.file(
_image!,
height: 200,
width: 200,
),
),
const SizedBox(
height: 8.0,
),
TextButton(
onPressed: getImage,
child: const Text('Selected Image'),
),
const SizedBox(
height: 8.0,
),
TextFormField(
controller: _titleController,
focusNode: _titleFocus,
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {},
decoration: const InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Insert Title';
} else {
return null;
}
},
),
const SizedBox(
height: 16.0,
),
TextFormField(
controller: _contentController,
focusNode: _contentFocus,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: const InputDecoration(
labelText: 'Content',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Insert Some Text';
} else {
return null;
}
},
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () => _submitData(),
child: const Text('Submit'),
),
),
],
),
),
),
),
);
}
}
Perubahan pada kode diatas:
final _formKey = GlobalKey<FormState>();
= Membuat kunci global untuk mengidentifikasi f.final _titleController = TextEditingController();
= Membuat pengontrol teks untuk title.final _contentController = TextEditingController();
= Membuat pengontrol teks untuk konten.final _titleFocus = FocusNode();
= Membuat node fokus untuk judul.final _contentFocus = FocusNode();
= Membuat node fokus untuk konten.File? _image;
= Deklarasi variabel untuk menyimpan file gambar yang diambil melalu library ImagePicker.final picker = ImagePicker();
= Membuat inisialiasi library imagePicker yang sudah di install.final Repository apiService = Repository();
= inisialiasai kedalam object untuk akses ke Rest API
Untuk fungsi getImage()
ini digunakan untuk mengambil gambar dari device kita, menggunakan async
lalu di dalam fungsi tersebut kita menggunakan library image_picker
. lalu kita panggil pada widget TextButton di argument OnPressed.
Untuk fungsi dispose()
ini digunakan untuk menghapus controller
dan focusnode
ketika widget di hancurkan.
Fungsi ini dipanggil agar tidak terjadi memmory leak atau kebocoran memori.
Untuk fungsi submitData()
digunakan untuk mengirim data, yang di dalam fungsi ini terdapat pengecekan terlebih dahulu _formKey.currentState!.validate() && _image != null
ketika form sudah ke isi semua dan image tidak bernilai null atau kosong maka akan mengirim data melalu API, yang dimana kita masukkan pada variable success
karena nilai kembalian dari hasil variabel apiService.insertPost
adalah true atau false. Ketika succes maka akan kembali ke halaman utama jika gagal akan menampilkan pesan Failed to Create Post.
Lalu fungsi submitData()
kita paggil pada widget FilledButton pada argument onPressed. Sekarang kalian jalankan lalu coba kita masukkan gambar, title, dan content, jika berhasil maka akan kembali ke halaman home, tapi coba lihat data tidak bertambah pada halaman home. Agar data dapat ter update maka rubah kode pada FloatingActionButton pada onPressed menjadi berikut:
..........
floatingActionButton: FloatingActionButton(
onPressed: () async {
var result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddEditPostScreen(),
),
);
if (result == true) {
_currentPage = 1;
_posts.clear();
_hasMore = true;
_loadPosts();
}
},
...............
Sekarang coba run dan masukkan data lagi, kalau berhasi data akan terupdate.
Kesimpulan
Ok teman pada artikel kita kali ini kita sudah belajar cara input data post dan cara memvalidasi data ketika akan mengirim ke server Rest Api, utuk selanjutnya kita akan belajar bagaimana cara edit dan update data.
Tutorial Flutter dengan Laravel Rest API #5: Edit dan Update di Flutter dengan Rest API
Membangun Website dan Aplikasi Android Desa Dengan Laravel, React.js dan React Native
Membangun Aplikasi dan Website News Dengan Laravel, React.js dan Android