Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,3 @@
# 3.0.0
Removes every deprecated Storefront API field the package still queried, migrating to the 2026-07 replacements. Because several of these change the shape of the public Dart models, this is a breaking release. **This version now requires Storefront API `2026-07` or newer** (it relies on `Cart.lines.discountAllocations(lineLevelOnly:)` and cart-level `delivery`, both added in 2026-07); the default `storefrontApiVersion` is already `2026-07`.

Breaking model changes:
* `ShopifyImage.originalSrc` → **`ShopifyImage.url`** (`Image.originalSrc`/`src` were deprecated in favour of `url`). Affects every image across products, collections, articles and media.
* `Option.values` (`List<String>`) → **`Option.optionValues`** (`List<ProductOptionValue>`, each with `id` + `name`). New exported model `ProductOptionValue`. Mirrors `ProductOption.values` → `optionValues` in the API.
* `Order.subtotalPriceV2`/`totalPriceV2`/`totalShippingPriceV2`/`totalTaxV2`/`totalRefundedV2` → **`subtotalPrice`/`totalPrice`/`totalShippingPrice`/`totalTax`/`totalRefunded`** (the `*V2` money fields were deprecated).
* `CartCost` lost `totalTaxAmount`, `totalTaxAmountEstimated`, `totalDutyAmount`, `totalDutyAmountEstimated`. Shopify no longer returns tax/duty amounts on the cart ("no longer available and will be removed in a future version"), so these always returned null/false.
* Cart delivery addresses moved off the buyer identity:
* Removed `CartBuyerIdentity.deliveryAddressPreferences` and `CartBuyerIdentityInput.deliveryAddressPreferences` (the input field was deprecated).
* `Cart` gains **`delivery`** (`CartDelivery` → `List<CartSelectableAddress>`, each exposing `id`, `selected`, `oneTimeUse`, and a `CartDeliveryAddress`). New exported models: `CartDelivery`, `CartSelectableAddress`, `CartDeliveryAddress`.
* `CartInput` gains **`delivery`** (`CartDeliveryInput`). New exported inputs: `CartDeliveryInput`, `CartSelectableAddressInput`, `CartAddressInput`, `CartDeliveryAddressInput`. These replace the removed `DeliveryAddressInput` (`MailingAddressInput` remains for other uses). Note the new delivery inputs take `countryCode`/`provinceCode` (e.g. `AU`/`NSW`) rather than full names.
* New `ShopifyCart.addDeliveryAddresses(cartId:, addresses:)` (mutation `cartDeliveryAddressesAdd`) to add delivery addresses to an existing cart; `updateBuyerIdentityInCart` no longer accepts addresses.
* Removed the `Market` model and its export, and the `market` field on `Localization` and `Country` (`Localization.market`/`Country.market` were deprecated with no Storefront replacement).

Other:
* Line-level `discountAllocations` now requests `lineLevelOnly: false` so order-level allocations are included, and the deprecated cart-level `Cart.discountAllocations` selection was dropped (use the per-line allocations).
* Internal query fields migrated with no public-API impact: `Image` `originalSrc`→`url` in all documents, `ProductVariant.priceV2`/`compareAtPriceV2`→`price`/`compareAtPrice`, and `productByHandle`/`blogByHandle`/`pageByHandle`→`product`/`blog`/`page`.
* Updated the default `storefrontApiVersion` from **2024-07** to **2026-07**. 2024-07 has been sunset since July 2025; Shopify serves requests for an unsupported version from the oldest supported version instead, so callers relying on the default were silently drifting across versions as each one sunset. Callers that already pass `storefrontApiVersion` explicitly should ensure it is `2026-07` or newer (see the requirement above).
* Fixed three GraphQL documents that were rejected by the Storefront API on every currently supported version:
* `getXCollectionsAndNProductsSorted` declared `$collectonMetafields` (typo) but used `$collectionMetafields`, so the server rejected the document with `Variable "$collectionMetafields" is not defined`. The Dart caller was already passing the correctly spelled variable, so this method could never have succeeded.
* `getNArticlesSorted` selected the removed `Article.url` field. Replaced with `onlineStoreUrl`, which is what the `Article` model already parses.
* `getCollectionByIdQuery` passed a list to the single-collection lookup (`collection(ids: $ids)`); corrected to `collection(id: $id)`. This query has no caller in the package.

