membuat aplikasi sederhana dengan konsep CRUD - ngawiflix
= CRUD adalah singkatan dari create,read,update,delete...
project ini dibuat sepenuhnya oleh dhanis fathan gunawan yang ditemani oleh saya (caca ardiansyah) dan dibantu oleh ChatGPT.
Membuat Aplikasi Jadwal Bioskop dengan Flutter
berfungsi untuk menampilkan, menambah, mengubah, dan menghapus daftar film beserta jadwal tayangnya.
(hasil akhir)
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: JadwalBioskop(),
));
}
class JadwalBioskop extends StatefulWidget {
@override
State<JadwalBioskop> createState() => _JadwalBioskopState();
}
class _JadwalBioskopState extends State<JadwalBioskop> {
List<Map<String, String>> filmList = [
{
"judul": "Ambaruwo",
"genre": "Horror, Sci-Fi",
"jam": "14:14, 17:17, 00:00",
"poster": "https://p16-sign-va.tiktokcdn.com/tos-maliva-i-photomode-us/2c1bf404b3494dbe88d2cfb529a07d8f~tplv-photomode-image-v1:q70.webp?dr=1334&refresh_token=96c9dbee&x-expires=1756436400&x-signature=hy0B0aIA8wrqS8GHYZ%2Fc5d2My98%3D&t=5897f7ec&ps=b40d0ec8&shp=d05b14bd&shcp=1d1a97fc&idc=my&s=AWEME_DETAIL&biz_tag=tt_photomode&sc=image",
"deskripsi":
"Si Hytam kembali bangkit untuk menghytamkan kalian! Saksikan aksi seru ini di"
},
{
"judul": "Ambarawuhi",
"genre": "Horror, Adventure",
"jam": "10:00, 13:00, 16:00",
"poster":
"https://p16-sign-va.tiktokcdn.com/tos-maliva-i-photomode-us/0159708740824b7395ae7b79c0ccf029~tplv-photomode-image-v1:q70.webp?dr=1334&refresh_token=58793e05&x-expires=1756436400&x-signature=7N62%2BwV6uxoottmwKwsBMBU2cF8%3D&t=5897f7ec&ps=b40d0ec8&shp=d05b14bd&shcp=1d1a97fc&idc=sg1&s=AWEME_DETAIL&biz_tag=tt_photomode&sc=image",
"deskripsi":
"Sekelompok KKN dari kota Ngawi yang diterror karena melakukan hal terlarang..."
},
{
"judul": "AMBATUKAM: one for all",
"genre": "Adventure",
"jam": "10:00, 13:00, 16:00",
"poster":
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSoTKE46hTPb_M8oWrv8yX7YRRAWYPOCRDEOQ&s",
"deskripsi": "Petualangan seru melawan desa misterius..."
},
{
"judul": "imut adalah maut",
"genre": "Romance, Drama",
"jam": "10:00, 13:00, 16:00",
"poster":
"https://p16-sign-va.tiktokcdn.com/tos-maliva-i-photomode-us/fff9a32b4474448482aea36881835dce~tplv-photomode-image-v1:q70.webp?dr=1334&refresh_token=6ec2b866&x-expires=1756436400&x-signature=u13SV9baoxGbHOa2UbHGp3w94kQ%3D&t=5897f7ec&ps=b40d0ec8&shp=d05b14bd&shcp=1d1a97fc&idc=my&s=AWEME_DETAIL&biz_tag=tt_photomode&sc=image",
"deskripsi": "drama rumah tangga yang diganggu oleh siimut"
},
{
"judul": "Anggrek mekar berduri",
"genre": "Drama",
"jam": "-",
"poster":
"https://p16-sign-va.tiktokcdn.com/tos-maliva-i-photomode-us/bee02b17c3aa485fb23c8d42359dca9d~tplv-photomode-image-v1:q70.webp?dr=1334&refresh_token=d5dc6895&x-expires=1756436400&x-signature=G3NJ9I%2BrORo%2FaEFoEY%2BLdQ56CJA%3D&t=5897f7ec&ps=b40d0ec8&shp=d05b14bd&shcp=1d1a97fc&idc=my&s=AWEME_DETAIL&biz_tag=tt_photomode&sc=image",
"deskripsi": "-"
},
{
"judul": "Ambabelle",
"genre": "Horror, shounen",
"jam": "-",
"poster":
"https://p16-sign-va.tiktokcdn.com/tos-maliva-i-photomode-us/033b71378e464c6ab3d974eb1b20cd86~tplv-photomode-image-v1:q70.webp?dr=1334&refresh_token=3164494f&x-expires=1756436400&x-signature=3UVFVwpJDuyDBbnSOdhSmWZIUAA%3D&t=5897f7ec&ps=b40d0ec8&shp=d05b14bd&shcp=1d1a97fc&idc=my&s=AWEME_DETAIL&biz_tag=tt_photomode&sc=image",
"deskripsi": "Film horror luar yang dibuat ulang di ngawi city"
},
];
String searchQuery = "";
void tambahFilm() {
showFormDialog();
}
void ubahFilm(int index) {
var film = filmList[index];
showFormDialog(editIndex: index, film: film);
}
void showFormDialog({int? editIndex, Map<String, String>? film}) {
String judul = film?["judul"] ?? "";
String genre = film?["genre"] ?? "";
String jam = film?["jam"] ?? "";
String poster = film?["poster"] ?? "";
String deskripsi = film?["deskripsi"] ?? "";
showDialog(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Colors.grey[900],
title: Text(
editIndex == null ? "Tambah Film" : "Ubah Film",
style: const TextStyle(color: Colors.white),
),
content: SingleChildScrollView(
child: Column(
children: [
buildInput("Judul", (val) => judul = val, judul),
buildInput("Genre", (val) => genre = val, genre),
buildInput("Jam Tayang", (val) => jam = val, jam),
buildInput("URL Poster", (val) => poster = val, poster),
buildInput("Deskripsi", (val) => deskripsi = val, deskripsi,
maxLines: 3),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Batal", style: TextStyle(color: Colors.red)),
),
ElevatedButton(
onPressed: () {
setState(() {
if (editIndex == null) {
filmList.add({
"judul": judul,
"genre": genre,
"jam": jam,
"poster": poster,
"deskripsi": deskripsi,
});
} else {
filmList[editIndex] = {
"judul": judul,
"genre": genre,
"jam": jam,
"poster": poster,
"deskripsi": deskripsi,
};
}
});
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[900],
),
child: const Text("Simpan",
style: TextStyle(color: Colors.white)),
),
],
);
},
);
}
static Widget buildInput(
String label, Function(String) onChanged, String initialValue,
{int maxLines = 1}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: TextField(
controller: TextEditingController(text: initialValue),
style: const TextStyle(color: Colors.white),
onChanged: onChanged,
maxLines: maxLines,
decoration: InputDecoration(
labelText: label,
labelStyle: const TextStyle(color: Colors.white),
filled: true,
fillColor: Colors.grey[800],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
),
),
);
}
void hapusFilm(int index) {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.grey[900],
title: const Text("Konfirmasi",
style: TextStyle(color: Colors.white)),
content: const Text("Yakin ingin menghapus film ini?",
style: TextStyle(color: Colors.white70)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child:
const Text("Batal", style: TextStyle(color: Colors.grey)),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[900],
),
onPressed: () {
setState(() {
filmList.removeAt(index);
});
Navigator.pop(context);
},
child:
const Text("Hapus", style: TextStyle(color: Colors.white)),
),
],
),
);
}
void detailFilm(Map<String, String> film) {
showDialog(
context: context,
builder: (context) {
final size = MediaQuery.of(context).size;
return Dialog(
backgroundColor: Colors.grey[900],
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 520,
maxHeight: size.height * 0.85,
),
child: SingleChildScrollView(
child: Column(
children: [
ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(12)),
child: AspectRatio(
aspectRatio: 2 / 3,
child: Image.network(film["poster"]!,
fit: BoxFit.cover),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(film["judul"]!,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white)),
const SizedBox(height: 4),
Text(film["genre"]!,
style: const TextStyle(color: Colors.grey)),
const SizedBox(height: 8),
Text("Jam: ${film["jam"]}",
style:
const TextStyle(color: Colors.white70)),
const SizedBox(height: 12),
Text(film["deskripsi"]!,
style:
const TextStyle(color: Colors.white)),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[900]),
onPressed: () => Navigator.pop(context),
child: const Text("Keluar",
style: TextStyle(color: Colors.white)),
),
),
],
),
),
],
),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
List<Map<String, String>> filteredList = filmList
.where((film) => film["judul"]!
.toLowerCase()
.contains(searchQuery.toLowerCase()))
.toList();
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text("Ngawiflix - Jadwal Bioskop",
style: TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Colors.red[900],
actions: [
IconButton(
onPressed: tambahFilm,
icon: const Icon(Icons.add, color: Colors.white),
),
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) => setState(() => searchQuery = value),
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
hintText: "Cari judul film...",
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon: const Icon(Icons.search, color: Colors.white),
filled: true,
fillColor: Colors.grey[900],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
),
),
),
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: width < 420 ? 360 : 240,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 0.55,
),
itemCount: filteredList.length,
itemBuilder: (context, index) {
var film = filteredList[index];
return Card(
color: Colors.grey[900],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: InkWell(
onTap: () => detailFilm(film),
child: ClipRRect(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12)),
child: Image.network(
film["poster"]!,
fit: BoxFit.cover,
width: double.infinity,
errorBuilder: (_, __, ___) => const Center(
child: Icon(Icons.broken_image)),
),
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(film["judul"]!,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white),
maxLines: 2,
overflow: TextOverflow.ellipsis),
const SizedBox(height: 4),
Text(film["genre"]!,
style: const TextStyle(
color: Colors.grey, fontSize: 12),
maxLines: 1,
overflow: TextOverflow.ellipsis),
const SizedBox(height: 4),
Text("Jam: ${film["jam"]}",
style: const TextStyle(
color: Colors.white70, fontSize: 12),
maxLines: 1,
overflow: TextOverflow.ellipsis),
const SizedBox(height: 8),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.edit,
color: Colors.yellow),
onPressed: () => ubahFilm(index),
),
IconButton(
icon: const Icon(Icons.delete,
color: Colors.red),
onPressed: () => hapusFilm(index),
),
],
),
],
),
),
],
),
);
},
),
),
],
),
);
}
}
Fitur Aplikasi
Menampilkan daftar film dalam bentuk.
Mencari film berdasarkan judul.
Menambah data film baru.
Mengubah data film yang ada.
Menghapus data film.
Menampilkan detail film.
^ ^ ^ ^ ^
___________________________________________________________________________________
dhanis fathan gunawan (main)
caca ardiansyah
ChatGPT & Google


Komentar
Posting Komentar