import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
// ================= APP UTAMA =================
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isDarkMode = false;
void toggleTheme(bool value) {
setState(() {
_isDarkMode = value;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Kuliner Nusantara',
debugShowCheckedModeBanner: false,
theme: _isDarkMode
? ThemeData.dark().copyWith(
appBarTheme: const AppBarTheme(backgroundColor: Colors.green),
)
: ThemeData.light().copyWith(
appBarTheme: const AppBarTheme(backgroundColor: Colors.green),
),
home: MainPage(
isDarkMode: _isDarkMode,
onThemeChanged: toggleTheme,
),
);
}
}
// ================= MAIN PAGE DENGAN NAVBAR =================
class MainPage extends StatefulWidget {
final bool isDarkMode;
final Function(bool) onThemeChanged;
const MainPage({
super.key,
required this.isDarkMode,
required this.onThemeChanged,
});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// Daftar favorit
List<Map<String, String>> favorit = [];
// Toggle favorit
void toggleFavorit(Map<String, String> item) {
setState(() {
if (favorit.contains(item)) {
favorit.remove(item);
} else {
favorit.add(item);
}
});
}
@override
Widget build(BuildContext context) {
final pages = [
const HomePage(),
KulinerPage(onFavoritChanged: toggleFavorit, favorit: favorit),
SearchPage(onFavoritChanged: toggleFavorit, favorit: favorit),
FavoritPage(favorit: favorit),
ProfilPage(isDarkMode: widget.isDarkMode, onThemeChanged: widget.onThemeChanged),
];
return Scaffold(
body: pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
onTap: (index) => setState(() => _currentIndex = index),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Beranda"),
BottomNavigationBarItem(icon: Icon(Icons.restaurant), label: "Kuliner"),
BottomNavigationBarItem(icon: Icon(Icons.search), label: "Cari"),
BottomNavigationBarItem(icon: Icon(Icons.favorite), label: "Favorit"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Profil"),
],
),
);
}
}
// ================= DATA KULINER =================
const List<Map<String, String>> kulinerList = [
{"nama": "Rendang", "gambar": "assets/images/rendang.jpg"},
{"nama": "Sate", "gambar": "assets/images/sate.jpg"},
{"nama": "Pempek", "gambar": "assets/images/pempek.jpg"},
{"nama": "Bakso", "gambar": "assets/images/bakso.jpg"},
{"nama": "Rawon", "gambar": "assets/images/rawon.jpg"},
{"nama": "Gudeg Jogja", "gambar": "assets/images/gudeg.jpg"},
];
// ================= HALAMAN BERANDA =================
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Beranda")),
body: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Rekomendasi Kuliner",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Expanded(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: kulinerList.length,
itemBuilder: (context, index) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Column(
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(
kulinerList[index]["gambar"]!,
fit: BoxFit.cover,
width: double.infinity,
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(
kulinerList[index]["nama"]!,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
)
],
),
);
},
),
)
],
),
),
);
}
}
// ================= HALAMAN KULINER =================
class KulinerPage extends StatelessWidget {
final Function(Map<String, String>) onFavoritChanged;
final List<Map<String, String>> favorit;
const KulinerPage({super.key, required this.onFavoritChanged, required this.favorit});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Kuliner Nusantara")),
body: Padding(
padding: const EdgeInsets.all(12),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: kulinerList.length,
itemBuilder: (context, index) {
final item = kulinerList[index];
final isFavorit = favorit.contains(item);
return GestureDetector(
onTap: () => onFavoritChanged(item),
child: Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Column(
children: [
Expanded(
child: Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(item["gambar"]!, fit: BoxFit.cover, width: double.infinity),
),
Positioned(
top: 8,
right: 8,
child: Icon(isFavorit ? Icons.favorite : Icons.favorite_border, color: Colors.red),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(item["nama"]!, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
)
],
),
),
);
},
),
),
);
}
}
// ================= HALAMAN PENCARIAN =================
class SearchPage extends StatefulWidget {
final Function(Map<String, String>)? onFavoritChanged;
final List<Map<String, String>>? favorit;
const SearchPage({super.key, this.onFavoritChanged, this.favorit});
@override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
String query = "";
List<Map<String, String>> results = [];
void search(String value) {
setState(() {
query = value;
results = kulinerList
.where((item) => item["nama"]!.toLowerCase().contains(query.toLowerCase()))
.toList();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Cari Kuliner")),
body: Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
TextField(
decoration: InputDecoration(
hintText: "Cari kuliner...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
),
onSubmitted: (value) => search(value), // Tekan Enter
),
const SizedBox(height: 12),
Expanded(
child: results.isEmpty
? const Center(child: Text("Hasil pencarian kosong"))
: ListView.builder(
itemCount: results.length,
itemBuilder: (context, index) {
final item = results[index];
final isFavorit = widget.favorit?.contains(item) ?? false;
return Card(
margin: const EdgeInsets.symmetric(vertical: 6),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.asset(item["gambar"]!, width: 50, height: 50, fit: BoxFit.cover),
),
title: Text(item["nama"]!, style: const TextStyle(fontWeight: FontWeight.bold)),
trailing: IconButton(
icon: Icon(isFavorit ? Icons.favorite : Icons.favorite_border, color: Colors.red),
onPressed: () {
if (widget.onFavoritChanged != null) widget.onFavoritChanged!(item);
setState(() {});
},
),
),
);
},
),
),
],
),
),
);
}
}
// ================= HALAMAN FAVORIT =================
class FavoritPage extends StatelessWidget {
final List<Map<String, String>> favorit;
const FavoritPage({super.key, required this.favorit});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Favorit")),
body: favorit.isEmpty
? const Center(child: Text("Belum ada favorit"))
: Padding(
padding: const EdgeInsets.all(12),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: favorit.length,
itemBuilder: (context, index) {
final item = favorit[index];
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Column(
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(item["gambar"]!, fit: BoxFit.cover, width: double.infinity),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(item["nama"]!, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
)
],
),
);
},
),
),
);
}
}
// ================= HALAMAN PROFIL =================
class ProfilPage extends StatelessWidget {
final bool isDarkMode;
final Function(bool) onThemeChanged;
const ProfilPage({super.key, required this.isDarkMode, required this.onThemeChanged});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Profil")),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 20),
const CircleAvatar(radius: 40, backgroundColor: Colors.teal, child: Icon(Icons.person, size: 50, color: Colors.white)),
const SizedBox(height: 10),
const Text("Rian", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
const Text("rian@example.com", style: TextStyle(color: Colors.grey)),
const Divider(height: 30, thickness: 1),
ListTile(
leading: const Icon(Icons.dark_mode),
title: const Text("Mode Gelap"),
trailing: Switch(value: isDarkMode, onChanged: onThemeChanged),
),
const ListTile(
leading: Icon(Icons.settings),
title: Text("Pengaturan Akun"),
),
const ListTile(
leading: Icon(Icons.logout),
title: Text("Keluar"),
),
],
),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
// ================= APP UTAMA =================
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isDarkMode = false;
void toggleTheme(bool value) {
setState(() {
_isDarkMode = value;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Kuliner Nusantara',
debugShowCheckedModeBanner: false,
theme: _isDarkMode
? ThemeData.dark().copyWith(
appBarTheme: const AppBarTheme(backgroundColor: Colors.green),
)
: ThemeData.light().copyWith(
appBarTheme: const AppBarTheme(backgroundColor: Colors.green),
),
home: MainPage(
isDarkMode: _isDarkMode,
onThemeChanged: toggleTheme,
),
);
}
}
// ================= MAIN PAGE DENGAN NAVBAR =================
class MainPage extends StatefulWidget {
final bool isDarkMode;
final Function(bool) onThemeChanged;
const MainPage({
super.key,
required this.isDarkMode,
required this.onThemeChanged,
});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// Daftar favorit
List<Map<String, String>> favorit = [];
// Toggle favorit
void toggleFavorit(Map<String, String> item) {
setState(() {
if (favorit.contains(item)) {
favorit.remove(item);
} else {
favorit.add(item);
}
});
}
@override
Widget build(BuildContext context) {
final pages = [
const HomePage(),
KulinerPage(onFavoritChanged: toggleFavorit, favorit: favorit),
SearchPage(onFavoritChanged: toggleFavorit, favorit: favorit),
FavoritPage(favorit: favorit),
ProfilPage(isDarkMode: widget.isDarkMode, onThemeChanged: widget.onThemeChanged),
];
return Scaffold(
body: pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
onTap: (index) => setState(() => _currentIndex = index),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Beranda"),
BottomNavigationBarItem(icon: Icon(Icons.restaurant), label: "Kuliner"),
BottomNavigationBarItem(icon: Icon(Icons.search), label: "Cari"),
BottomNavigationBarItem(icon: Icon(Icons.favorite), label: "Favorit"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Profil"),
],
),
);
}
}
// ================= DATA KULINER =================
const List<Map<String, String>> kulinerList = [
{"nama": "Rendang", "gambar": "assets/images/rendang.jpg"},
{"nama": "Sate", "gambar": "assets/images/sate.jpg"},
{"nama": "Pempek", "gambar": "assets/images/pempek.jpg"},
{"nama": "Bakso", "gambar": "assets/images/bakso.jpg"},
{"nama": "Rawon", "gambar": "assets/images/rawon.jpg"},
{"nama": "Gudeg Jogja", "gambar": "assets/images/gudeg.jpg"},
];
// ================= HALAMAN BERANDA =================
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Beranda")),
body: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Rekomendasi Kuliner",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Expanded(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: kulinerList.length,
itemBuilder: (context, index) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Column(
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(
kulinerList[index]["gambar"]!,
fit: BoxFit.cover,
width: double.infinity,
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(
kulinerList[index]["nama"]!,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
)
],
),
);
},
),
)
],
),
),
);
}
}
// ================= HALAMAN KULINER =================
class KulinerPage extends StatelessWidget {
final Function(Map<String, String>) onFavoritChanged;
final List<Map<String, String>> favorit;
const KulinerPage({super.key, required this.onFavoritChanged, required this.favorit});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Kuliner Nusantara")),
body: Padding(
padding: const EdgeInsets.all(12),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: kulinerList.length,
itemBuilder: (context, index) {
final item = kulinerList[index];
final isFavorit = favorit.contains(item);
return GestureDetector(
onTap: () => onFavoritChanged(item),
child: Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Column(
children: [
Expanded(
child: Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(item["gambar"]!, fit: BoxFit.cover, width: double.infinity),
),
Positioned(
top: 8,
right: 8,
child: Icon(isFavorit ? Icons.favorite : Icons.favorite_border, color: Colors.red),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(item["nama"]!, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
)
],
),
),
);
},
),
),
);
}
}
// ================= HALAMAN PENCARIAN =================
class SearchPage extends StatefulWidget {
final Function(Map<String, String>)? onFavoritChanged;
final List<Map<String, String>>? favorit;
const SearchPage({super.key, this.onFavoritChanged, this.favorit});
@override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
String query = "";
List<Map<String, String>> results = [];
void search(String value) {
setState(() {
query = value;
results = kulinerList
.where((item) => item["nama"]!.toLowerCase().contains(query.toLowerCase()))
.toList();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Cari Kuliner")),
body: Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
TextField(
decoration: InputDecoration(
hintText: "Cari kuliner...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
),
onSubmitted: (value) => search(value), // Tekan Enter
),
const SizedBox(height: 12),
Expanded(
child: results.isEmpty
? const Center(child: Text("Hasil pencarian kosong"))
: ListView.builder(
itemCount: results.length,
itemBuilder: (context, index) {
final item = results[index];
final isFavorit = widget.favorit?.contains(item) ?? false;
return Card(
margin: const EdgeInsets.symmetric(vertical: 6),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.asset(item["gambar"]!, width: 50, height: 50, fit: BoxFit.cover),
),
title: Text(item["nama"]!, style: const TextStyle(fontWeight: FontWeight.bold)),
trailing: IconButton(
icon: Icon(isFavorit ? Icons.favorite : Icons.favorite_border, color: Colors.red),
onPressed: () {
if (widget.onFavoritChanged != null) widget.onFavoritChanged!(item);
setState(() {});
},
),
),
);
},
),
),
],
),
),
);
}
}
// ================= HALAMAN FAVORIT =================
class FavoritPage extends StatelessWidget {
final List<Map<String, String>> favorit;
const FavoritPage({super.key, required this.favorit});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Favorit")),
body: favorit.isEmpty
? const Center(child: Text("Belum ada favorit"))
: Padding(
padding: const EdgeInsets.all(12),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: favorit.length,
itemBuilder: (context, index) {
final item = favorit[index];
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Column(
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(item["gambar"]!, fit: BoxFit.cover, width: double.infinity),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(item["nama"]!, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
)
],
),
);
},
),
),
);
}
}
// ================= HALAMAN PROFIL =================
class ProfilPage extends StatelessWidget {
final bool isDarkMode;
final Function(bool) onThemeChanged;
const ProfilPage({super.key, required this.isDarkMode, required this.onThemeChanged});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Profil")),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 20),
const CircleAvatar(radius: 40, backgroundColor: Colors.teal, child: Icon(Icons.person, size: 50, color: Colors.white)),
const SizedBox(height: 10),
const Text("Rian", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
const Text("rian@example.com", style: TextStyle(color: Colors.grey)),
const Divider(height: 30, thickness: 1),
ListTile(
leading: const Icon(Icons.dark_mode),
title: const Text("Mode Gelap"),
trailing: Switch(value: isDarkMode, onChanged: onThemeChanged),
),
const ListTile(
leading: Icon(Icons.settings),
title: Text("Pengaturan Akun"),
),
const ListTile(
leading: Icon(Icons.logout),
title: Text("Keluar"),
),
],
),
);
}
}
Kalo semisal kalian ingin mencobanya ini dia kalian tinggal salin
Gabar :
Komentar
Posting Komentar