Migration guide:
* Replace `image.originalSrc` with `image.url`.
* Replace `option.values` (strings) with `option.optionValues.map((v) => v.name)`.
* Replace `order.totalPriceV2` etc. with `order.totalPrice` (drop the `V2` suffix).
* Replace `CartBuyerIdentityInput(deliveryAddressPreferences: [...])` with either `CartInput(delivery: CartDeliveryInput(addresses: [...]))` on create, or `ShopifyCart.addDeliveryAddresses(...)` on an existing cart, and read addresses back from `cart.delivery`.
* Remove any use of `localization.market` / `country.market`.

# 2.8.3
* `cartDiscountCodesUpdate`: optional warnings (`CartWarningCode` `errorCode` + localized `message`) surfaced on `CartDiscountCode`. When a discount code is not applicable, the payload-level `warnings` (`errorCode`/`errorMessage`) are attached to the corresponding `CartDiscountCode` via `copyWith` after parsing. These fields are absent from the `discountCodes` JSON, so `fromJson` leaves them null.

# 2.8.2
* Fix Shopify-side validation error `Nullability mismatch on variable $discountCodes and argument discountCodes ([String!] / [String!]!)` from `ShopifyCart.updateCartDiscountCodes` against newer Storefront API versions (e.g. `2026-01`) that promoted the `cartDiscountCodesUpdate` argument from `[String!]` to `[String!]!`. The SDK's mutation document still declared the variable as the older nullable type, so the server rejected the request. Promoted the variable to `[String!]!`. Per the GraphQL spec ("All Variable Usages Are Allowed"), a non-null variable is also valid for the older nullable argument shape, so this change is forward- and backward-compatible across Storefront API versions; the Dart caller signature (`required List<String> discountCodes`) already enforces non-null.

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ void main() {
// Optional | Needed only if needed to call admin api
adminAccessToken: "shpat_*******************",

// optional | default: 2026-07
storefrontApiVersion: '2026-07',
// optional | default: 2024-07
storefrontApiVersion: '2024-07',

// optional | default: null
cachePolicy: CachePolicy.cacheAndNetwork,
Expand Down Expand Up @@ -55,7 +55,7 @@ void main() {
> `adminAccessToken` is only required for admin api calls like `deleteCustomer()`.
If you are not using that function, you may not need to provide it.

> `storefrontApiVersion` default vesion is set to '2026-07'. This package requires **2026-07 or newer** (the cart operations rely on fields added in 2026-07). Shopify supports each version for 12 months after release and then falls forward to the oldest supported version, so prefer keeping this current.
> `storefrontApiVersion` default vesion is set to '2024-07'

> `language` defaults to 'en'. It is the default locale/language of the store. Only takes effect if the store supports provided language code.

Expand Down Expand Up @@ -370,7 +370,7 @@ For more information about filters visit:

1. https://shopify.dev/docs/custom-storefronts/building-with-the-storefront-api/products-collections/filter-products#step-1-query-products

2. https://shopify.dev/docs/api/storefront/2026-07/input-objects/productfilter
2. https://shopify.dev/docs/api/storefront/2024-07/input-objects/productfilter

<hr>

Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/blog_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class ArtilePage extends StatelessWidget {
image: DecorationImage(
fit: BoxFit.contain,
image: NetworkImage(
article.image!.url,
article.image!.originalSrc,
))),
),
const SizedBox(height: 10),
Expand Down
63 changes: 27 additions & 36 deletions example/lib/screens/cart_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -480,29 +480,20 @@ class _BuyerIndetityState extends State<BuyerIndetity> {
email: randomEmail(),
phone: randomPhone(),
countryCode: buyerIndetity?.countryCode,
),
);
// Delivery addresses moved off the buyer identity in the 2026-07
// Storefront API. Add them to the cart directly instead.
await shopifyCart.addDeliveryAddresses(
cartId: cart.id,
addresses: [
const CartSelectableAddressInput(
selected: true,
address: CartAddressInput(
deliveryAddress: CartDeliveryAddressInput(
deliveryAddressPreferences: [
const DeliveryAddressInput(
deliveryAddress: MailingAddressInput(
address1: '11 Hinkler Avenue',
city: 'Sydney',
countryCode: 'AU',
provinceCode: 'NSW',
country: 'Australia',
firstName: 'Anderson',
lastName: 'Fetter',
phone: '044444444',
zip: '2229',
),
),
),
],
],
),
);
setState(() {
cart = updatedCart;
Expand Down Expand Up @@ -559,30 +550,30 @@ class _BuyerIndetityState extends State<BuyerIndetity> {
),
ExpansionTile(
initiallyExpanded: true,
title: const Text('Delivery Addresses'),
title: const Text('Delivery Address Preferences'),
children: [
if (cart.delivery?.addresses.isEmpty ?? true)
if (buyerIndetity?.deliveryAddressPreferences?.isEmpty ??
true)
const ListTile(
title: Text('No delivery addresses'),
title: Text('No delivery address preferences'),
),
...(buyerIndetity?.deliveryAddressPreferences ?? []).map(
(mailingAddress) => ListTile(
title: Text(
'${mailingAddress?.firstName} ${mailingAddress?.lastName}'),
subtitle: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${mailingAddress?.address1}',
),
Text(
'${mailingAddress?.city}, ${mailingAddress?.country}',
),
],
),
),
...(cart.delivery?.addresses ?? []).map(
(selectable) {
final address = selectable.address;
return ListTile(
title: Text(
'${address.firstName} ${address.lastName}'),
subtitle: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${address.address1}'),
Text(
'${address.city}, ${address.countryCode}',
),
],
),
);
},
),
],
),
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/collection_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class CollectionDetailScreenState extends State<CollectionDetailScreen> {
? ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.network(
product.images.first.url,
product.images.first.originalSrc,
width: 60,
height: 60,
fit: BoxFit.cover,
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/home_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class HomeTabState extends State<HomeTab> {
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
product.images.first.url,
product.images.first.originalSrc,
)))
: const BoxDecoration(),
child: Stack(
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/product_detail_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class ProductDetailScreenState extends State<ProductDetailScreen> {
children: <Widget>[
product.images.isNotEmpty
? Image.network(
product.images.first.url,
product.images.first.originalSrc,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height / 3,
fit: BoxFit.cover,
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/search_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class SearchTabState extends State<SearchTab> {
),
leading: product.images.isNotEmpty
? Image.network(
product.images.first.url,
product.images.first.originalSrc,
width: 60,
height: 60,
fit: BoxFit.cover,
Expand Down
24 changes: 12 additions & 12 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
Expand Down Expand Up @@ -620,26 +620,26 @@ packages:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
source: hosted
version: "0.12.19"
version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.13.0"
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.17.0"
nested:
dependency: transitive
description:
Expand Down Expand Up @@ -878,7 +878,7 @@ packages:
path: ".."
relative: true
source: path
version: "3.0.0"
version: "2.6.3"
sky_engine:
dependency: transitive
description: flutter
Expand Down Expand Up @@ -1000,10 +1000,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
version: "0.7.11"
version: "0.7.7"
typed_data:
dependency: transitive
description:
Expand Down Expand Up @@ -1277,5 +1277,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.10.0-0 <4.0.0"
dart: ">=3.9.0 <4.0.0"
flutter: ">=3.35.0"
Loading
Loading