Skip to content

Conversation

@Haidar0096
Copy link
Contributor

Summary

Adds two new StoreKit 2 features to in_app_purchase_storekit:

  • SK2Transaction.unfinishedTransactions() - Queries only unfinished transactions for better performance
  • SK2PurchaseDetails.appAccountToken - Exposes user UUID for backend integration

Motivation

  1. Performance: Developers often only need unfinished transactions to complete them, not all historical transactions. This mirrors Apple's official Transaction.unfinished API.
  2. User Identification: The ability to set appAccountToken already exists when making purchases, but reading it back from transaction details was missing.

Changes

  • Added pigeon interface method for unfinishedTransactions()
  • Implemented Swift native code using Apple's Transaction.unfinished API
  • Exposed appAccountToken property in SK2PurchaseDetails
  • Added unit tests for both features

Breaking Changes

None. Both features are additive and maintain full backward compatibility.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces two new features from StoreKit 2: the unfinishedTransactions() API and the exposure of appAccountToken on purchase details. The changes are additive, well-implemented, and follow the existing architecture. The implementation correctly spans native Swift code, pigeon interface updates, and Dart-side wrappers, complete with corresponding unit tests. This is a solid contribution that enhances the plugin's capabilities.

Comment on lines 239 to 250
@MainActor in
do {
let transactionsMsgs = await rawUnfinishedTransactions().map {
$0.convertToPigeon(receipt: nil)
}
completion(.success(transactionsMsgs))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The do block is unnecessary here because rawUnfinishedTransactions() is a non-throwing function. While it doesn't cause a bug in this case, it's a good practice to avoid do-catch structures when no errors can be thrown and caught. If rawUnfinishedTransactions were to throw, the lack of a catch block would prevent the completion handler from being called on an error path. Removing the do block simplifies the code and makes it more robust.

      @MainActor in
      let transactionsMsgs = await rawUnfinishedTransactions().map {
        $0.convertToPigeon(receipt: nil)
      }
      completion(.success(transactionsMsgs))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while since I last used swift, but I have the same question: why the do block?

@Haidar0096
Copy link
Contributor Author

Haidar0096 commented Nov 15, 2025

This also partially solves #165355

@Haidar0096 Haidar0096 force-pushed the add-storekit2-transaction-unfinished-and-appaccounttoken branch from dabe171 to fa16cac Compare November 16, 2025 19:00
Copy link
Contributor

@LouiseHsu LouiseHsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good!! Thank you so much for your contribution. I took a look, and the test failures seems to be half swift formatting errors and pigeon files. Could you upgrade pigeon and regenerate the files + fix the swift formatting errors? Otherwise looks good!

@Haidar0096 Haidar0096 force-pushed the add-storekit2-transaction-unfinished-and-appaccounttoken branch from fa16cac to 1c0aecc Compare November 17, 2025 10:24
@Haidar0096
Copy link
Contributor Author

Haidar0096 commented Nov 17, 2025

@LouiseHsu Thanks for the kind words!

Problem with updating pigeon to the latest version is that when I run dart run pigeon --input pigeons/sk2_pigeon.dart
It is generating equality methods that are causing compilation error

I downgraded pigeon until I found a working version (i.e. version before the introduction of the generation of == method)

Here is one of the error logs when running on real device after updating pigeon to the latest version 26.1.0:

Swift Compiler Error (Xcode): Invalid redeclaration of '=='
<path to packages folder>/flutter_packages/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/sk2_pigeon.g.swift:242:14
Generated file that is causing the error
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v25.5.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon

import Foundation

#if os(iOS)
  import Flutter
#elseif os(macOS)
  import FlutterMacOS
#else
  #error("Unsupported platform.")
#endif

/// Error class for passing custom error details to Dart side.
final class PigeonError: Error {
  let code: String
  let message: String?
  let details: Sendable?

  init(code: String, message: String?, details: Sendable?) {
    self.code = code
    self.message = message
    self.details = details
  }

  var localizedDescription: String {
    return
      "PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
  }
}

private func wrapResult(_ result: Any?) -> [Any?] {
  return [result]
}

private func wrapError(_ error: Any) -> [Any?] {
  if let pigeonError = error as? PigeonError {
    return [
      pigeonError.code,
      pigeonError.message,
      pigeonError.details,
    ]
  }
  if let flutterError = error as? FlutterError {
    return [
      flutterError.code,
      flutterError.message,
      flutterError.details,
    ]
  }
  return [
    "\(error)",
    "\(type(of: error))",
    "Stacktrace: \(Thread.callStackSymbols)",
  ]
}

private func createConnectionError(withChannelName channelName: String) -> PigeonError {
  return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "")
}

private func isNullish(_ value: Any?) -> Bool {
  return value is NSNull || value == nil
}

private func nilOrValue<T>(_ value: Any?) -> T? {
  if value is NSNull { return nil }
  return value as! T?
}

func deepEqualsSk2Pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool {
  let cleanLhs = nilOrValue(lhs) as Any?
  let cleanRhs = nilOrValue(rhs) as Any?
  switch (cleanLhs, cleanRhs) {
  case (nil, nil):
    return true

  case (nil, _), (_, nil):
    return false

  case is (Void, Void):
    return true

  case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
    return cleanLhsHashable == cleanRhsHashable

  case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
    guard cleanLhsArray.count == cleanRhsArray.count else { return false }
    for (index, element) in cleanLhsArray.enumerated() {
      if !deepEqualsSk2Pigeon(element, cleanRhsArray[index]) {
        return false
      }
    }
    return true

  case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
    guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
    for (key, cleanLhsValue) in cleanLhsDictionary {
      guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
      if !deepEqualsSk2Pigeon(cleanLhsValue, cleanRhsDictionary[key]!) {
        return false
      }
    }
    return true

  default:
    // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
    return false
  }
}

func deepHashSk2Pigeon(value: Any?, hasher: inout Hasher) {
  if let valueList = value as? [AnyHashable] {
     for item in valueList { deepHashSk2Pigeon(value: item, hasher: &hasher) }
     return
  }

  if let valueDict = value as? [AnyHashable: AnyHashable] {
    for key in valueDict.keys { 
      hasher.combine(key)
      deepHashSk2Pigeon(value: valueDict[key]!, hasher: &hasher)
    }
    return
  }

  if let hashableValue = value as? AnyHashable {
    hasher.combine(hashableValue.hashValue)
  }

  return hasher.combine(String(describing: value))
}

    

enum SK2ProductTypeMessage: Int {
  /// A consumable in-app purchase.
  case consumable = 0
  /// A non-consumable in-app purchase.
  case nonConsumable = 1
  /// A non-renewing subscription.
  case nonRenewable = 2
  /// An auto-renewable subscription.
  case autoRenewable = 3
}

enum SK2SubscriptionOfferTypeMessage: Int {
  case introductory = 0
  case promotional = 1
  case winBack = 2
}

enum SK2SubscriptionOfferPaymentModeMessage: Int {
  case payAsYouGo = 0
  case payUpFront = 1
  case freeTrial = 2
}

enum SK2SubscriptionPeriodUnitMessage: Int {
  case day = 0
  case week = 1
  case month = 2
  case year = 3
}

