Proyek ini adalah aplikasi e-commerce sederhana dengan desain premium dan fitur keranjang belanja, dibangun menggunakan Flutter. Cocok untuk pemula hingga menengah!
dependencies:
flutter:
sdk: flutter
provider: ^6.0.5
http: ^1.1.0
collection: ^1.18.0
cupertino_icons: ^1.0.2
Struktur folder utama dalam proyek:
lib/
βββ main.dart # Titik masuk aplikasi dan routing
βββ models/ # Menyimpan model data
β βββ product.dart # Model data produk
β βββ cart_item.dart # Model data item dalam keranjang
βββ page/ # Halaman-halaman antarmuka pengguna
β βββ login_page.dart # Halaman login pengguna
β βββ product_list_page.dart # Daftar produk dari API
β βββ product_detail_page.dart # Halaman detail produk
β βββ cart_page.dart # Halaman keranjang belanja
βββ providers/ # State management
β βββ auth_provider.dart # Manajemen autentikasi login
β βββ cart_provider.dart # Manajemen data keranjang belanja
Provider ini digunakan untuk autentikasi login sederhana.
import 'package:flutter/material.dart';
class AuthProvider extends ChangeNotifier {
bool _isLoggedIn = false;
bool get isLoggedIn => _isLoggedIn;
void login(String username, String password) {
if (username == 'user' && password == 'password') {
_isLoggedIn = true;
notifyListeners();
}
}
void logout() {
_isLoggedIn = false;
notifyListeners();
}
}
Provider ini digunakan untuk mengelola data keranjang belanja.
import 'package:flutter/material.dart';
import '../models/cart_item.dart';
class CartProvider extends ChangeNotifier {
final List<CartItem> _items = [];
List<CartItem> get items => List.unmodifiable(_items);
double get totalPrice =>
_items.fold(0.0, (sum, item) => sum + item.price * item.quantity);
void addToCart(CartItem item) {
final index = _items.indexWhere((e) => e.productId == item.productId);
if (index >= 0) {
_items[index].quantity++;
} else {
_items.add(item);
}
notifyListeners();
}
void removeItem(int productId) {
_items.removeWhere((item) => item.productId == productId);
notifyListeners();
}
void clearCart() {
_items.clear();
notifyListeners();
}
}
Model data untuk produk yang digunakan dalam daftar dan detail produk.
class Product {
final int id;
final String title;
final String description;
final String thumbnail;
final double price;
final String category;
final double rating;
final String? brand;
final double discountPercentage;
final int stock;
final List<String> images;
Product({
required this.id,
required this.title,
required this.description,
required this.thumbnail,
required this.price,
required this.category,
required this.rating,
this.brand,
required this.discountPercentage,
required this.stock,
required this.images,
});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
title: json['title'],
description: json['description'],
thumbnail: json['thumbnail'],
price: (json['price'] as num).toDouble(),
category: json['category'],
rating: (json['rating'] as num).toDouble(),
brand: json['brand'],
discountPercentage: (json['discountPercentage'] as num).toDouble(),
stock: json['stock'],
images: List<String>.from(json['images']),
);
}
}
Model data untuk item yang ditambahkan ke keranjang.
class CartItem {
final int productId;
final String title;
final double price;
final String thumbnail;
final String? brand;
final String? category;
int quantity;
CartItem({
required this.productId,
required this.title,
required this.price,
required this.thumbnail,
this.brand,
this.category,
this.quantity = 1,
});
}
File utama aplikasi Flutter. Berfungsi sebagai titik awal (entry point) dari seluruh proyek, mengatur routing, tema, dan penyedia state management secara global. dan konfigurasi routing serta penyedia state global.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/auth_provider.dart';
import 'providers/cart_provider.dart';
import 'page/login_page.dart';
import 'page/product_list_page.dart';
import 'page/product_detail_page.dart';
import 'page/cart_page.dart';
import 'models/product.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => CartProvider()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter E-Commerce App',
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.deepPurple,
),
initialRoute: '/',
routes: {
'/': (context) => const LoginPage(),
'/products': (context) => const ProductListPage(),
'/cart': (context) => const CartPage(),
},
onGenerateRoute: (settings) {
if (settings.name == '/product-detail') {
final product = settings.arguments as Product;
return MaterialPageRoute(
builder: (_) => ProductDetailPage(product: product),
);
}
return null;
},
);
}
}
Halaman login sederhana yang memungkinkan pengguna masuk ke aplikasi menggunakan username dan password.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _usernameController,
decoration: const InputDecoration(labelText: 'Username'),
),
const SizedBox(height: 10),
TextField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(labelText: 'Password'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final authProvider = Provider.of<AuthProvider>(context, listen: false);
authProvider.login(_usernameController.text, _passwordController.text);
if (authProvider.isLoggedIn) {
Navigator.pushReplacementNamed(context, '/products');
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Login gagal')),
);
}
},
child: const Text('Login'),
),
],
),
),
);
}
}
Menampilkan daftar produk dari API dan tombol tambah ke keranjang.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/cart_provider.dart';
import '../models/cart_item.dart';
import '../models/product.dart';
import '../services/api_service.dart';
import 'product_detail_page.dart';
class ProductListPage extends StatefulWidget {
const ProductListPage({super.key});
@override
State<ProductListPage> createState() => _ProductListPageState();
}
class _ProductListPageState extends State<ProductListPage> {
List<Product> products = [];
@override
void initState() {
super.initState();
fetchProducts();
}
Future<void> fetchProducts() async {
final data = await ApiService.fetchProducts();
setState(() => products = data);
}
@override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context);
return Scaffold(
appBar: AppBar(title: const Text('Produk')),
body: GridView.builder(
padding: const EdgeInsets.all(10),
itemCount: products.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.7,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (_, i) {
final p = products[i];
return GestureDetector(
onTap: () => Navigator.pushNamed(
context,
'/product-detail',
arguments: p,
),
child: Card(
child: Column(
children: [
Expanded(child: Image.network(p.thumbnail)),
Text(p.title),
Text('Rp ${p.price}'),
TextButton(
onPressed: () => cart.addToCart(CartItem(
productId: p.id,
title: p.title,
price: p.price,
thumbnail: p.thumbnail,
brand: p.brand,
category: p.category,
)),
child: const Text('Tambah ke Keranjang'),
),
],
),
),
);
},
),
);
}
}
Menampilkan detail produk secara lengkap dan tombol beli.
import 'package:flutter/material.dart';
import '../models/product.dart';
import '../models/cart_item.dart';
import 'package:provider/provider.dart';
import '../providers/cart_provider.dart';
class ProductDetailPage extends StatelessWidget {
final Product product;
const ProductDetailPage({super.key, required this.product});
@override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context);
return Scaffold(
appBar: AppBar(title: Text(product.title)),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Image.network(product.thumbnail, height: 200),
const SizedBox(height: 10),
Text(product.description),
const SizedBox(height: 10),
Text('Rp ${product.price}', style: const TextStyle(fontSize: 18)),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
cart.addToCart(CartItem(
productId: product.id,
title: product.title,
price: product.price,
thumbnail: product.thumbnail,
brand: product.brand,
category: product.category,
));
},
child: const Text('Beli Sekarang'),
)
],
),
),
);
}
}
Menampilkan semua item di keranjang dan total harga.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/cart_provider.dart';
class CartPage extends StatelessWidget {
const CartPage({super.key});
@override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context);
return Scaffold(
appBar: AppBar(title: const Text('Keranjang')),
body: cart.items.isEmpty
? const Center(child: Text('Keranjang kosong'))
: ListView.builder(
itemCount: cart.items.length,
itemBuilder: (_, i) {
final item = cart.items[i];
return ListTile(
leading: Image.network(item.thumbnail, width: 50),
title: Text(item.title),
subtitle: Text('Rp ${item.price} x ${item.quantity}'),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => cart.removeItem(item.productId),
),
);
},
),
bottomNavigationBar: Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Total: Rp ${cart.totalPrice.toStringAsFixed(0)}'),
ElevatedButton(
onPressed: cart.clearCart,
child: const Text('Checkout'),
)
],
),
),
);
}
}
Layanan untuk mengambil data produk dari API eksternal.
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/product.dart';
class ApiService {
static Future<List<Product>> fetchProducts() async {
final response = await http.get(Uri.parse("https://dummyjson.com/products"));
if (response.statusCode == 200) {
final data = json.decode(response.body);
return (data['products'] as List)
.map((json) => Product.fromJson(json))
.toList();
} else {
throw Exception('Gagal memuat produk');
}
}
}