> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.qonversion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter SDK 7.x to 8.x migration guide

> We've upgraded the Google Play Billing Library dependency on Android to version 6 in this release, which has led to major changes in the product and subscription management parts of our SDK. These are described below.

## Upgrading version

Increase the dependency version in your *pubspec.yaml* file to upgrade your Qonversion SDK to the latest

<CodeGroup>
  ```yaml theme={null}
  dependencies:
    qonversion_flutter: ^8.0.0
  ```
</CodeGroup>

## Product updates

### Qonversion and Google Play product link changes

Google Play has recently upgraded the structure of their subscription products. We've written about it [in our blog](https://qonversion.io/blog/google-play-billing-library-5-0/). In short, they combined the same subscriptions with different durations into a single subscription with different base plans. In addition, they announced different kinds of offers to base plans, including free trials or introductory prices. In this release, we begin to support those changes.

Previously Qonversion **subscription** products required a Google Play subscription identifier. Now you can also set a specific **base plan** for the subscription. Different base plans of the same subscription can be linked to different Qonversion products. Let's have a look at an example.

Let's say you sell monthly and annual subscriptions in your app, both on Android and iOS. With the previous Qonversion SDK versions, you would specify two Qonversion products (`premium_monthly` and `premium_annual`), two App Store products (`premium_monthly_ios` and `premium_annual_ios`), and two Google Play products (`premium_monthly_android` and `premium_annual_android`). Then you would link those store products to Qonversion products as follows:

`premium_monthly` - `premium_monthly_ios` and `premium_monthly_android`,

`premium_annual` - `premium_annual_ios` and `premium_annual_android`.

With the new Google Play Subscriptions structure, you can create only one subscription for Android - `premium_android` and two base plans for it (`monthly` and `annual`). With this done, you can now use a single subscription for both Qonversion products as follows:

`premium_monthly` - `premium_monthly_ios` and `premium_android` with `monthly` base plan,

`premium_annual` - `premium_annual_ios` and `premium_android` with `annual` base plan.

### Store details for the `QProduct` class

Speaking of the SDK side - the first big part of the changes is the addition of the new `basePlanId` field to `QProduct` which can be set to the product via Qonversion Dashboard.

We have deprecated the `QProduct.skuDetails` field, because `SkuDetails` contain information only for backward compatible base plans and are now deprecated in the Google Play Billing Library. Instead, we have added the new `storeDetails` field of type `QProductStoreDetails`, which contains all the store information for the Google Play Product. You can find the specifications of that class [on this page](google-play-product-details).

If `basePlanId` is not specified for a product, it means one of the following:

* it is a one-time (in-app) product that can not have any base plans,
* it is a product that was not updated in the Qonversion dashboard after this version was released,
* it is an iOS-only product.

In the case of the in-app product, both `skuDetails` and `storeDetails` fields will contain information about that in-app. But in the second case, when there is a subscription product without a specified base plan, the `skuDetails` field will contain information about the **backward compatible** base plan, while the `storeDetails` field will only contain common subscription information without base-plan-specific data. It means that you should start using `storeDetails` field for base-plan-specific data (like price, duration, and so on) for Android subscription products only if you've specified their base plan IDs in Qonversion product settings.

### The other `QProduct` fields changes

Along with the above changes, we have changed the `type` field and removed the `duration` and `trialDuration` fields from the product class. The `type` and `duration` were previously being set via the dashboard, and the `trialDuration` was calculated from the store details and was represented as an enumeration with several common durations. Now, the product `type` is calculated from the store details both for Android and iOS. We've also added the `intro` value to `QProductType` to separate `trial` and `intro` products (Android only for now). An `unknown` value was also added for the cases where we are unable to determine the product type. The `duration` and `trialDuration` fields were replaced with `subscriptionPeriod` and `trialPeriod`. Both properties are of type `QSubscriptionPeriod` and the values are also calculated from the store details for both platforms.

The last thing to note is that we've also changed the `prettyPrice` and `prettyIntrodactoryPrice` fields calculation for Android. Earlier they were calculated from `skuDetails`, while now we first try to use `storeDetails` if possible and only then fall back to `skuDetails`.

### `QProduct` updates summary

Below is the shortened summary of the `QProduct` changes:

