Plant Checkout View

Plant Checkout View

by dockui

Reusable SwiftUI checkout cart component with product list, quantity controls, pricing breakdown, and modern iOS UI styling

Platform
SwiftUI
Code
Native Swift
Format
Screen
Access
Premium

Code Snippet

import SwiftUI

struct PlantCheckoutView: View {
    @Environment(\.dismiss) private var dismiss
    @State private var cartItems: [CartItem] = [
        CartItem(
            id: 1,
            name: "Snake Plant",
            size: "Small",
            price: 35.00,
            quantity: 2,
            imageName: "leaf.fill"
        ),
        CartItem(
            id: 2,
            name: "Pothos Golden",
            size: "Medium",
            price: 28.50,
            quantity: 1,
            imageName: "leaf.fill"
        ),
        CartItem(
            id: 4,
            name: "Spider Plant",
            size: "Small",
            price: 22.99,
            quantity: 1,
            imageName: "leaf.fill"
        )
    ]
    
    var totalItems: Int {
        cartItems.reduce(0) { $0 + $1.quantity }
    }
    
    var subtotal: Double {
        cartItems.reduce(0) { $0 + ($1.price * Double($1.quantity)) }
    }
    
    var deliveryFee: Double {
        0.0 // Free delivery
    }
    
    var total: Double {
        subtotal + deliveryFee
    }
    
    var body: some View {
        ZStack {
            // Light green background
            Color(red: 0.85, green: 0.92, blue: 0.85)
                .ignoresSafeArea()
            
            VStack(spacing: 0) {
                // Header
                headerSection
                
                // Main Content Card
                ScrollView {
                    VStack {
                        // Product List
                        productListSection
                        
                        // Order Summary
                        orderSummarySection
                            .padding(.top, 24)
                            .padding(.bottom)
                        
                        // Continue Button
                        continueButton
                    }
                    .padding(.horizontal, 20)
                    .padding(.top, 20)
                }
                .background(
                    Color.white
                        .clipShape(
                            .rect(
                                topLeadingRadius: 30,
                                topTrailingRadius: 30
                            )
                        )
                )
                .shadow(color: Color.black.opacity(0.1), radius: 10, y: -5)
            }
        }
        .ignoresSafeArea()
    }
    
    private var headerSection: some View {
        HStack {
            // Back Button
            Button(action: { dismiss() }) {
                Image(systemName: "chevron.left")
                    .font(.system(size: 16, weight: .semibold))
                    .foregroundColor(.black)
                    .frame(width: 40, height: 40)
                    .background(Color.white)
                    .clipShape(Circle())
                    .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2)
            }
            
            Spacer()
            
            // Title
            Text("Cart")
                .font(.system(size: 20, weight: .semibold))
                .foregroundColor(.black)
            
            Spacer()
            
            // Item Count
            Text("\(totalItems) items")
                .font(.system(size: 14, weight: .medium))
                .foregroundColor(.black.opacity(0.6))
                .frame(width: 40)
        }
        .padding(.horizontal, 20)
        .padding(.top, 60)
        .padding(.bottom, 20)
    }
    
    private var productListSection: some View {
        VStack(spacing: 20) {
            ForEach(cartItems) { item in
                ProductRow(item: item) { updatedItem in
                    if let index = cartItems.firstIndex(where: { $0.id == updatedItem.id }) {
                        cartItems[index] = updatedItem
                    }
                }
            }
        }
    }
    
    private var orderSummarySection: some View {
        VStack(spacing: 16) {
            HStack {
                Text("Subtotal")
                    .font(.system(size: 16))
                    .foregroundColor(.black.opacity(0.7))
                
                Spacer()
                
                Text("$\(String(format: "%.2f", subtotal))")
                    .font(.system(size: 16))
                    .foregroundColor(.black)
            }
            
            HStack {
                Text("Delivery")
                    .font(.system(size: 16))
                    .foregroundColor(.black.opacity(0.7))
                
                Spacer()
                
                if deliveryFee == 0 {
                    Text("Free")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundColor(Color(red: 0.2, green: 0.6, blue: 0.3))
                        .padding(.horizontal, 10)
                        .padding(.vertical, 4)
                        .background(Color(red: 0.85, green: 0.95, blue: 0.85))
                        .clipShape(RoundedRectangle(cornerRadius: 8))
                } else {
                    Text("$\(String(format: "%.2f", deliveryFee))")
                        .font(.system(size: 16))
                        .foregroundColor(.black)
                }
            }
            
            Divider()
                .padding(.vertical, 8)
            
            HStack {
                Text("Total")
                    .font(.system(size: 18, weight: .bold))
                    .foregroundColor(.black)
                
                Spacer()
                
                Text("$\(String(format: "%.2f", total))")
                    .font(.system(size: 18, weight: .bold))
                    .foregroundColor(.black)
            }
        }
        .padding(20)
        .background(Color.gray.opacity(0.05))
        .clipShape(RoundedRectangle(cornerRadius: 16))
    }
    
    private var continueButton: some View {
        Button(action: {
            // Handle checkout
        }) {
            Text("Continue")
                .font(.system(size: 18, weight: .semibold))
                .foregroundColor(.white)
                .frame(maxWidth: .infinity)
                .padding(.vertical, 18)
                .background(Color.black)
                .clipShape(RoundedRectangle(cornerRadius: 16))
        }
        .padding(.horizontal, 20)
        .padding(.bottom, 34)
        .shadow(color: Color.black.opacity(0.2), radius: 10, y: 5)
    }
}

