import SwiftUI struct SnackbarOverlay: View { #if os(Android) let center: SnackbarCenter @State var current: SnackbarMessage? @State var observer: Any? #else @ObservedObject var center: SnackbarCenter #endif var body: some View { #if os(Android) VStack { if let message = current { HStack(spacing: 10) { if let icon = message.iconSystemName, !icon.isEmpty { Image(systemName: icon) .font(.system(size: 16, weight: .semibold)) } Text(message.title) .font(AppTypography.heading3) .multilineTextAlignment(.leading) .lineLimit(3) Spacer(minLength: 0) } .foregroundStyle(Color.white) .padding(.top, 14) .padding(.horizontal, 16) .padding(.bottom, 16) .frame(maxWidth: .infinity, alignment: .leading) .background(message.style.backgroundColor) .appContentShape(Rectangle()) .onTapGesture { center.handleTap() } .transition(.move(edge: .top).combined(with: .opacity)) .zIndex(999) } Spacer() } .animation(.spring(response: 0.3, dampingFraction: 0.9), value: current?.id) .allowsHitTesting(current != nil) .onAppear { current = center.current attachObserverIfNeeded() } .onDisappear { detachObserver() } #else VStack { if let message = center.current { HStack(spacing: 10) { if let icon = message.iconSystemName, !icon.isEmpty { Image(systemName: icon) .font(.system(size: 16, weight: .semibold)) } Text(message.title) .font(AppTypography.heading3) .multilineTextAlignment(.leading) .lineLimit(3) Spacer(minLength: 0) } .foregroundStyle(Color.white) .padding(.top, 14) .padding(.horizontal, 16) .padding(.bottom, 16) .frame(maxWidth: .infinity, alignment: .leading) .background(message.style.backgroundColor) .appContentShape(Rectangle()) .onTapGesture { center.handleTap() } .transition(.move(edge: .top).combined(with: .opacity)) .zIndex(999) } Spacer() } .animation(.spring(response: 0.3, dampingFraction: 0.9), value: center.current?.id) .allowsHitTesting(center.current != nil) #endif } #if os(Android) private func attachObserverIfNeeded() { guard observer == nil else { return } observer = NotificationCenter.default.addObserver( forName: .snackbarDidChange, object: nil, queue: nil ) { _ in Task { @MainActor in current = center.current } } } private func detachObserver() { guard let observer else { return } NotificationCenter.default.removeObserver(observer) self.observer = nil } #endif }