skiptools / skiptools/skip-ui

Android/Fuse: conditional styling issue after tap selection

Open
#520 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Swift
Stars
330
Forks
76
Avg merge
3h 6m
Merged PRs (30d)
1

Description

Summary

On Android in native/Fuse mode, grid cells can show stale conditional styling after selection changes.

I am building a calendar which consists of a LazyVGrid of tappable day cells with a shared @State selectedDate. Each cell receives an isSelected boolean. Logs confirm that after each tap, exactly one cell renders with isSelected == true, but visually the newly selected cell and previously tapped cells intermittently blink together. This does not happen on iOS.

Environment

  • Android device: Samsung SM-G973F, Android 12
  • Build: release, minified
  • Mode: native / Skip Fuse
  • Reproduces with:
    • skip-fuse-ui 1.18.1
    • skip-ui 1.59.1
  • Last known working in our released app:
    • skip-fuse-ui 1.13.0
    • skip-ui 1.53.1

Minimal POC

import SwiftUI

struct CalendarBugRepro: View {
    @State var selectedDate = Date()
    private let calendar = Calendar(identifier: .gregorian)

    var body: some View {
        LazyVGrid(
            columns: Array(repeating: GridItem(.flexible(), spacing: 0), count: 7),
            spacing: 4
        ) {
            ForEach(days, id: \.self) { date in
                Button {
                    selectedDate = date
                } label: {
                    ReproDayCell(
                        day: calendar.component(.day, from: date),
                        isSelected: calendar.isDate(date, inSameDayAs: selectedDate)
                    )
                }
                .buttonStyle(.plain)
            }
        }
        .padding()
    }

    private var days: [Date] {
        let first = calendar.date(from: DateComponents(year: 2026, month: 9, day: 1))!
        return (0..<30).compactMap { calendar.date(byAdding: .day, value: $0, to: first) }
    }
}

struct ReproDayCell: View {
    let day: Int
    let isSelected: Bool

    var body: some View {
        Text("\(day)")
            .font(.system(size: 14, weight: isSelected ? .bold : .regular))
            .foregroundStyle(isSelected ? .white : .primary)
            .frame(width: 32, height: 32)
            .background(Circle().fill(isSelected ? Color.accentColor : .clear))
            .frame(height: 44)
    }
}

Expected

Only the selected day is highlighted.

Actual

Previously selected days blink together, even though only one cell has isSelected == true.

Workaround

Keeping the view tree stable and toggling opacity avoids the issue:

  .background {
      Circle()
          .fill(Color.accentColor)
          .opacity(isSelected ? 1 : 0)
  }

So the issue seems related to conditional styling like:

  .background(Circle().fill(isSelected ? Color.accentColor : .clear))
  .foregroundStyle(isSelected ? .white : .primary)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the Minimal POC using LazyVGrid, tappable day cells, and conditional styling on Android in native/Fuse mode. Compare the blinking behavior with the opacity workaround and the listed skip-fuse-ui and skip-ui versions. Done means that after selection changes, only the selected day remains highlighted without stale cells blinking.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
frontend, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.