// MARK: - Product Row

struct ProductRow: View {
    let item: CartItem
    let onUpdate: (CartItem) -> Void
    
    var body: some View {
        HStack(spacing: 16) {
            // Plant Image
            ZStack {
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color(red: 0.9, green: 0.95, blue: 0.9))
                    .frame(width: 80, height: 80)
                
                Image(systemName: item.imageName)
                    .font(.system(size: 32))
                    .foregroundColor(Color(red: 0.2, green: 0.6, blue: 0.3))
            }
            
            // Product Info
            VStack(alignment: .leading, spacing: 8) {
                Text(item.name)
                    .font(.system(size: 16, weight: .semibold))
                    .foregroundColor(.black)
                
                // Size Badge
                Text(item.size)
                    .font(.system(size: 11, weight: .medium))
                    .foregroundColor(Color(red: 0.2, green: 0.5, blue: 0.2))
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .background(Color(red: 0.85, green: 0.95, blue: 0.85))
                    .clipShape(RoundedRectangle(cornerRadius: 6))
                
                Text("$\(String(format: "%.2f", item.price))")
                    .font(.system(size: 14))
                    .foregroundColor(.black.opacity(0.7))
            }
            
            Spacer()
            
            // Quantity Selector
            HStack(spacing: 12) {
                Button(action: {
                    if item.quantity > 1 {
                        var updated = item
                        updated.quantity -= 1
                        onUpdate(updated)
                    }
                }) {
                    Image(systemName: "minus")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundColor(.black.opacity(0.6))
                        .frame(width: 28, height: 28)
                        .background(Color.gray.opacity(0.1))
                        .clipShape(RoundedRectangle(cornerRadius: 6))
                }
                
                Text("\(item.quantity)")
                    .font(.system(size: 16, weight: .medium))
                    .foregroundColor(.black)
                    .frame(minWidth: 30)
                
                Button(action: {
                    var updated = item
                    updated.quantity += 1
                    onUpdate(updated)
                }) {
                    Image(systemName: "plus")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundColor(.black.opacity(0.6))
                        .frame(width: 28, height: 28)
                        .background(Color.gray.opacity(0.1))
                        .clipShape(RoundedRectangle(cornerRadius: 6))
                }
            }
            .padding(8)
            .background(Color.gray.opacity(0.05))
            .clipShape(RoundedRectangle(cornerRadius: 12))
        }
        .padding(.vertical, 8)
    }
}

// MARK: - Models

struct CartItem: Identifiable {
    let id: Int
    let name: String
    let size: String
    let price: Double
    var quantity: Int
    let imageName: String
}

Premium Template

Premium subscriptions are only available through our iOS app. Download the app to unlock premium templates and features.

Download on the App Store

More Templates

Similar Blogs

View All Articles
How to Translate Xcode String Catalogs with AI

How to Translate Xcode String Catalogs with AI

Learn how to efficiently translate Xcode string catalogs using AI fast, streamlining localization for your iOS and SwiftUI applications.

by dockui
May 4
How to optimize your SwiftUI app in 2026

How to optimize your SwiftUI app in 2026

Discover top strategies to optimize your SwiftUI app in 2026, enhancing performance, responsiveness, and maintainability with modern concurrency.

by dockui
Apr 29
SwiftUI: iOS Confirmation Alert (Confirm + Cancel)

SwiftUI: iOS Confirmation Alert (Confirm + Cancel)

SwiftUI iOS confirmation alert with Confirm and Cancel buttons. Beginners guide to building safe actions, destructive deletes, and commit dialogs.

by dockui
Nov 11
SwiftUI: IOS Haptic Feedback Guide 2026

SwiftUI: IOS Haptic Feedback Guide 2026

Add click to vibrate haptic feedback in SwiftUI. Full guide to UIImpactFeedbackgenerator, success, error, selection, and tap vibration patterns with code.

by dockui
Nov 11

Code copied to clipboard!