Displaying Restaurant List
Displaying Restaurant List from Apps Development with Flutter Path MySkill.id
Yuk subscribe untuk mendapatkan email notifikasi setiap ada artikel terbaru oleh MySkill
Dalam industri kuliner digital, kemampuan menampilkan daftar restoran secara efisien dan menarik menjadi fondasi kesuksesan aplikasi. Pengguna modern mengharapkan akses cepat ke informasi restoran dengan visual yang memukau dan navigasi yang intuitif. Flutter hadir sebagai solusi ideal karena kemampuannya menghasilkan UI native yang konsisten di berbagai platform dengan performa tinggi.
1. Persiapan Lingkungan Pengembangan
Sebelum memulai implementasi, pastikan struktur proyek sudah terorganisir dengan baik. Struktur folder yang direkomendasikan:
lib/
├── main.dart
├── models/
│ └── restaurant_model.dart
├── screens/
│ ├── home_screen.dart
│ └── restaurant_detail_screen.dart
└── widgets/
└── restaurant_card.dart
assets/
└── images/
├── banner_0.jpg
├── banner_1.jpg
├── banner_2.jpg
├── sate.jpg
├── gudeg.jpg
├── rendang.jpg
└── pempek.jpgPenjelasan Struktur:
models/: Menyimpan struktur data restoranscreens/: Berisi halaman utama aplikasiwidgets/: Komponen UI yang dapat digunakan kembaliassets/: Menyimpan gambar dan resource statis
a. File: main.dart (Entry Point Aplikasi)
import 'package:flutter/material.dart';
import 'screens/home_screen.dart';
void main() {
runApp(const RestaurantApp());
}
class RestaurantApp extends StatelessWidget {
const RestaurantApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Restaurant Explorer',
theme: ThemeData(
primarySwatch: Colors.deepOrange,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const HomeScreen(),
debugShowCheckedModeBanner: false,
);
}
}Penjelasan:
MaterialApp: Widget root aplikasi Fluttertheme: Mengatur tema warna utama aplikasihome: Halaman pertama yang ditampilkan (HomeScreen)debugShowCheckedModeBanner: Menyembunyikan banner debug
2. Membangun Model Data Restoran
File restaurant_model.dart berfungsi sebagai cetakan untuk data restoran:
class Restaurant {
final String id; // Identifikasi unik setiap restoran
final String name; // Nama restoran yang ditampilkan
final String city; // Lokasi kota restoran
final String imageUrl; // Path gambar thumbnail
final double rating; // Rating restoran (0-5)
Restaurant({
required this.id,
required this.name,
required this.city,
required this.imageUrl,
required this.rating,
});
}Penjelasan Kode untuk Pemula:
class Restaurant: Seperti blueprint untuk membuat objek restoranfinal String id: Variabel yang tidak bisa diubah setelah dibuat, berfungsi sebagai KTP restoranrequired: Memastikan setiap restoran harus memiliki data ini saat dibuatdouble rating: Angka desimal untuk rating (contoh: 4.5)
3. Implementasi UI Dashboard
Dashboard dibangun dengan komponen-komponen yang saling terintegrasi dalam satu layar utama.
a. App Bar — Jendela Utama Aplikasi
AppBar(
elevation: 0, // Menghilangkan bayangan di bawah app bar
backgroundColor: Colors.white, // Latar belakang putih bersih
title: const Text(
'Jelajahi Restoran',
style: TextStyle(
color: Colors.deepOrange, // Warna teks oranye untuk branding
fontWeight: FontWeight.bold, // Tebal untuk penekanan
fontSize: 20, // Ukuran teks yang mudah dibaca
),
),
actions: [ // Tombol di sisi kanan app bar
IconButton(
icon: const Icon(Icons.search, color: Colors.deepOrange),
onPressed: () {
// Tempat logika pencarian restoran
},
),
IconButton(
icon: const Icon(Icons.filter_list, color: Colors.deepOrange),
onPressed: () {
// Tempat logika filter restoran
},
),
],
)Penjelasan Visual:
elevation: 0membuat app bar menyatu dengan layar (flat design)actionsseperti rak tempat menyimpan tombol aksi cepat- Ikon berwarna oranye menciptakan konsistensi visual dengan tema aplikasi
b. Promotional Banner — Media Promosi Interaktif
Container(
height: 180, // Tinggi banner 180 pixel
margin: const EdgeInsets.all(16), // Jarak 16 pixel dari semua sisi
child: PageView.builder( // Widget untuk slide gambar
itemCount: 3, // Jumlah slide banner
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.only(right: 8), // Jarak antar slide
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), // Sudut melengkung
image: DecorationImage(
image: AssetImage('assets/images/banner_$index.jpg'),
fit: BoxFit.cover, // Gambar menutupi seluruh area
),
),
);
},
),
)Penjelasan Cara Kerja:
PageView.builderseperti karusel foto yang bisa digeserBoxDecorationseperti bingkai foto dengan sudut melengkungBoxFit.covermemastikan gambar tidak terdistorsi dan memenuhi area
c. Restaurant List — Daftar Restoran Interaktif
Struktur Data Contoh:
final List<Restaurant> restaurantList = [
Restaurant(
id: '1',
name: 'Sate Khas Senayan',
city: 'Jakarta',
imageUrl: 'assets/images/sate.jpg',
rating: 4.5,
),
Restaurant(
id: '2',
name: 'Gudeg Yu Djum',
city: 'Yogyakarta',
imageUrl: 'assets/images/gudeg.jpg',
rating: 4.8,
),
];Implementasi ListView:
Expanded( // Memastikan list mengambil sisa ruang layar
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16), // Jarak dalam list
itemCount: restaurantList.length, // Jumlah item dalam list
itemBuilder: (context, index) {
final restaurant = restaurantList[index]; // Ambil data restoran ke-index
return Container(
margin: const EdgeInsets.only(bottom: 16), // Jarak antar kartu
child: Card(
elevation: 4, // Efek bayangan untuk kedalaman
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), // Sudut melengkung
),
child: InkWell( // Membuat area bisa diklik
borderRadius: BorderRadius.circular(12),
onTap: () {
// Aksi saat kartu diklik
},
child: Padding(
padding: const EdgeInsets.all(12), // Jarak dalam kartu
child: Row(
children: [
// Gambar Thumbnail
ClipRRect( // Widget untuk memotong sudut gambar
borderRadius: BorderRadius.circular(8),
child: Image.asset(
restaurant.imageUrl,
width: 100, // Lebar gambar
height: 100, // Tinggi gambar
fit: BoxFit.cover, // Gambar menutupi area
),
),
const SizedBox(width: 16), // Jarak horizontal
// Informasi Restoran
Expanded( // Mengisi sisa ruang horizontal
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // Rata kiri
children: [
Text(
restaurant.name,
style: const TextStyle(
fontWeight: FontWeight.bold, // Teks tebal
fontSize: 16, // Ukuran teks
),
),
const SizedBox(height: 4), // Jarak vertikal
Row( // Baris untuk lokasi
children: [
const Icon(
Icons.location_on, // Ikon lokasi
size: 16, // Ukuran ikon kecil
color: Colors.grey, // Warna abu-abu
),
const SizedBox(width: 4), // Jarak kecil
Text(
restaurant.city,
style: const TextStyle(color: Colors.grey),
),
],
),
const SizedBox(height: 8), // Jarak vertikal
Row( // Baris untuk rating
children: [
const Icon(
Icons.star, // Ikon bintang
color: Colors.amber, // Warna kuning emas
size: 16,
),
const SizedBox(width: 4),
Text(
restaurant.rating.toString(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
],
),
),
// Tombol Check
ElevatedButton(
onPressed: () {
// Aksi saat tombol ditekan
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepOrange, // Warna tombol
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // Sudut tombol
),
),
child: const Text('Check'), // Teks tombol
),
],
),
),
),
),
);
},
),
)Penjelasan Mendalam untuk Pemula:
- Struktur Kartu Restoran:
Cardseperti kartu nama fisik dengan efek bayanganInkWellmemberikan efek riak saat disentuh (feedback visual)Rowmenyusun elemen secara horizontal seperti meja dengan kursi
2. Komponen Visual:
Get My Skill’s stories in your inbox
Join Medium for free to get updates from this writer.
Gambar Thumbnail:
ClipRRectseperti gunting yang memotong sudut gambar- Ukuran 100x100 pixel seimbang untuk layar ponsel
Informasi Teks:
- Nama restoran dengan font tebal sebagai fokus utama
- Lokasi dengan ikon pin untuk pengenalan cepat
- Rating dengan bintang untuk visualisasi kualitas
Tombol Aksi:
- Warna oranye konsisten dengan tema aplikasi
- Ukuran proporsional tidak terlalu besar/kecil
3. Optimasi Performa:
ListView.builderhanya membuat item yang terlihat di layarExpandedmemastikan layout tidak pecah saat layar berubah
d. Bottom Navigation Bar — Peta Navigasi Aplikasi
BottomNavigationBar(
type: BottomNavigationBarType.fixed, // Mencegah pergeseran label
selectedItemColor: Colors.deepOrange, // Warna item aktif
unselectedItemColor: Colors.grey, // Warna item non-aktif
currentIndex: 0, // Halaman aktif (Beranda)
onTap: (index) {
// Logika perpindahan halaman berdasarkan index
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home), // Ikon rumah
label: 'Beranda', // Label teks
),
BottomNavigationBarItem(
icon: Icon(Icons.explore), // Ikon kompas
label: 'Jelajahi',
),
BottomNavigationBarItem(
icon: Icon(Icons.bookmark), // Ikon bookmark
label: 'Favorit',
),
BottomNavigationBarItem(
icon: Icon(Icons.person), // Ikon orang
label: 'Profil',
),
],
)Penjelasan Fungsional:
type: fixedmembuat semua menu selalu terlihatcurrentIndexmenentukan menu yang sedang dibukaonTapseperti pintu yang membuka halaman berbeda- Setiap item memiliki ikon dan label untuk kejelasan
4. Integrasi Navigasi ke Halaman Detail
Saat tombol “Check” ditekan, pengguna akan diarahkan ke halaman detail restoran:
// Di dalam onPressed tombol Check
onPressed: () {
Navigator.push( // Perintah untuk berpindah halaman
context,
MaterialPageRoute(
builder: (context) => RestaurantDetailPage(
restaurant: restaurant, // Kirim data restoran ke halaman detail
),
),
);
},Halaman Detail Restoran:
class RestaurantDetailPage extends StatelessWidget {
final Restaurant restaurant; // Terima data restoran
const RestaurantDetailPage({Key? key, required this.restaurant}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(restaurant.name), // Judul dengan nama restoran
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView( // Agar konten bisa di-scroll
child: Column(
children: [
Image.asset(restaurant.imageUrl), // Gambar utama restoran
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
restaurant.name,
style: const TextStyle(
fontSize: 24, // Ukuran besar untuk judul
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.location_on),
const SizedBox(width: 8),
Text(restaurant.city),
],
),
// Tambahkan informasi detail lainnya seperti:
// - Deskripsi restoran
// - Jam operasional
// - Menu makanan
// - Ulasan pengguna
],
),
),
],
),
),
);
}
}Penjelasan Alur Navigasi:
- Pengguna menekan tombol “Check” pada kartu restoran
Navigator.pushmembuka halaman baru seperti menambahkan lembar baru di atas- Data restoran dikirim melalui konstruktor halaman detail
- Halaman detail menampilkan informasi lengkap dengan gambar besar
5. Tips Praktis untuk Pengembang Pemula
- Manajemen Gambar:
- Simpan gambar di folder
assets/images - Daftarkan di
pubspec.yamldengan format:
flutter:
assets:
- assets/images/- Gunakan format WebP untuk ukuran file lebih kecil
2. Optimasi Performa:
- Gunakan
constuntuk widget yang tidak berubah - Batasi jumlah item dalam
ListView(ideal < 100 untuk data statis) - Implementasikan
ListView.separateduntuk pembatas antar item
3. Debugging Visual:
- Gunakan
debugPaintSizeEnabled = truedimain.dartuntuk melihat batas widget - Aktifkan Flutter Inspector di Android Studio/Dart DevTools
4. Responsivitas:
- Gunakan
MediaQueryuntuk menyesuaikan ukuran:
final screenWidth = MediaQuery.of(context).size.width;- Atur padding berdasarkan ukuran layar
Cara Menjalankan Aplikasi:
- Buat proyek Flutter baru
- Buat struktur folder seperti di atas
- Salin semua kode ke file yang sesuai
- Tambahkan gambar ke folder
assets/images - Konfigurasi
pubspec.yaml - Jalankan dengan
flutter run
Kesimpulan
Implementasi daftar restoran dalam Flutter membutuhkan perhatian pada tiga aspek utama: struktur data yang jelas, komponen UI yang intuitif, dan alur navigasi yang mulus. Dengan mengikuti panduan ini, kita telah membangun fondasi kuat untuk aplikasi kuliner yang tidak hanya fungsional tetapi juga menarik secara visual. Setiap komponen dirancang dengan pertimbangan khusus untuk pengalaman pengguna optimal — dari app bar yang informatif hingga kartu restoran yang interaktif.
Langkah selanjutnya yang dapat dikembangkan:
- Integrasi dengan API untuk data real-time
- Implementasi fitur pencarian dan filter
- Penambahan animasi transisi halaman
- Optimasi untuk berbagai ukuran layar
- Implementasi state management (Provider/Riverpod)
Dengan Flutter, kemungkinan pengembangan fitur kuliner menjadi tak terbatas — tergantung pada kreativitas dan kebutuhan pengguna akhir. Pastikan selalu menguji aplikasi di berbagai perangkat untuk memastikan konsistensi pengalaman pengguna.
Learn More Via: https://myskill.id/course/state-and-props-management