* new `basePlanId` field, specifying concrete subscription base plan for Google Play subscriptions, added,
* `skuDetails` field deprecated,
* new `storeDetails` field, containing information about Google Play product, added;
* the `type` field is now calculated based on store details both for Android and iOS, instead of the value set via the Qonversion dashboard. The `intro` and `unknown` values were added to `QProductType` to separate cases of trial and intro products (Android only for now) and also the case when we are unable to determine the product type;
* the `duration` and `trialDuration` fields were replaced with `subscriptionPeriod` and `trialPeriod` fields of type `QSubscriptionPeriod` with the values calculated from the store details both for Android and iOS;
* the `prettyIntroductoryPrice` field on Android now prefers `storeDetails` to get the price from, and only then - `skuDetails`.

## Purchase flow updates

In this release, we have changed the `purchase` and `updatePurchase` methods signatures. Previously there were several methods for different sets of arguments. Now we've arranged all those properties into classes - `QPurchaseModel` and `QPurchaseUpdateModel`, that should be instantiated and provided to the single `purchase` or `updatePurchase` method.

You can create instances of those classes either manually or by using the utility functions `toPurchaseModel` and `toPurchaseUpdateModel` added to the `QProduct` class.

Below is an example of how the `purchase` flow has changed:

<CodeGroup>
  ```dart Flutter theme={null}
  // Old
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchaseProduct(product);

  // New
  final QPurchaseModel purchaseModel = product.toPurchaseModel();
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
  ```
</CodeGroup>

And an `updatePurchase` flow example:

<CodeGroup>
  ```dart Flutter theme={null}
  // Old
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchaseWithProduct(
    newProduct: product,
    oldProductId: 'oldProductId',
  );

  // New
  final QPurchaseUpdateModel purchaseUpdateModel = product.toPurchaseUpdateModel('oldProductId');
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);
  ```
</CodeGroup>

If you were using only product identifiers, you can create purchase models manually as follows:

<CodeGroup>
  ```dart Flutter theme={null}
  // Old
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase('productId');

  // New
  final QPurchaseModel purchaseModel = new QPurchaseModel('productId');
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
  ```
</CodeGroup>

<CodeGroup>
  ```dart Flutter theme={null}
  // Old
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(
    newProductId: 'newId',
    oldProductId: 'oldId',
  );

  // New
  final QPurchaseUpdateModel purchaseUpdateModel = new QPurchaseUpdateModel('newProductId', 'oldProductId');
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);
  ```
</CodeGroup>

If necessary, you can provide a specific offer for subscription purchase.

<CodeGroup>
  ```dart Flutter theme={null}
  // Specifying offer via the `toPurchaseModel` method:
  final QProductOfferDetails productOfferDetails = ...; // Choose an offer from `storeDetails`
  final QPurchaseModel purchaseModel = product.toPurchaseModelWithOffer(productOfferDetails);

  // Specifying offer ID via the `toPurchaseModel` method:
  final QPurchaseModel purchaseModel = product.toPurchaseModel('offer_id');

  // Specifying offer ID via the constructor:
  final QPurchaseModel purchaseModel = new QPurchaseModel('productId', 'offer_id');
  final QPurchaseUpdateModel purchaseUpdateModel = new QPurchaseUpdateModel('newProductId', 'oldProductId', 'offer_id');

  // Specifying offer ID after the purchase model creation:
  purchaseModel.offerId = 'offer_id';
  ```
</CodeGroup>

If provided, we will try to find and purchase the offer with the specified ID for the requested Qonversion product. If there is no offer with the specified ID, an error will be returned. If no offer ID is provided for the subscription purchase of Qonversion product with a specified base plan ID, then we will choose the most profitable offer for the client from all the available offers. We calculate the cheapest price for the client by comparing all the trial or intro phases and the base plan. For iOS, old Qonversion products (where the base plan ID is not specified), or in-app products, the offer ID is ignored.

You can also remove any intro/trial offer from the purchase (use only a bare base plan). For that purpose, you should call `removeOffer` method of purchase model:

<CodeGroup>
  ```dart Flutter theme={null}
  purchaseModel.removeOffer()
  ```
</CodeGroup>

## The other changes

* Gradle on Android was upgraded to the version 8;
* The `checkTrialIntroEligibility` method improved and now detects the eligibility based on store details on Android;
* The `QProductDuration` and `QTrialDuration` classes were removed;
* The `QProrationMode` enum was removed and replaced with `QPurchaseUpdatePolicy` ([read more](making-purchases)).

***

[Android SDK 6.x to 7.x migration guide](android-7-migration-guide)

[React Native SDK 6.x to 7.x migration guide](react-native-7-migration-guide)