enum SK2ProductPurchaseResultMessage: Int {
  case success = 0
  case unverified = 1
  case userCancelled = 2
  case pending = 3
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2SubscriptionOfferMessage: Hashable {
  var id: String? = nil
  var price: Double
  var type: SK2SubscriptionOfferTypeMessage
  var period: SK2SubscriptionPeriodMessage
  var periodCount: Int64
  var paymentMode: SK2SubscriptionOfferPaymentModeMessage


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2SubscriptionOfferMessage? {
    let id: String? = nilOrValue(pigeonVar_list[0])
    let price = pigeonVar_list[1] as! Double
    let type = pigeonVar_list[2] as! SK2SubscriptionOfferTypeMessage
    let period = pigeonVar_list[3] as! SK2SubscriptionPeriodMessage
    let periodCount = pigeonVar_list[4] as! Int64
    let paymentMode = pigeonVar_list[5] as! SK2SubscriptionOfferPaymentModeMessage

    return SK2SubscriptionOfferMessage(
      id: id,
      price: price,
      type: type,
      period: period,
      periodCount: periodCount,
      paymentMode: paymentMode
    )
  }
  func toList() -> [Any?] {
    return [
      id,
      price,
      type,
      period,
      periodCount,
      paymentMode,
    ]
  }
  static func == (lhs: SK2SubscriptionOfferMessage, rhs: SK2SubscriptionOfferMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2SubscriptionPeriodMessage: Hashable {
  /// The number of units that the period represents.
  var value: Int64
  /// The unit of time that this period represents.
  var unit: SK2SubscriptionPeriodUnitMessage


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2SubscriptionPeriodMessage? {
    let value = pigeonVar_list[0] as! Int64
    let unit = pigeonVar_list[1] as! SK2SubscriptionPeriodUnitMessage

    return SK2SubscriptionPeriodMessage(
      value: value,
      unit: unit
    )
  }
  func toList() -> [Any?] {
    return [
      value,
      unit,
    ]
  }
  static func == (lhs: SK2SubscriptionPeriodMessage, rhs: SK2SubscriptionPeriodMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2SubscriptionInfoMessage: Hashable {
  /// An array of all the promotional offers configured for this subscription.
  var promotionalOffers: [SK2SubscriptionOfferMessage]
  /// The group identifier for this subscription.
  var subscriptionGroupID: String
  /// The duration that this subscription lasts before auto-renewing.
  var subscriptionPeriod: SK2SubscriptionPeriodMessage


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2SubscriptionInfoMessage? {
    let promotionalOffers = pigeonVar_list[0] as! [SK2SubscriptionOfferMessage]
    let subscriptionGroupID = pigeonVar_list[1] as! String
    let subscriptionPeriod = pigeonVar_list[2] as! SK2SubscriptionPeriodMessage

    return SK2SubscriptionInfoMessage(
      promotionalOffers: promotionalOffers,
      subscriptionGroupID: subscriptionGroupID,
      subscriptionPeriod: subscriptionPeriod
    )
  }
  func toList() -> [Any?] {
    return [
      promotionalOffers,
      subscriptionGroupID,
      subscriptionPeriod,
    ]
  }
  static func == (lhs: SK2SubscriptionInfoMessage, rhs: SK2SubscriptionInfoMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// A Pigeon message class representing a Product
/// https://developer.apple.com/documentation/storekit/product
///
/// Generated class from Pigeon that represents data sent in messages.
struct SK2ProductMessage: Hashable {
  /// The unique product identifier.
  var id: String
  /// The localized display name of the product, if it exists.
  var displayName: String
  /// The localized description of the product.
  var description: String
  /// The localized string representation of the product price, suitable for display.
  var price: Double
  /// The localized price of the product as a string.
  var displayPrice: String
  /// The types of in-app purchases.
  var type: SK2ProductTypeMessage
  /// The subscription information for an auto-renewable subscription.
  var subscription: SK2SubscriptionInfoMessage? = nil
  /// The currency and locale information for this product
  var priceLocale: SK2PriceLocaleMessage


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2ProductMessage? {
    let id = pigeonVar_list[0] as! String
    let displayName = pigeonVar_list[1] as! String
    let description = pigeonVar_list[2] as! String
    let price = pigeonVar_list[3] as! Double
    let displayPrice = pigeonVar_list[4] as! String
    let type = pigeonVar_list[5] as! SK2ProductTypeMessage
    let subscription: SK2SubscriptionInfoMessage? = nilOrValue(pigeonVar_list[6])
    let priceLocale = pigeonVar_list[7] as! SK2PriceLocaleMessage

    return SK2ProductMessage(
      id: id,
      displayName: displayName,
      description: description,
      price: price,
      displayPrice: displayPrice,
      type: type,
      subscription: subscription,
      priceLocale: priceLocale
    )
  }
  func toList() -> [Any?] {
    return [
      id,
      displayName,
      description,
      price,
      displayPrice,
      type,
      subscription,
      priceLocale,
    ]
  }
  static func == (lhs: SK2ProductMessage, rhs: SK2ProductMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2PriceLocaleMessage: Hashable {
  var currencyCode: String
  var currencySymbol: String


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2PriceLocaleMessage? {
    let currencyCode = pigeonVar_list[0] as! String
    let currencySymbol = pigeonVar_list[1] as! String

    return SK2PriceLocaleMessage(
      currencyCode: currencyCode,
      currencySymbol: currencySymbol
    )
  }
  func toList() -> [Any?] {
    return [
      currencyCode,
      currencySymbol,
    ]
  }
  static func == (lhs: SK2PriceLocaleMessage, rhs: SK2PriceLocaleMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// A Pigeon message class representing a Signature
/// https://developer.apple.com/documentation/storekit/product/subscriptionoffer/signature
///
/// Generated class from Pigeon that represents data sent in messages.
struct SK2SubscriptionOfferSignatureMessage: Hashable {
  var keyID: String
  var nonce: String
  var timestamp: Int64
  var signature: String


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2SubscriptionOfferSignatureMessage? {
    let keyID = pigeonVar_list[0] as! String
    let nonce = pigeonVar_list[1] as! String
    let timestamp = pigeonVar_list[2] as! Int64
    let signature = pigeonVar_list[3] as! String

    return SK2SubscriptionOfferSignatureMessage(
      keyID: keyID,
      nonce: nonce,
      timestamp: timestamp,
      signature: signature
    )
  }
  func toList() -> [Any?] {
    return [
      keyID,
      nonce,
      timestamp,
      signature,
    ]
  }
  static func == (lhs: SK2SubscriptionOfferSignatureMessage, rhs: SK2SubscriptionOfferSignatureMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2SubscriptionOfferPurchaseMessage: Hashable {
  var promotionalOfferId: String
  var promotionalOfferSignature: SK2SubscriptionOfferSignatureMessage


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2SubscriptionOfferPurchaseMessage? {
    let promotionalOfferId = pigeonVar_list[0] as! String
    let promotionalOfferSignature = pigeonVar_list[1] as! SK2SubscriptionOfferSignatureMessage

    return SK2SubscriptionOfferPurchaseMessage(
      promotionalOfferId: promotionalOfferId,
      promotionalOfferSignature: promotionalOfferSignature
    )
  }
  func toList() -> [Any?] {
    return [
      promotionalOfferId,
      promotionalOfferSignature,
    ]
  }
  static func == (lhs: SK2SubscriptionOfferPurchaseMessage, rhs: SK2SubscriptionOfferPurchaseMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2ProductPurchaseOptionsMessage: Hashable {
  var appAccountToken: String? = nil
  var quantity: Int64? = nil
  var promotionalOffer: SK2SubscriptionOfferPurchaseMessage? = nil
  var winBackOfferId: String? = nil


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2ProductPurchaseOptionsMessage? {
    let appAccountToken: String? = nilOrValue(pigeonVar_list[0])
    let quantity: Int64? = nilOrValue(pigeonVar_list[1])
    let promotionalOffer: SK2SubscriptionOfferPurchaseMessage? = nilOrValue(pigeonVar_list[2])
    let winBackOfferId: String? = nilOrValue(pigeonVar_list[3])

    return SK2ProductPurchaseOptionsMessage(
      appAccountToken: appAccountToken,
      quantity: quantity,
      promotionalOffer: promotionalOffer,
      winBackOfferId: winBackOfferId
    )
  }
  func toList() -> [Any?] {
    return [
      appAccountToken,
      quantity,
      promotionalOffer,
      winBackOfferId,
    ]
  }
  static func == (lhs: SK2ProductPurchaseOptionsMessage, rhs: SK2ProductPurchaseOptionsMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2TransactionMessage: Hashable {
  var id: Int64
  var originalId: Int64
  var productId: String
  var purchaseDate: String
  var expirationDate: String? = nil
  var purchasedQuantity: Int64
  var appAccountToken: String? = nil
  var restoring: Bool
  var receiptData: String? = nil
  var error: SK2ErrorMessage? = nil
  var jsonRepresentation: String? = nil


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2TransactionMessage? {
    let id = pigeonVar_list[0] as! Int64
    let originalId = pigeonVar_list[1] as! Int64
    let productId = pigeonVar_list[2] as! String
    let purchaseDate = pigeonVar_list[3] as! String
    let expirationDate: String? = nilOrValue(pigeonVar_list[4])
    let purchasedQuantity = pigeonVar_list[5] as! Int64
    let appAccountToken: String? = nilOrValue(pigeonVar_list[6])
    let restoring = pigeonVar_list[7] as! Bool
    let receiptData: String? = nilOrValue(pigeonVar_list[8])
    let error: SK2ErrorMessage? = nilOrValue(pigeonVar_list[9])
    let jsonRepresentation: String? = nilOrValue(pigeonVar_list[10])

    return SK2TransactionMessage(
      id: id,
      originalId: originalId,
      productId: productId,
      purchaseDate: purchaseDate,
      expirationDate: expirationDate,
      purchasedQuantity: purchasedQuantity,
      appAccountToken: appAccountToken,
      restoring: restoring,
      receiptData: receiptData,
      error: error,
      jsonRepresentation: jsonRepresentation
    )
  }
  func toList() -> [Any?] {
    return [
      id,
      originalId,
      productId,
      purchaseDate,
      expirationDate,
      purchasedQuantity,
      appAccountToken,
      restoring,
      receiptData,
      error,
      jsonRepresentation,
    ]
  }
  static func == (lhs: SK2TransactionMessage, rhs: SK2TransactionMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

/// Generated class from Pigeon that represents data sent in messages.
struct SK2ErrorMessage: Hashable {
  var code: Int64
  var domain: String
  var userInfo: [String: Any]? = nil


  // swift-format-ignore: AlwaysUseLowerCamelCase
  static func fromList(_ pigeonVar_list: [Any?]) -> SK2ErrorMessage? {
    let code = pigeonVar_list[0] as! Int64
    let domain = pigeonVar_list[1] as! String
    let userInfo: [String: Any]? = nilOrValue(pigeonVar_list[2])

    return SK2ErrorMessage(
      code: code,
      domain: domain,
      userInfo: userInfo
    )
  }
  func toList() -> [Any?] {
    return [
      code,
      domain,
      userInfo,
    ]
  }
  static func == (lhs: SK2ErrorMessage, rhs: SK2ErrorMessage) -> Bool {
    return deepEqualsSk2Pigeon(lhs.toList(), rhs.toList())  }
  func hash(into hasher: inout Hasher) {
    deepHashSk2Pigeon(value: toList(), hasher: &hasher)
  }
}

private class Sk2PigeonPigeonCodecReader: FlutterStandardReader {
  override func readValue(ofType type: UInt8) -> Any? {
    switch type {
    case 129:
      let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
      if let enumResultAsInt = enumResultAsInt {
        return SK2ProductTypeMessage(rawValue: enumResultAsInt)
      }
      return nil
    case 130:
      let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
      if let enumResultAsInt = enumResultAsInt {
        return SK2SubscriptionOfferTypeMessage(rawValue: enumResultAsInt)
      }
      return nil
    case 131:
      let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
      if let enumResultAsInt = enumResultAsInt {
        return SK2SubscriptionOfferPaymentModeMessage(rawValue: enumResultAsInt)
      }
      return nil
    case 132:
      let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
      if let enumResultAsInt = enumResultAsInt {
        return SK2SubscriptionPeriodUnitMessage(rawValue: enumResultAsInt)
      }
      return nil
    case 133:
      let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
      if let enumResultAsInt = enumResultAsInt {
        return SK2ProductPurchaseResultMessage(rawValue: enumResultAsInt)
      }
      return nil
    case 134:
      return SK2SubscriptionOfferMessage.fromList(self.readValue() as! [Any?])
    case 135:
      return SK2SubscriptionPeriodMessage.fromList(self.readValue() as! [Any?])
    case 136:
      return SK2SubscriptionInfoMessage.fromList(self.readValue() as! [Any?])
    case 137:
      return SK2ProductMessage.fromList(self.readValue() as! [Any?])
    case 138:
      return SK2PriceLocaleMessage.fromList(self.readValue() as! [Any?])
    case 139:
      return SK2SubscriptionOfferSignatureMessage.fromList(self.readValue() as! [Any?])
    case 140:
      return SK2SubscriptionOfferPurchaseMessage.fromList(self.readValue() as! [Any?])
    case 141:
      return SK2ProductPurchaseOptionsMessage.fromList(self.readValue() as! [Any?])
    case 142:
      return SK2TransactionMessage.fromList(self.readValue() as! [Any?])
    case 143:
      return SK2ErrorMessage.fromList(self.readValue() as! [Any?])
    default:
      return super.readValue(ofType: type)
    }
  }
}

private class Sk2PigeonPigeonCodecWriter: FlutterStandardWriter {
  override func writeValue(_ value: Any) {
    if let value = value as? SK2ProductTypeMessage {
      super.writeByte(129)
      super.writeValue(value.rawValue)
    } else if let value = value as? SK2SubscriptionOfferTypeMessage {
      super.writeByte(130)
      super.writeValue(value.rawValue)
    } else if let value = value as? SK2SubscriptionOfferPaymentModeMessage {
      super.writeByte(131)
      super.writeValue(value.rawValue)
    } else if let value = value as? SK2SubscriptionPeriodUnitMessage {
      super.writeByte(132)
      super.writeValue(value.rawValue)
    } else if let value = value as? SK2ProductPurchaseResultMessage {
      super.writeByte(133)
      super.writeValue(value.rawValue)
    } else if let value = value as? SK2SubscriptionOfferMessage {
      super.writeByte(134)
      super.writeValue(value.toList())
    } else if let value = value as? SK2SubscriptionPeriodMessage {
      super.writeByte(135)
      super.writeValue(value.toList())
    } else if let value = value as? SK2SubscriptionInfoMessage {
      super.writeByte(136)
      super.writeValue(value.toList())
    } else if let value = value as? SK2ProductMessage {
      super.writeByte(137)
      super.writeValue(value.toList())
    } else if let value = value as? SK2PriceLocaleMessage {
      super.writeByte(138)
      super.writeValue(value.toList())
    } else if let value = value as? SK2SubscriptionOfferSignatureMessage {
      super.writeByte(139)
      super.writeValue(value.toList())
    } else if let value = value as? SK2SubscriptionOfferPurchaseMessage {
      super.writeByte(140)
      super.writeValue(value.toList())
    } else if let value = value as? SK2ProductPurchaseOptionsMessage {
      super.writeByte(141)
      super.writeValue(value.toList())
    } else if let value = value as? SK2TransactionMessage {
      super.writeByte(142)
      super.writeValue(value.toList())
    } else if let value = value as? SK2ErrorMessage {
      super.writeByte(143)
      super.writeValue(value.toList())
    } else {
      super.writeValue(value)
    }
  }
}

private class Sk2PigeonPigeonCodecReaderWriter: FlutterStandardReaderWriter {
  override func reader(with data: Data) -> FlutterStandardReader {
    return Sk2PigeonPigeonCodecReader(data: data)
  }

  override func writer(with data: NSMutableData) -> FlutterStandardWriter {
    return Sk2PigeonPigeonCodecWriter(data: data)
  }
}

class Sk2PigeonPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
  static let shared = Sk2PigeonPigeonCodec(readerWriter: Sk2PigeonPigeonCodecReaderWriter())
}


/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol InAppPurchase2API {
  func canMakePayments() throws -> Bool
  func products(identifiers: [String], completion: @escaping (Result<[SK2ProductMessage], Error>) -> Void)
  func purchase(id: String, options: SK2ProductPurchaseOptionsMessage?, completion: @escaping (Result<SK2ProductPurchaseResultMessage, Error>) -> Void)
  func isWinBackOfferEligible(productId: String, offerId: String, completion: @escaping (Result<Bool, Error>) -> Void)
  func isIntroductoryOfferEligible(productId: String, completion: @escaping (Result<Bool, Error>) -> Void)
  func transactions(completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void)
  func unfinishedTransactions(completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void)
  func finish(id: Int64, completion: @escaping (Result<Void, Error>) -> Void)
  func startListeningToTransactions() throws
  func stopListeningToTransactions() throws
  func restorePurchases(completion: @escaping (Result<Void, Error>) -> Void)
  func countryCode(completion: @escaping (Result<String, Error>) -> Void)
  func sync(completion: @escaping (Result<Void, Error>) -> Void)
}

/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class InAppPurchase2APISetup {
  static var codec: FlutterStandardMessageCodec { Sk2PigeonPigeonCodec.shared }
  /// Sets up an instance of `InAppPurchase2API` to handle messages through the `binaryMessenger`.
  static func setUp(binaryMessenger: FlutterBinaryMessenger, api: InAppPurchase2API?, messageChannelSuffix: String = "") {
    let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
    let canMakePaymentsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.canMakePayments\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      canMakePaymentsChannel.setMessageHandler { _, reply in
        do {
          let result = try api.canMakePayments()
          reply(wrapResult(result))
        } catch {
          reply(wrapError(error))
        }
      }
    } else {
      canMakePaymentsChannel.setMessageHandler(nil)
    }
    let productsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.products\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      productsChannel.setMessageHandler { message, reply in
        let args = message as! [Any?]
        let identifiersArg = args[0] as! [String]
        api.products(identifiers: identifiersArg) { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      productsChannel.setMessageHandler(nil)
    }
    let purchaseChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.purchase\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      purchaseChannel.setMessageHandler { message, reply in
        let args = message as! [Any?]
        let idArg = args[0] as! String
        let optionsArg: SK2ProductPurchaseOptionsMessage? = nilOrValue(args[1])
        api.purchase(id: idArg, options: optionsArg) { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      purchaseChannel.setMessageHandler(nil)
    }
    let isWinBackOfferEligibleChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isWinBackOfferEligible\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      isWinBackOfferEligibleChannel.setMessageHandler { message, reply in
        let args = message as! [Any?]
        let productIdArg = args[0] as! String
        let offerIdArg = args[1] as! String
        api.isWinBackOfferEligible(productId: productIdArg, offerId: offerIdArg) { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      isWinBackOfferEligibleChannel.setMessageHandler(nil)
    }
    let isIntroductoryOfferEligibleChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.isIntroductoryOfferEligible\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      isIntroductoryOfferEligibleChannel.setMessageHandler { message, reply in
        let args = message as! [Any?]
        let productIdArg = args[0] as! String
        api.isIntroductoryOfferEligible(productId: productIdArg) { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      isIntroductoryOfferEligibleChannel.setMessageHandler(nil)
    }
    let transactionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.transactions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      transactionsChannel.setMessageHandler { _, reply in
        api.transactions { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      transactionsChannel.setMessageHandler(nil)
    }
    let unfinishedTransactionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.unfinishedTransactions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      unfinishedTransactionsChannel.setMessageHandler { _, reply in
        api.unfinishedTransactions { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      unfinishedTransactionsChannel.setMessageHandler(nil)
    }
    let finishChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      finishChannel.setMessageHandler { message, reply in
        let args = message as! [Any?]
        let idArg = args[0] as! Int64
        api.finish(id: idArg) { result in
          switch result {
          case .success:
            reply(wrapResult(nil))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      finishChannel.setMessageHandler(nil)
    }
    let startListeningToTransactionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.startListeningToTransactions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      startListeningToTransactionsChannel.setMessageHandler { _, reply in
        do {
          try api.startListeningToTransactions()
          reply(wrapResult(nil))
        } catch {
          reply(wrapError(error))
        }
      }
    } else {
      startListeningToTransactionsChannel.setMessageHandler(nil)
    }
    let stopListeningToTransactionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.stopListeningToTransactions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      stopListeningToTransactionsChannel.setMessageHandler { _, reply in
        do {
          try api.stopListeningToTransactions()
          reply(wrapResult(nil))
        } catch {
          reply(wrapError(error))
        }
      }
    } else {
      stopListeningToTransactionsChannel.setMessageHandler(nil)
    }
    let restorePurchasesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.restorePurchases\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      restorePurchasesChannel.setMessageHandler { _, reply in
        api.restorePurchases { result in
          switch result {
          case .success:
            reply(wrapResult(nil))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      restorePurchasesChannel.setMessageHandler(nil)
    }
    let countryCodeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.countryCode\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      countryCodeChannel.setMessageHandler { _, reply in
        api.countryCode { result in
          switch result {
          case .success(let res):
            reply(wrapResult(res))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      countryCodeChannel.setMessageHandler(nil)
    }
    let syncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.sync\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
    if let api = api {
      syncChannel.setMessageHandler { _, reply in
        api.sync { result in
          switch result {
          case .success:
            reply(wrapResult(nil))
          case .failure(let error):
            reply(wrapError(error))
          }
        }
      }
    } else {
      syncChannel.setMessageHandler(nil)
    }
  }
}
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol InAppPurchase2CallbackAPIProtocol {
  func onTransactionsUpdated(newTransactions newTransactionsArg: [SK2TransactionMessage], completion: @escaping (Result<Void, PigeonError>) -> Void)
}
class InAppPurchase2CallbackAPI: InAppPurchase2CallbackAPIProtocol {
  private let binaryMessenger: FlutterBinaryMessenger
  private let messageChannelSuffix: String
  init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") {
    self.binaryMessenger = binaryMessenger
    self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
  }
  var codec: Sk2PigeonPigeonCodec {
    return Sk2PigeonPigeonCodec.shared
  }
  func onTransactionsUpdated(newTransactions newTransactionsArg: [SK2TransactionMessage], completion: @escaping (Result<Void, PigeonError>) -> Void) {
    let channelName: String = "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated\(messageChannelSuffix)"
    let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
    channel.sendMessage([newTransactionsArg] as [Any?]) { response in
      guard let listResponse = response as? [Any?] else {
        completion(.failure(createConnectionError(withChannelName: channelName)))
        return
      }
      if listResponse.count > 1 {
        let code: String = listResponse[0] as! String
        let message: String? = nilOrValue(listResponse[1])
        let details: String? = nilOrValue(listResponse[2])
        completion(.failure(PigeonError(code: code, message: message, details: details)))
      } else {
        completion(.success(()))
      }
    }
  }
}

Here is the version that introduced this behavior in the generated code
Screenshot 2025-11-17 at 11 51 37 AM

What I am going to do now is pin pigeon to the latest version I found that works (25.2.0) and push the changes.

Do you think this works?

@stuartmorgan-g
Copy link
Collaborator

A repo-wide update of all packages to Pigeon 26 landed yesterday, FYI.

@Haidar0096
Copy link
Contributor Author

@stuartmorgan-g I fetched it locally, and ran the pigeon generator, and I am hitting
something weird, here are the things I did exactly and the weird thing happening:

Commands I ran:

# Verify pigeon version
cd packages/in_app_purchase/in_app_purchase_storekit
dart pub deps | grep pigeon
# Output: ├── pigeon 26.1.0

# Regenerate pigeon
dart run pigeon --input pigeons/sk2_pigeon.dart

# Check format
cd ../../..
dart run script/tool/bin/flutter_plugin_tools.dart format --packages
in_app_purchase_storekit

Result:
Format fails with 2 Swift lint errors:

  sk2_pigeon.g.swift:76: [AlwaysUseLowerCamelCase] rename 'deepEqualssk2_pigeon' using
  lowerCamelCase
  sk2_pigeon.g.swift:117: [AlwaysUseLowerCamelCase] rename 'deepHashsk2_pigeon' using
  lowerCamelCase

The weird thing: upstream/main's generated file has the // swift-format-ignore: AlwaysUseLowerCamelCase comments before these functions, but when I regenerate pigeon
locally with the same version (26.1.0), it doesn't generate them. Should pigeon be
generating these comments, or are you doing something else to make them be generated when you run pigeon?

@stuartmorgan-g
Copy link
Collaborator

Sorry, I meant to follow up on that this morning and had forgotten. I filed flutter/flutter#178736 to track the problem; for now you can just manually add those (which is what I did to unblock the roll).

(Longer term we need to fix that issue, and/or change the output names of these Pigeon files.)

@Haidar0096
Copy link
Contributor Author

@stuartmorgan-g ohh I thought claude was hallucinating when it told me the flutter team added them manually😂😂

Thank you for the guidance, will supress them manually then and update the PR🙏🏻

And a suggestion, if this takes some time to fix (the pigeon issue), maybe mention this as a note in the pigeon section so new comers like me know that they are not doing anything wrong in their setup but rather they should supress such errors (to be specified) manually for now.

@Haidar0096 Haidar0096 force-pushed the add-storekit2-transaction-unfinished-and-appaccounttoken branch from 1c0aecc to f8257a4 Compare November 18, 2025 16:58
@stuartmorgan-g
Copy link
Collaborator

stuartmorgan-g commented Nov 18, 2025

And a suggestion, if this takes some time to fix (the pigeon issue), maybe mention this as a note in the pigeon section so new comers like me know that they are not doing anything wrong in their setup but rather they should supress such errors (to be specified) manually for now.

Having to make manual changes is extremely rare, and my plan was to resolve this case ASAP; it confuses everyone (not just newcomers). I wouldn't have done it at all except that it was blocking an important change in flutter/flutter for complex reasons.

Yesterday I thought this was a quick Pigeon fix that I would do today, but having investigated the cause more I'll probably change the Swift filenames for this package today as the short-term fix.

@Haidar0096
Copy link
Contributor Author

The remaining failing check as per logs is due to:

[!] CDN: trunk URL couldn't be downloaded: https://cdn.cocoapods.org/deprecated_podspecs.txt Response: 500 <!DOCTYPE html>

I don't have an option to rerun the check to try it again.

@stuartmorgan-g
Copy link
Collaborator

'll probably change the Swift filenames for this package today as the short-term fix.

This landed as #10465, so the manual change is no longer necessary.

@Haidar0096 Haidar0096 force-pushed the add-storekit2-transaction-unfinished-and-appaccounttoken branch from f8257a4 to 6445537 Compare November 19, 2025 13:36
@Haidar0096 Haidar0096 requested a review from LouiseHsu November 19, 2025 17:13
Copy link
Contributor

@LouiseHsu LouiseHsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!!!

@LouiseHsu LouiseHsu added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 21, 2025
@auto-submit
Copy link
Contributor

auto-submit bot commented Nov 21, 2025

autosubmit label was removed for flutter/packages/10439, because This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.

  • Merge guidelines: A PR needs at least one approved review if the author is already part of flutter-hackers or two member reviews if the author is not a flutter-hacker before re-applying the autosubmit label. Reviewers: If you left a comment approving, please use the "approve" review action instead.

@auto-submit auto-submit bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 21, 2025
@stuartmorgan-g stuartmorgan-g added the triage-ios Should be looked at in iOS triage label Nov 24, 2025
@LongCatIsLooong
Copy link
Contributor

(sorry for the delay, taking a look tomorrow)

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nits

Comment on lines 239 to 250
@MainActor in
do {
let transactionsMsgs = await rawUnfinishedTransactions().map {
$0.convertToPigeon(receipt: nil)
}
completion(.success(transactionsMsgs))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while since I last used swift, but I have the same question: why the do block?

);
});

group('appAccountToken exposure', () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 tests don't seem to be super valuable, would it be possible to test appAccountToken the same way receiptData was tested?

Copy link
Contributor Author

@Haidar0096 Haidar0096 Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LongCatIsLooong
Thanks for the comments :)

  • I was trying to keep consistent code with the transactions method (which also contains a questionable do block, as I understood from its code). I removed the do block anyway for the method I added.

  • I also removed the 2 tests and added a check for the appAccountToken in the existing test that tests the receipt.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regarding the tests, would it be possible to add appAccountToken to the API fake in fake_storekit_platform.dart and verify the dart API caller would receive the correct token?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LongCatIsLooong kindly check if the test I added makes the PR good to go, please let me know if I need to make any other changes

…aseDetails

This PR adds two new features to in_app_purchase_storekit for StoreKit 2:

1. SK2Transaction.unfinishedTransactions() - Queries only unfinished transactions,
   mirroring Apple's Transaction.unfinished API for better performance.

2. SK2PurchaseDetails.appAccountToken - Exposes the UUID that associates
   transactions with users in custom backend systems.

Both features are additive and maintain full backward compatibility.

Tests included for both features.
Previously returned nil for receipt data, now properly includes
jwsRepresentation for server-side verification.
@Haidar0096 Haidar0096 force-pushed the add-storekit2-transaction-unfinished-and-appaccounttoken branch from 6445537 to bacf772 Compare November 27, 2025 08:17
@LongCatIsLooong
Copy link
Contributor

@Haidar0096 it looks like there's still one unresolved comment, but IMO the PR is probably good to merge as is. Would you like to address the comment? If not I can add this to the autosubmit queue.

@Haidar0096
Copy link
Contributor Author

@LongCatIsLooong Sure I will address it, I will work on it on this weekend, thank you so much for the guidance🙏🏻

@LouiseHsu
Copy link
Contributor

is this ready to merge? :)

@Haidar0096
Copy link
Contributor Author

@LouiseHsu I was asked to add a test and I added it and waiting for the new review

@LouiseHsu LouiseHsu added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 11, 2025
@auto-submit auto-submit bot merged commit 0ac7a03 into flutter:main Dec 11, 2025
80 checks passed
@Haidar0096 Haidar0096 deleted the add-storekit2-transaction-unfinished-and-appaccounttoken branch December 12, 2025 08:59
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 12, 2025
github-merge-queue bot pushed a commit to flutter/flutter that referenced this pull request Dec 12, 2025
flutter/packages@36383d6...0ac7a03

2025-12-11 49842031+Haidar0096@users.noreply.github.com
[in_app_purchase_storekit] Add Transaction.unfinished API and expose
appAccountToken (flutter/packages#10439)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
harryterkelsen added a commit to harryterkelsen/flutter that referenced this pull request Dec 19, 2025
Fix CPU rendering fallback

Respond to review comments

Remove unused imports from surface_test.dart

Don't double dispose of displayFactory

feat: Add bitmap-less rendering support to OffscreenCanvasRasterizer by introducing `rasterizeToCanvas` and `canvasImageSource` to `Surface` and `CkOffscreenSurface`.

Skip tests which use OffscreenSurfaceProvider in Safari

format

Use the base canvas to create Images in Skwasm

Fix analysis warnings. Don't render disposed pictures.

Add TODO comments

Add isDisposed to Picture

Fix up merge

Delete confusing comment

Enhance documentation for `LocalHistoryEntry` class (#179223)

Fixes : #11660

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

Roll Dart SDK from 019cb923bf62 to 17749965ec57 (5 revisions) (#179640)

https://dart.googlesource.com/sdk.git/+log/019cb923bf62..17749965ec57

2025-12-09 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-227.0.dev
2025-12-09 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-226.0.dev
2025-12-09 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-225.0.dev
2025-12-09 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-224.0.dev
2025-12-09 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-223.0.dev

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/dart-sdk-flutter
Please CC dart-vm-team@google.com,jimgraham@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Skia from f9e32c28c5c5 to 82fff05cc621 (2 revisions) (#179641)

https://skia.googlesource.com/skia.git/+log/f9e32c28c5c5..82fff05cc621

2025-12-09
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2025-12-09 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from 4edb81c5d7c8 to 3486579f434a (4 revisions)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Make sure that a CupertinoPageScaffold doesn't crash in 0x0 environment (#179245)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the
CupertinoPageScaffold widget.

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

Make sure that a CupertinoSlider doesn't crash in 0x0 environment (#179566)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the CupertinoSlider
widget.

Make sure that a CupertinoSegmentedControl doesn't crash in 0x0 envir… (#179544)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the
CupertinoSegmentedControl widget.

Make sure that a Tooltip doesn't crash in 0x0 environment (#178461)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the Tooltip widget.

---------

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

Make sure that a CupertinoMagnifier doesn't crash in 0x0 environment (#179206)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the
CupertinoMagnifier widget.

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

Make sure that a CupertinoFormSection doesn't crash in 0x0 environment (#179001)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the
CupertinoFormSection widget.

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

Make sure that a CupertinoListSection doesn't crash in 0x0 environment (#179068)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the
CupertinoListSection widget.

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

Make SettingsChannel configuration queue not static (#179636)

SettingsChannel has a static data structure that stores system
configurations. This is fine if there is one engine. If there are two
FlutterViews and two engines - this static configuration is shared
between the two different engines. Because of the way the queue deletes
old entries this will result in one engine missing it's configuration.

Fixes: #179559

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [X] I signed the [CLA].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [X] I updated/added relevant documentation (doc comments with `///`).
- [X] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [X] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [X] All existing and new tests are passing.

Make sure that a CupertinoDialogAction doesn't crash in 0x0 environment (#178956)

This is my attempt to handle
https://github.com/flutter/flutter/issues/6537 for the
CupertinoDialogAction widget.

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>

Roll Skia from 82fff05cc621 to e61cc6d073fd (4 revisions) (#179646)

https://skia.googlesource.com/skia.git/+log/82fff05cc621..e61cc6d073fd

2025-12-09
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2025-12-09
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2025-12-09 vigneshv@google.com SkCrabbyAvifCodec: Disable sample
transforms
2025-12-09 bungeman@google.com Roll FreeType from 1518bc83 to 30e45abe
(119 commits)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Implement flutter/accessibility channel (#179484)

Fixes https://github.com/flutter/flutter/issues/172023

Roll Fuchsia Linux SDK from _pSztGZvEA3-Ry-GW... to u5vxWTRT0HlxOP5_r... (#179652)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter
Please CC jimgraham@google.com,zra@google.com on the revert to ensure
that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Check for a null cached image in SingleFrameCodec::getNextFrame (#179483)

This could happen if the image decoder invoked its callback with a null
image to signal an error. The SingleFrameCodec's status will be
kComplete but its cached image will not be set.

Fixes https://github.com/flutter/flutter/issues/161031

Marks Mac_ios draw_arcs_all_stroke_styles_perf_ios__timeline_summary to be unflaky (#179669)

<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
{
"name": "Mac_ios draw_arcs_all_stroke_styles_perf_ios__timeline_summary"
}
-->
The test has been passing for [50 consecutive
runs](https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:%22Mac_ios%20draw_arcs_all_stroke_styles_perf_ios__timeline_summary%22).
This test can be marked as unflaky.

Roll Packages from 338ecd3864b8 to 74a5a5314f5c (4 revisions) (#179693)

https://github.com/flutter/packages/compare/338ecd3864b8...74a5a5314f5c

2025-12-10 47866232+chunhtai@users.noreply.github.com [ci] Rewrites
branch exists without external dependencies (flutter/packages#10594)
2025-12-10 61003485+BottlePumpkin@users.noreply.github.com [rfw] Add
Flexible widget support to core widgets (flutter/packages#9750)
2025-12-09 matt.boetger@gmail.com Redistribute package ownership among
Android team (flutter/packages#10569)
2025-12-09 10687576+bparrishMines@users.noreply.github.com
[camera_android_camerax] Updates pigeon generation to prevent crash when
objects call to Dart after a hot restart (flutter/packages#10571)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Manually roll characters (#179447)

Flutter pub roller does not update characters to 1.4.1.

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Roll Dart SDK from 17749965ec57 to 077062c5e515 (3 revisions) (#179691)

https://dart.googlesource.com/sdk.git/+log/17749965ec57..077062c5e515

2025-12-10 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-230.0.dev
2025-12-10 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-229.0.dev
2025-12-10 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-228.0.dev

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/dart-sdk-flutter
Please CC dart-vm-team@google.com,jimgraham@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Fixed RenderFlex overflow in RouteObserver Example (#170980)

Fixed RenderOverflow in RouteObserver Example:
https://api.flutter.dev/flutter/widgets/RouteObserver-class.html

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

This PR is about solving render overflow issue in Route Observer Example
in Live Website. I've wrapped the body with a Scrollable View to solve
this issue

| | |
| --- | --- |
| B |
![image](https://github.com/user-attachments/assets/c8fa78e7-62ca-4bd1-9798-588c636cd61a)
|
| A |
![image](https://github.com/user-attachments/assets/3a2b4697-121a-43e3-9c85-b1379a575af1)
|

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Victor Sanni <victorsanniay@gmail.com>

added onUserInteractionIfError for form (#175515)

This PR adds a new `AutovalidateMode` value:
`onUserInteractionIfError`.

This mode allows `Form` and `FormField` widgets to auto-validate **only
when a field already has an error and the user interacts with it
again**.

- Current modes (`disabled`, `always`, `onUserInteraction`, `onUnfocus`)
do not cover the case where developers want validation **only when
correcting an error**.
- This improves UX by reducing unnecessary validation calls while still
ensuring errors disappear as soon as the user fixes them.

- Added `onUserInteractionIfError` to `AutovalidateMode`.
- Updated Dartdoc with detailed description and example snippet.
- Added new unit tests to validate behavior.
- Verified no regressions in other modes (`always`, `onUnfocus`,
`onUserInteraction`).

```dart
TextFormField(
  autovalidateMode: AutovalidateMode.onUserInteractionIfError,
  validator: (value) => value!.isEmpty ? 'Required field' : null,
)
````

---

Fixes #\<INSERT\_RELATED\_ISSUE\_NUMBER\_IF\_ANY>

---

* Added widget tests to confirm that:

  * No validation occurs until a field has an error.
* Once an error exists, validation runs automatically on user
interaction.
  * Resetting the form clears errors correctly.
* Regression tests ensure that `always`, `onUserInteraction`, and
`onUnfocus` modes still behave correctly.

---

* [x] I have read the [[Contributor
Guide](https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md)](https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md)
* [x] I have read the \[Tree Hygiene] wiki page and followed its
guidance
* [x] I have read and followed the \[Flutter Style Guide]
* [x] I signed the \[CLA]
* [x] I added a new enum value with clear Dartdoc
(`onUserInteractionIfError`)
* [x] I added example code in the Dartdoc
* [x] I added new widget/unit tests for the new mode
* [x] All existing and new tests pass
* [x] I ran `flutter analyze` and fixed all issues
* [x] No breaking changes introduced (or marked accordingly)

---

* [x] Yes
* [x] No

---

*(N/A — logic-only change)*

---

* The new mode reduces unnecessary validation until the user corrects an
error.
* All tests confirm correct behavior and no regressions in existing
modes.

---------

Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>

Fix the issue with pinned headers in nested SliverMainAxisGroup. (#179132)

Fixes: #178973

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Ensure that the engine converts std::filesystem::path objects to UTF-8 strings on Windows (#179528)

Fixes https://github.com/flutter/flutter/issues/178896

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

Reland: Add framework-side hitTestBehavior support to Semantics (#178817) (#179352)

This is a reland of #178817, which was reverted in #179100 due to test
failures.

The original PR introduced `hitTestBehavior` to the semantics framework
but incorrectly applied `opaque` behavior to `ModalRoute`, which blocked
platform views from receiving pointer events.

Instead of making the entire modal opaque, we:
1. Keep `ModalRoute` without explicit `hitTestBehavior` (defaults to
`defer`)
2. Make only the dialog/sheet content opaque (blocks clicks to barrier)
3. Platform views remain clickable because they're outside the opaque
content boundary

Fixes #149001
Original PR: #177570
Revert: #178744

Roll Skia from e61cc6d073fd to 59c6cad539f7 (22 revisions) (#179714)

https://skia.googlesource.com/skia.git/+log/e61cc6d073fd..59c6cad539f7

2025-12-10 bungeman@google.com Modernize
SkFontMgr_Mac::onMatchFamilyStyleCharacter
2025-12-10 kylechar@google.com Upgrade gradle to version 9.1.0
2025-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from 9f7342ee3761 to 50c5f8b4dd57 (8 revisions)
2025-12-10 bungeman@google.com Revert "Pass first family name to
paragraph fallback"
2025-12-10 kjlubick@google.com Revert "[rust bmp] Add initial Rust BMP
decoder implementation"
2025-12-10 bungeman@google.com Roll HarfBuzz from 08b52ae2 to 31695252
(971 commits)
2025-12-10
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2025-12-10 sergiog@microsoft.com [rust bmp] Add initial Rust BMP decoder
implementation
2025-12-10 bungeman@google.com Pass first family name to paragraph
fallback
2025-12-10 kjlubick@google.com Update CanvasKit to use const version of
SerialProcs
2025-12-10 kjlubick@google.com Remove legacy SkParagraphBuilder API
2025-12-10 michaelludwig@google.com [wgsl] Stop forcing all function
calls into scratch lets
2025-12-10 michaelludwig@google.com [ganesh] Guard division by zero in
AAConvexPathRenderer
2025-12-10 nicolettep@google.com [graphite] Use depth/stencil image
aspects for default d/s attachments
2025-12-10 nscobie@google.com Make SkDebugf calls log at Android's WARN
level in RenderEngine
2025-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Manual roll
Dawn from 57fca18a1603 to 9e1721615b33 (7 revisions)
2025-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from 3486579f434a to 9f7342ee3761 (5 revisions)
2025-12-10
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).
2025-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE
from 78bb5a33a9e9 to c2a048f9a1db (7 revisions)
2025-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Skia
Infra from ca3eb8741f18 to 492dfbfe3bb2 (10 revisions)
2025-12-10 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn
from 8de9b80c5bd9 to 57fca18a1603 (17 revisions)
2025-12-10
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Use kPreventOverdraw for arcs with overlapping stroke caps (#179312)

Fixes #178746

Before:
<img width="341" height="527" alt="image"
src="https://github.com/user-attachments/assets/b2458490-109f-4e6f-b35d-3bc0e5648de6"
/>

After:
<img width="295" height="566" alt="image"
src="https://github.com/user-attachments/assets/f5958934-dae2-4181-b142-9c68f8cdc2e8"
/>

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Roll Dart SDK from 077062c5e515 to dbcb567e2432 (2 revisions) (#179715)

https://dart.googlesource.com/sdk.git/+log/077062c5e515..dbcb567e2432

2025-12-11 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-232.0.dev
2025-12-10 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-231.0.dev

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/dart-sdk-flutter
Please CC dart-vm-team@google.com,jimgraham@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Skia from 59c6cad539f7 to deb0153719dd (2 revisions) (#179721)

https://skia.googlesource.com/skia.git/+log/59c6cad539f7..deb0153719dd

2025-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn
from 9e1721615b33 to f39150de4906 (8 revisions)
2025-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Skia
Infra from 492dfbfe3bb2 to 6cd15cab2194 (10 revisions)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Fuchsia Linux SDK from u5vxWTRT0HlxOP5_r... to QssSL8DkxIbMvf89C... (#179729)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter
Please CC jimgraham@google.com,zra@google.com on the revert to ensure
that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Dart SDK from dbcb567e2432 to 3d4b989bdc41 (1 revision) (#179733)

https://dart.googlesource.com/sdk.git/+log/dbcb567e2432..3d4b989bdc41

2025-12-11 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-233.0.dev

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/dart-sdk-flutter
Please CC dart-vm-team@google.com,jimgraham@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Skia from deb0153719dd to d570e2317110 (2 revisions) (#179734)

https://skia.googlesource.com/skia.git/+log/deb0153719dd..d570e2317110

2025-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from 50c5f8b4dd57 to f23836d62fa0 (5 revisions)
2025-12-11 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE
from c2a048f9a1db to 92d454d83328 (10 revisions)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

[skia] Disable legacy image decoders (#179277)

After
https://github.com/flutter/flutter/commit/8666e1af8ffa7e8cb0b9a7b6bb5bdd19afeabc49
Flutter shouldn't be
reliant on Skia's legacy image codec registration. This will enforce
that.

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Revert "Android implementation of content sizing" (#179698)

Reverts flutter/flutter#176063

Fixes: #179667

Git bisect:
```
❯ git bisect log
git bisect start 'master' 'flutter-3.38-candidate.0'
git bisect good df87ee3db00df61d882f99e655a3dc5f8387f806
git bisect good 951b25d2400a298f77a10e6a790905d045f62ddf
git bisect good 337bbfbe042a89c0a887c895fa3aa364decc7077
git bisect good 8e952d62976dfe77e54849e9e110e5ebb7ef7f71
git bisect good 723fec502fd9788c94fb039464ab9418b3ca9a3e
git bisect bad 96c65251a604a3960a9186b3011a70ee6883148f
git bisect good 4000ce2b4e576e356ee0f8a3841bbb5e280cac2c
git bisect good 26f311ca5e8519f31b10a2fca6f10b6eaa028e04
git bisect bad 8cc9feed310df448d80798bfc710e950cdbba4a8
git bisect bad b37b6fa92736083e8c24a56da8b4b3d326df1c31
git bisect bad f197a3a00ca9483435b8c04bb972fbcfa7737f25
```

Roll Packages from 74a5a5314f5c to 36383d6b0cd4 (4 revisions) (#179740)

https://github.com/flutter/packages/compare/74a5a5314f5c...36383d6b0cd4

2025-12-10 engine-flutter-autoroll@skia.org Manual roll Flutter from
6a1f5b7f85a4 to e25d71b086d6 (30 revisions) (flutter/packages#10601)
2025-12-10 arin.abdul99@gmail.com [go_router] Fixes an issue where
`onEnter` blocking causes navigation stack loss (stale state
restoration) (flutter/packages#10476)
2025-12-10 stuartmorgan@google.com [file_selector] Update Linux example
for deprecations (flutter/packages#10542)
2025-12-10 engine-flutter-autoroll@skia.org Manual roll Flutter from
b2de367c41e0 to 6a1f5b7f85a4 (22 revisions) (flutter/packages#10593)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

chore: linux fuchsia tests are flaking (#179744)

Roll Skia from d570e2317110 to 979b7ae09145 (4 revisions) (#179742)

https://skia.googlesource.com/skia.git/+log/d570e2317110..979b7ae09145

2025-12-11 kjlubick@google.com Add ZoomInSlide for investigating
aliasing bugs
2025-12-11 kjlubick@google.com Add missing bazel dep to fontmgr_coretext
2025-12-11 mike@reedtribe.org Refactor gradients API
2025-12-11 sergiog@microsoft.com Reland "[rust bmp] Add initial Rust BMP
decoder implementation"

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

[ Analysis ] Added initial implementation of the `flutter_analyzer_plugin` (#175679)

The `flutter_analyzer_plugin` implements rules previously enforced by
the `dev/bots/analyze.dart` check run on the CI, allowing for earlier
detection of custom lint violations before a change is uploaded for
review.

Currently, the plugin implements the following rules:

  - avoid_future_catch_error
  - no_double_clamp
  - no_stopwatches
  - protect_public_state_subtypes
  - render_box_intrinsics

Towards https://github.com/flutter/flutter/issues/175276

fix: fresh install of windows support (#179720)

- python 3.14 (pipes -> shlex)
- content_aware_hash.ps1 call from gn
- visual studio 2026 (MS changed directory naming)

Roll Dart SDK from 3d4b989bdc41 to 9a65db770758 (2 revisions) (#179746)

https://dart.googlesource.com/sdk.git/+log/3d4b989bdc41..9a65db770758

2025-12-11 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-235.0.dev
2025-12-11 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.11.0-234.0.dev

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/dart-sdk-flutter
Please CC dart-vm-team@google.com,jimgraham@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Add 3.38.5 changelog (#179750)

Reverts "[ Analysis ] Added initial implementation of the `flutter_analyzer_plugin` (#175679)" (#179766)

<!-- start_original_pr_link -->
Reverts: flutter/flutter#175679
<!-- end_original_pr_link -->
<!-- start_initiating_author -->
Initiated by: jtmcdole
<!-- end_initiating_author -->
<!-- start_revert_reason -->
Reason for reverting: Linux linux_unopt is now timing out in postsubmit
<!-- end_revert_reason -->
<!-- start_original_pr_author -->
Original PR Author: bkonyi
<!-- end_original_pr_author -->

<!-- start_reviewers -->
Reviewed By: {srawlins}
<!-- end_reviewers -->

<!-- start_revert_body -->
This change reverts the following previous change:
The `flutter_analyzer_plugin` implements rules previously enforced by
the `dev/bots/analyze.dart` check run on the CI, allowing for earlier
detection of custom lint violations before a change is uploaded for
review.

Currently, the plugin implements the following rules:

  - avoid_future_catch_error
  - no_double_clamp
  - no_stopwatches
  - protect_public_state_subtypes
  - render_box_intrinsics

Towards https://github.com/flutter/flutter/issues/175276
<!-- end_revert_body -->

Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>

[ Tool ] Assemble: "Improper" validation error when dealing with base64 encoded dart-define value  (#178737)

addressing a regression from #172510 that caused the assemble command to
fail in the validation phase when a dart-define was passed as a
base64-encoded value.

Example command:
```
flutter --verbose assemble --no-version-check \
  --output=<build_output_path>/ \
  -dTargetPlatform=ios \
  -dTargetFile=lib/main.dart \
  -dBuildMode=release \
  -dConfiguration=Debug \
  -dIosArchs=arm64 \
  -dSdkRoot=<xcode_sdk_path>/iPhoneOS.sdk \
  -dSplitDebugInfo= \
  -dTreeShakeIcons=false \
  -dTrackWidgetCreation=true \
  -dDartObfuscation=false \
  -dAction=install \
  -dFrontendServerStarterPath= \
  --ExtraGenSnapshotOptions= \
  --DartDefines=ZW52PXFh \
  --ExtraFrontEndOptions= \
  -dSrcRoot=<project_root> \
  -dTargetDeviceOSVersion= \
  -dCodesignIdentity=<codesign_identity> \
  release_ios_bundle_flutter_assets
 ```

Partially addressing: https://github.com/flutter/flutter/issues/178452

- [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is [test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Ben Konyi <bkonyi@google.com>

Exit with code 1 when calling `flutter build` without arguments (#175900)

Resolves #5797

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

A whole bunch of tool tests failed locally even before the change, so
I'll have to see CI results to know if something actually broke due to
the change.

Roll Skia from 979b7ae09145 to 7ad91f64b468 (6 revisions) (#179770)

https://skia.googlesource.com/skia.git/+log/979b7ae09145..7ad91f64b468

2025-12-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll
vulkan-deps from f23836d62fa0 to 63f49739609f (3 revisions)
2025-12-11 kjlubick@google.com Mac ld doesn't support
--allow-multiple-definition
2025-12-11 kjlubick@google.com Delete unused makeIndexWriter functions
2025-12-11 kjlubick@google.com Mark other makeVertexWriter checks as
unlikely/likely
2025-12-11 kjlubick@google.com Check vertex allocation in
PathStencilCoverOp
2025-12-11 michaelludwig@google.com [graphite] RendererProvider selects
path strategy

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

[fuchsia] temporarily disable touch-input-test due to the flakiness (#179747)

The FlutterEmbedTapTest.* are running on fuchsia_debug_x64_tester, and
they are recently flaky; FlutterTapTest is less but still flaky.

Before finding out the root cause, disabling the test suite is better
than disabling the entire builder.

https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20linux_fuchsia_tests

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Roll Skia from 7ad91f64b468 to 344ff9c41fb6 (1 revision) (#179774)

https://skia.googlesource.com/skia.git/+log/7ad91f64b468..344ff9c41fb6

2025-12-12 michaelludwig@google.com [wgsl] check binary component matrix
polyfill once

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

[ios][pv] accept/reject gesture based on hitTest (with new widget API) (#179659)

This is a follow up PR to [this original
PR](https://github.com/flutter/flutter/pull/177859).

The difference is the API - the original PR chooses Option 1 [in the
design
doc](https://docs.google.com/document/d/1ag4drAdJsR7y-rQZkqJWc6tOQ4qCbflQSGyoxsSC6MM/edit?tab=t.0),
while this PR chooses Option 3.

To directly use flutter API, just pass in the policy when creating
UiKitView widget.

```
UiKitView(
  ...
  gestureBlockingPolicy: UiKitViewGestureBlockingPolicy)
  ...
)
```

For plugins, we need to update plugins to use this new API.

```
WebView(
  ...
  gestureBlockingPolicy: UiKitViewGestureBlockingPolicy
) {
  return UiKitView(
    ..
    gestureBlockingPolicy: gestureBlockingPolicy
  )
}
```
For more information, refer to [the old
PR](https://github.com/flutter/flutter/pull/177859).

*List which issues are fixed by this PR. You must list at least one
issue. An issue is not required if the PR fixes something trivial like a
typo.*

https://github.com/flutter/flutter/issues/175099
https://github.com/flutter/flutter/issues/165787

*If you had to change anything in the [flutter/tests] repo, include a
link to the migration guide as per the [breaking change policy].*

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Fix draggable scrollable sheet example drag speed is off  (#179179)

- Fix https://github.com/flutter/flutter/issues/179102
- In this PR:
- Fix the sheet position so that it will follow the pointer, even when
dragging mouse beyond the window then returns inside.
    - Some improvement for coding convention:
- declare minChildSize and maxChildSize const(s) then use them in the
hardcoded positions
- move `_isOnDesktopAndWeb` out of Grabber, so it's easier to see
Grabber widget purpose when reading the code

<details open>
<summary>Demo</summary>

| before | after |
| --------------- | --------------- |
<img width="360"
src="https://github.com/user-attachments/assets/5f4f507d-1491-47a2-90a6-c2a32ecdf6b5">
| <img width="360"
src="https://github.com/user-attachments/assets/b2eb9ede-572e-4fcb-81c1-2269b8db2140">

</details>

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Signed-off-by: huycozy <huy@nevercode.io>

Roll Skia from 344ff9c41fb6 to 4251aa7454da (2 revisions) (#179779)

https://skia.googlesource.com/skia.git/+log/344ff9c41fb6..4251aa7454da

2025-12-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn
from f39150de4906 to 83072ee716dc (25 revisions)
2025-12-12
recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com
Roll recipe dependencies (trivial).

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Skia from 4251aa7454da to ee08571bbf67 (1 revision) (#179783)

https://skia.googlesource.com/skia.git/+log/4251aa7454da..ee08571bbf67

2025-12-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE
from 92d454d83328 to fe3c8640fe8d (11 revisions)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Fuchsia Linux SDK from QssSL8DkxIbMvf89C... to fppT9ZrwbFx7iYrIh... (#179785)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter
Please CC jimgraham@google.com,zra@google.com on the revert to ensure
that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Packages from 36383d6b0cd4 to 0ac7a032dbcd (1 revision) (#179793)

https://github.com/flutter/packages/compare/36383d6b0cd4...0ac7a032dbcd

2025-12-11 49842031+Haidar0096@users.noreply.github.com
[in_app_purchase_storekit] Add Transaction.unfinished API and expose
appAccountToken (flutter/packages#10439)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Roll Skia from ee08571bbf67 to e66816c3645e (2 revisions) (#179792)

https://skia.googlesource.com/skia.git/+log/ee08571bbf67..e66816c3645e

2025-12-12 mike@reedtribe.org Stop using (deprecated) SkGradientShader
2025-12-12 michaelludwig@google.com [graphite] Fix vello build typo in
RendererProvider

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skia-flutter-autoroll
Please CC jimgraham@google.com,kjlubick@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry
To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Add documentation for what to do when bumping the minimum sdk version supported by flutter (#179795)

Fixes #170807

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

[win32] Do not flood message loop with wake up messages (#179538)

Fixes https://github.com/flutter/flutter/issues/173843

*If you had to change anything in the [flutter/tests] repo, include a
link to the migration guide as per the [breaking change policy].*

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/c…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App p: in_app_purchase platform-ios platform-macos triage-ios Should be looked at in iOS triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants