From f4209a786b1eb83dfb026372532026aa3f3b5bfc Mon Sep 17 00:00:00 2001 From: Remon Date: Sun, 15 Dec 2024 18:19:19 +0000 Subject: [PATCH 1/7] feat: create android binding for addresssheet --- .../stripe/AddressSheetPlatformView.kt | 49 +++++++++++++++++++ .../StripeAddressSheetPlatformViewFactory.kt | 26 ++++++++++ .../com/flutter/stripe/StripeAndroidPlugin.kt | 6 +++ 3 files changed, 81 insertions(+) create mode 100644 packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt create mode 100644 packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt diff --git a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt new file mode 100644 index 000000000..0c1a13afe --- /dev/null +++ b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt @@ -0,0 +1,49 @@ +package com.flutter.stripe + +import android.content.Context +import android.view.View +import com.facebook.react.uimanager.ThemedReactContext +import com.reactnativestripesdk.StripeSdkModule +import com.reactnativestripesdk.addresssheet.AddressSheetView +import com.reactnativestripesdk.addresssheet.AddressSheetViewManager +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.platform.PlatformView + +class AddressSheetPlatformView( + private val context: Context, + private val channel: MethodChannel, + id: Int, + private val creationParams: Map?, + private val addressSheetManager: AddressSheetViewManager, + private val sdkAccessor: () -> StripeSdkModule + +) : PlatformView, MethodChannel.MethodCallHandler { + private val themedContext = ThemedReactContext(sdkAccessor().reactContext, channel, sdkAccessor) + + + lateinit var addressSheetView: AddressSheetView + + init { + + addressSheetView = addressSheetManager.createViewInstance(themedContext) + channel.setMethodCallHandler(this) + } + + override fun getView(): View? { + return addressSheetView + } + + override fun dispose() { + addressSheetManager.onDropViewInstance(addressSheetView) + } + + override fun onFlutterViewAttached(flutterView: View) { + addressSheetManager.onAfterUpdateTransaction(addressSheetView) + } + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + + } + +} \ No newline at end of file diff --git a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt new file mode 100644 index 000000000..1bbbe5a6c --- /dev/null +++ b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt @@ -0,0 +1,26 @@ +package com.flutter.stripe + +import android.content.Context +import com.reactnativestripesdk.StripeSdkModule +import com.reactnativestripesdk.addresssheet.AddressSheetViewManager +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.platform.PlatformView +import io.flutter.plugin.platform.PlatformViewFactory + +class StripeAddressSheetPlatformViewFactory( + private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, + private val addresSheetViewManager: AddressSheetViewManager, + private val sdkAccessor: () -> StripeSdkModule +) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { + + override fun create(context: Context?, viewId: Int, args: Any?): PlatformView { + val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter.stripe/address_sheet/${viewId}") + val creationParams = args as? Map? + if(context == null){ + throw AssertionError("Context is not allowed to be null when launching aubecs view.") + } + return AddressSheetPlatformView(context,channel,viewId, creationParams, addresSheetViewManager, sdkAccessor) + } +} \ No newline at end of file diff --git a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAndroidPlugin.kt b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAndroidPlugin.kt index 41a413ae4..85cb088fa 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAndroidPlugin.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAndroidPlugin.kt @@ -9,6 +9,7 @@ import com.facebook.react.uimanager.DisplayMetricsHolder import com.facebook.react.uimanager.ThemedReactContext import com.google.android.material.internal.ThemeEnforcement import com.reactnativestripesdk.* +import com.reactnativestripesdk.addresssheet.AddressSheetViewManager import com.reactnativestripesdk.pushprovisioning.AddToWalletButtonManager import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.plugins.FlutterPlugin @@ -49,6 +50,10 @@ class StripeAndroidPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { AuBECSDebitFormViewManager() } + private val addressSheetFormViewManager: AddressSheetViewManager by lazy { + AddressSheetViewManager() + } + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(flutterPluginBinding.applicationContext) @@ -69,6 +74,7 @@ class StripeAndroidPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { flutterPluginBinding .platformViewRegistry .registerViewFactory("flutter.stripe/add_to_wallet", StripeAddToWalletPlatformViewFactory(flutterPluginBinding, AddToWalletButtonManager(flutterPluginBinding.applicationContext)){stripeSdk}) + flutterPluginBinding.platformViewRegistry.registerViewFactory("flutter.stripe/address_sheet", StripeAddressSheetPlatformViewFactory(flutterPluginBinding, addressSheetFormViewManager ){stripeSdk}) } override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { From 3444cd81c75c7db06502e092b24bec0e02dff8a9 Mon Sep 17 00:00:00 2001 From: Remon Date: Sun, 15 Dec 2024 19:15:34 +0000 Subject: [PATCH 2/7] feat: create addressheet model --- .../stripe/lib/src/widgets/adresssheet.dart | 108 +++ .../lib/src/models/address_sheet.dart | 134 +++ .../lib/src/models/address_sheet.freezed.dart | 904 ++++++++++++++++++ .../lib/src/models/address_sheet.g.dart | 110 +++ .../lib/src/models/errors.dart | 2 + .../lib/stripe_platform_interface.dart | 1 + 6 files changed, 1259 insertions(+) create mode 100644 packages/stripe/lib/src/widgets/adresssheet.dart create mode 100644 packages/stripe_platform_interface/lib/src/models/address_sheet.dart create mode 100644 packages/stripe_platform_interface/lib/src/models/address_sheet.freezed.dart create mode 100644 packages/stripe_platform_interface/lib/src/models/address_sheet.g.dart diff --git a/packages/stripe/lib/src/widgets/adresssheet.dart b/packages/stripe/lib/src/widgets/adresssheet.dart new file mode 100644 index 000000000..40a5169c0 --- /dev/null +++ b/packages/stripe/lib/src/widgets/adresssheet.dart @@ -0,0 +1,108 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_stripe/flutter_stripe.dart'; + +class AddressSheet extends StatelessWidget { + const AddressSheet({ + required this.onSubmit, + required this.onError, + required this.params, + this.height = 300, + super.key, + }); + + /// The height of the address sheet + final int height; + + /// Called when the user submits their information + final OnAddressSheetError onSubmit; + + /// Called when the user taps the button to close the sheet before submitting their information, or when an error occurs + final OnAddressSheetError onError; + + /// The parameters for the address sheet + final AddressSheetParams params; + + @override + Widget build(BuildContext context) { + return _AddressSheet( + onSubmit: onSubmit, + onError: onError, + params: params, + height: height, + ); + } +} + +class _AddressSheet extends StatefulWidget { + const _AddressSheet({ + required this.onSubmit, + required this.onError, + required this.height, + required this.params, + }); + + final AddressSheetParams params; + final OnAddressSheetError onSubmit; + final OnAddressSheetError onError; + final int height; + + @override + State<_AddressSheet> createState() => _AddressSheetState(); +} + +class _AddressSheetState extends State<_AddressSheet> { + static const _viewType = 'flutter.stripe/address_sheet'; + MethodChannel? _methodChannel; + + void onPlatformViewCreated(int viewId) { + _methodChannel = MethodChannel('flutter.stripe/address_sheet/$viewId'); + _methodChannel?.setMethodCallHandler((call) async { + if (call.method == 'onSubmit') { + print('blaat details ${call.arguments}'); + // widget.onSubmit.call(); + } else if (call.method == 'onError') { + print('blaat details ${call.arguments}'); + } + }); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: widget.height.toDouble(), + child: defaultTargetPlatform == TargetPlatform.iOS + ? UiKitView( + viewType: _viewType, + creationParams: const StandardMessageCodec(), + ) + : PlatformViewLink( + surfaceFactory: (context, controller) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + gestureRecognizers: const >{}, + ); + }, + onCreatePlatformView: (params) { + onPlatformViewCreated(params.id); + return PlatformViewsService.initExpensiveAndroidView( + id: params.id, + viewType: _viewType, + layoutDirection: TextDirection.ltr, + creationParams: {}, + creationParamsCodec: const StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener( + params.onPlatformViewCreated) + ..create(); + }, + viewType: _viewType, + ), + ); + } +} diff --git a/packages/stripe_platform_interface/lib/src/models/address_sheet.dart b/packages/stripe_platform_interface/lib/src/models/address_sheet.dart new file mode 100644 index 000000000..b41e1197a --- /dev/null +++ b/packages/stripe_platform_interface/lib/src/models/address_sheet.dart @@ -0,0 +1,134 @@ +import 'dart:async'; + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:stripe_platform_interface/stripe_platform_interface.dart'; + +part 'address_sheet.freezed.dart'; +part 'address_sheet.g.dart'; + +/// The style of how the address sheet should be presented +enum AddressSheetPresentationStyle { + /// Full screen + fullscreen, + + /// Popover + popover, + + /// Page sheet + pageSheet, + + /// Form sheet + formSheet, + + /// Automatic + automatic, + + /// Over full screen + overFullScreen, +} + +/// How the address sheet should animate +enum AddressSheetAnimationStyle { + /// flip animation style + flip, + + /// curl animation style + curl, + + /// slide animation style + slide, + + /// dissolve animation style + dissolve, +} + +@freezed +class AddressSheetAdditionalFields with _$AddressSheetAdditionalFields { + const factory AddressSheetAdditionalFields({ + ///Determines whether the phone number is hidden, required, or optional. Defaults to hidden + required AddressSheetPhoneNumberField phoneNumber, + + /// The label of a checkbox displayed below other fields. If null or undefined, the checkbox is not displayed + required String? checkboxLabel, + }) = _AddressSheetAdditionalFields; + + factory AddressSheetAdditionalFields.fromJson(Map json) => + _$AddressSheetAdditionalFieldsFromJson(json); +} + +/// The style of how the phone number should be presented +enum AddressSheetPhoneNumberField { + /// The phone number is required + required, + + /// The phone number is optional + optional, + + /// The phone number is hidden + hidden, +} + +@freezed +class CollectAddressResult with _$CollectAddressResult { + const factory CollectAddressResult({ + /// The customer's full name + required String name, + + /// The customer's address + required Address address, + + /// The customer's phone number + String? phoneNumber, + }) = _CollectAddressResult; + + factory CollectAddressResult.fromJson(Map json) => + _$CollectAddressResultFromJson(json); +} + +typedef OnAddressSheetSubmit = FutureOr Function( + CollectAddressResult result); + +typedef OnAddressSheetError = FutureOr Function( + StripeError error); + +@freezed +class AddressSheetParams with _$AddressSheetParams { + @JsonSerializable(explicitToJson: true) + const factory AddressSheetParams({ + /// Whether the address sheet is visible + @Default(true) bool visible, + + /// Controls how the modal is presented (after animation). iOS only. + AddressSheetPresentationStyle? presentationStyle, + + /// Controls how the modal animates. iOS only. + AddressSheetAnimationStyle? animationStyle, + + /// Configuration for the appearance of the address sheet + PaymentSheetAppearance? appearance, + + /// Default values to prepoulate the address sheet + AddressDetails? defaultValues, + + /// Configuration for additional fields besides the physical address + AddressSheetAdditionalFields? additionalFields, + + /// A list of two-letter country codes representing countries the customers can select. If the list is empty (the default), we display all countries. + List? allowedCountries, + + /// A list of two-letter country codes representing countries that support address autocomplete. Defaults to a list of countries that Stripe has audited to ensure a good autocomplete experience. + List? autocompleteCountries, + + /// The title of the primary button displayed at the bottom of the screen. Defaults to "Save Address" + String? primaryButtonTitle, + + /// The title of the address sheet. Defaults to "Address" + String? sheetTitle, + + /// Android only. Google Places api key used to provide autocomplete suggestions. When null, autocomplete is disabled on Android. + String? googlePlacesApiKey, + }) = _AddressSheetParams; + + factory AddressSheetParams.fromJson(Map json) => + _$AddressSheetParamsFromJson(json); +} diff --git a/packages/stripe_platform_interface/lib/src/models/address_sheet.freezed.dart b/packages/stripe_platform_interface/lib/src/models/address_sheet.freezed.dart new file mode 100644 index 000000000..fb9d0328f --- /dev/null +++ b/packages/stripe_platform_interface/lib/src/models/address_sheet.freezed.dart @@ -0,0 +1,904 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'address_sheet.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +AddressSheetAdditionalFields _$AddressSheetAdditionalFieldsFromJson( + Map json) { + return _AddressSheetAdditionalFields.fromJson(json); +} + +/// @nodoc +mixin _$AddressSheetAdditionalFields { + ///Determines whether the phone number is hidden, required, or optional. Defaults to hidden + AddressSheetPhoneNumberField get phoneNumber => + throw _privateConstructorUsedError; + + /// The label of a checkbox displayed below other fields. If null or undefined, the checkbox is not displayed + String? get checkboxLabel => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $AddressSheetAdditionalFieldsCopyWith + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AddressSheetAdditionalFieldsCopyWith<$Res> { + factory $AddressSheetAdditionalFieldsCopyWith( + AddressSheetAdditionalFields value, + $Res Function(AddressSheetAdditionalFields) then) = + _$AddressSheetAdditionalFieldsCopyWithImpl<$Res, + AddressSheetAdditionalFields>; + @useResult + $Res call({AddressSheetPhoneNumberField phoneNumber, String? checkboxLabel}); +} + +/// @nodoc +class _$AddressSheetAdditionalFieldsCopyWithImpl<$Res, + $Val extends AddressSheetAdditionalFields> + implements $AddressSheetAdditionalFieldsCopyWith<$Res> { + _$AddressSheetAdditionalFieldsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? phoneNumber = null, + Object? checkboxLabel = freezed, + }) { + return _then(_value.copyWith( + phoneNumber: null == phoneNumber + ? _value.phoneNumber + : phoneNumber // ignore: cast_nullable_to_non_nullable + as AddressSheetPhoneNumberField, + checkboxLabel: freezed == checkboxLabel + ? _value.checkboxLabel + : checkboxLabel // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$AddressSheetAdditionalFieldsImplCopyWith<$Res> + implements $AddressSheetAdditionalFieldsCopyWith<$Res> { + factory _$$AddressSheetAdditionalFieldsImplCopyWith( + _$AddressSheetAdditionalFieldsImpl value, + $Res Function(_$AddressSheetAdditionalFieldsImpl) then) = + __$$AddressSheetAdditionalFieldsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({AddressSheetPhoneNumberField phoneNumber, String? checkboxLabel}); +} + +/// @nodoc +class __$$AddressSheetAdditionalFieldsImplCopyWithImpl<$Res> + extends _$AddressSheetAdditionalFieldsCopyWithImpl<$Res, + _$AddressSheetAdditionalFieldsImpl> + implements _$$AddressSheetAdditionalFieldsImplCopyWith<$Res> { + __$$AddressSheetAdditionalFieldsImplCopyWithImpl( + _$AddressSheetAdditionalFieldsImpl _value, + $Res Function(_$AddressSheetAdditionalFieldsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? phoneNumber = null, + Object? checkboxLabel = freezed, + }) { + return _then(_$AddressSheetAdditionalFieldsImpl( + phoneNumber: null == phoneNumber + ? _value.phoneNumber + : phoneNumber // ignore: cast_nullable_to_non_nullable + as AddressSheetPhoneNumberField, + checkboxLabel: freezed == checkboxLabel + ? _value.checkboxLabel + : checkboxLabel // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$AddressSheetAdditionalFieldsImpl + implements _AddressSheetAdditionalFields { + const _$AddressSheetAdditionalFieldsImpl( + {required this.phoneNumber, required this.checkboxLabel}); + + factory _$AddressSheetAdditionalFieldsImpl.fromJson( + Map json) => + _$$AddressSheetAdditionalFieldsImplFromJson(json); + + ///Determines whether the phone number is hidden, required, or optional. Defaults to hidden + @override + final AddressSheetPhoneNumberField phoneNumber; + + /// The label of a checkbox displayed below other fields. If null or undefined, the checkbox is not displayed + @override + final String? checkboxLabel; + + @override + String toString() { + return 'AddressSheetAdditionalFields(phoneNumber: $phoneNumber, checkboxLabel: $checkboxLabel)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AddressSheetAdditionalFieldsImpl && + (identical(other.phoneNumber, phoneNumber) || + other.phoneNumber == phoneNumber) && + (identical(other.checkboxLabel, checkboxLabel) || + other.checkboxLabel == checkboxLabel)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, phoneNumber, checkboxLabel); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AddressSheetAdditionalFieldsImplCopyWith< + _$AddressSheetAdditionalFieldsImpl> + get copyWith => __$$AddressSheetAdditionalFieldsImplCopyWithImpl< + _$AddressSheetAdditionalFieldsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$AddressSheetAdditionalFieldsImplToJson( + this, + ); + } +} + +abstract class _AddressSheetAdditionalFields + implements AddressSheetAdditionalFields { + const factory _AddressSheetAdditionalFields( + {required final AddressSheetPhoneNumberField phoneNumber, + required final String? checkboxLabel}) = + _$AddressSheetAdditionalFieldsImpl; + + factory _AddressSheetAdditionalFields.fromJson(Map json) = + _$AddressSheetAdditionalFieldsImpl.fromJson; + + @override + + ///Determines whether the phone number is hidden, required, or optional. Defaults to hidden + AddressSheetPhoneNumberField get phoneNumber; + @override + + /// The label of a checkbox displayed below other fields. If null or undefined, the checkbox is not displayed + String? get checkboxLabel; + @override + @JsonKey(ignore: true) + _$$AddressSheetAdditionalFieldsImplCopyWith< + _$AddressSheetAdditionalFieldsImpl> + get copyWith => throw _privateConstructorUsedError; +} + +CollectAddressResult _$CollectAddressResultFromJson(Map json) { + return _CollectAddressResult.fromJson(json); +} + +/// @nodoc +mixin _$CollectAddressResult { + /// The customer's full name + String get name => throw _privateConstructorUsedError; + + /// The customer's address + Address get address => throw _privateConstructorUsedError; + + /// The customer's phone number + String? get phoneNumber => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $CollectAddressResultCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CollectAddressResultCopyWith<$Res> { + factory $CollectAddressResultCopyWith(CollectAddressResult value, + $Res Function(CollectAddressResult) then) = + _$CollectAddressResultCopyWithImpl<$Res, CollectAddressResult>; + @useResult + $Res call({String name, Address address, String? phoneNumber}); + + $AddressCopyWith<$Res> get address; +} + +/// @nodoc +class _$CollectAddressResultCopyWithImpl<$Res, + $Val extends CollectAddressResult> + implements $CollectAddressResultCopyWith<$Res> { + _$CollectAddressResultCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? address = null, + Object? phoneNumber = freezed, + }) { + return _then(_value.copyWith( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as Address, + phoneNumber: freezed == phoneNumber + ? _value.phoneNumber + : phoneNumber // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $AddressCopyWith<$Res> get address { + return $AddressCopyWith<$Res>(_value.address, (value) { + return _then(_value.copyWith(address: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$CollectAddressResultImplCopyWith<$Res> + implements $CollectAddressResultCopyWith<$Res> { + factory _$$CollectAddressResultImplCopyWith(_$CollectAddressResultImpl value, + $Res Function(_$CollectAddressResultImpl) then) = + __$$CollectAddressResultImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String name, Address address, String? phoneNumber}); + + @override + $AddressCopyWith<$Res> get address; +} + +/// @nodoc +class __$$CollectAddressResultImplCopyWithImpl<$Res> + extends _$CollectAddressResultCopyWithImpl<$Res, _$CollectAddressResultImpl> + implements _$$CollectAddressResultImplCopyWith<$Res> { + __$$CollectAddressResultImplCopyWithImpl(_$CollectAddressResultImpl _value, + $Res Function(_$CollectAddressResultImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? address = null, + Object? phoneNumber = freezed, + }) { + return _then(_$CollectAddressResultImpl( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + address: null == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as Address, + phoneNumber: freezed == phoneNumber + ? _value.phoneNumber + : phoneNumber // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$CollectAddressResultImpl implements _CollectAddressResult { + const _$CollectAddressResultImpl( + {required this.name, required this.address, this.phoneNumber}); + + factory _$CollectAddressResultImpl.fromJson(Map json) => + _$$CollectAddressResultImplFromJson(json); + + /// The customer's full name + @override + final String name; + + /// The customer's address + @override + final Address address; + + /// The customer's phone number + @override + final String? phoneNumber; + + @override + String toString() { + return 'CollectAddressResult(name: $name, address: $address, phoneNumber: $phoneNumber)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CollectAddressResultImpl && + (identical(other.name, name) || other.name == name) && + (identical(other.address, address) || other.address == address) && + (identical(other.phoneNumber, phoneNumber) || + other.phoneNumber == phoneNumber)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, name, address, phoneNumber); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$CollectAddressResultImplCopyWith<_$CollectAddressResultImpl> + get copyWith => + __$$CollectAddressResultImplCopyWithImpl<_$CollectAddressResultImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$CollectAddressResultImplToJson( + this, + ); + } +} + +abstract class _CollectAddressResult implements CollectAddressResult { + const factory _CollectAddressResult( + {required final String name, + required final Address address, + final String? phoneNumber}) = _$CollectAddressResultImpl; + + factory _CollectAddressResult.fromJson(Map json) = + _$CollectAddressResultImpl.fromJson; + + @override + + /// The customer's full name + String get name; + @override + + /// The customer's address + Address get address; + @override + + /// The customer's phone number + String? get phoneNumber; + @override + @JsonKey(ignore: true) + _$$CollectAddressResultImplCopyWith<_$CollectAddressResultImpl> + get copyWith => throw _privateConstructorUsedError; +} + +AddressSheetParams _$AddressSheetParamsFromJson(Map json) { + return _AddressSheetParams.fromJson(json); +} + +/// @nodoc +mixin _$AddressSheetParams { + /// Whether the address sheet is visible + bool get visible => throw _privateConstructorUsedError; + + /// Controls how the modal is presented (after animation). iOS only. + AddressSheetPresentationStyle? get presentationStyle => + throw _privateConstructorUsedError; + + /// Controls how the modal animates. iOS only. + AddressSheetAnimationStyle? get animationStyle => + throw _privateConstructorUsedError; + + /// Configuration for the appearance of the address sheet + PaymentSheetAppearance? get appearance => throw _privateConstructorUsedError; + + /// Default values to prepoulate the address sheet + AddressDetails? get defaultValues => throw _privateConstructorUsedError; + + /// Configuration for additional fields besides the physical address + AddressSheetAdditionalFields? get additionalFields => + throw _privateConstructorUsedError; + + /// A list of two-letter country codes representing countries the customers can select. If the list is empty (the default), we display all countries. + List? get allowedCountries => throw _privateConstructorUsedError; + + /// A list of two-letter country codes representing countries that support address autocomplete. Defaults to a list of countries that Stripe has audited to ensure a good autocomplete experience. + List? get autocompleteCountries => throw _privateConstructorUsedError; + + /// The title of the primary button displayed at the bottom of the screen. Defaults to "Save Address" + String? get primaryButtonTitle => throw _privateConstructorUsedError; + + /// The title of the address sheet. Defaults to "Address" + String? get sheetTitle => throw _privateConstructorUsedError; + + /// Android only. Google Places api key used to provide autocomplete suggestions. When null, autocomplete is disabled on Android. + String? get googlePlacesApiKey => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $AddressSheetParamsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AddressSheetParamsCopyWith<$Res> { + factory $AddressSheetParamsCopyWith( + AddressSheetParams value, $Res Function(AddressSheetParams) then) = + _$AddressSheetParamsCopyWithImpl<$Res, AddressSheetParams>; + @useResult + $Res call( + {bool visible, + AddressSheetPresentationStyle? presentationStyle, + AddressSheetAnimationStyle? animationStyle, + PaymentSheetAppearance? appearance, + AddressDetails? defaultValues, + AddressSheetAdditionalFields? additionalFields, + List? allowedCountries, + List? autocompleteCountries, + String? primaryButtonTitle, + String? sheetTitle, + String? googlePlacesApiKey}); + + $PaymentSheetAppearanceCopyWith<$Res>? get appearance; + $AddressDetailsCopyWith<$Res>? get defaultValues; + $AddressSheetAdditionalFieldsCopyWith<$Res>? get additionalFields; +} + +/// @nodoc +class _$AddressSheetParamsCopyWithImpl<$Res, $Val extends AddressSheetParams> + implements $AddressSheetParamsCopyWith<$Res> { + _$AddressSheetParamsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? visible = null, + Object? presentationStyle = freezed, + Object? animationStyle = freezed, + Object? appearance = freezed, + Object? defaultValues = freezed, + Object? additionalFields = freezed, + Object? allowedCountries = freezed, + Object? autocompleteCountries = freezed, + Object? primaryButtonTitle = freezed, + Object? sheetTitle = freezed, + Object? googlePlacesApiKey = freezed, + }) { + return _then(_value.copyWith( + visible: null == visible + ? _value.visible + : visible // ignore: cast_nullable_to_non_nullable + as bool, + presentationStyle: freezed == presentationStyle + ? _value.presentationStyle + : presentationStyle // ignore: cast_nullable_to_non_nullable + as AddressSheetPresentationStyle?, + animationStyle: freezed == animationStyle + ? _value.animationStyle + : animationStyle // ignore: cast_nullable_to_non_nullable + as AddressSheetAnimationStyle?, + appearance: freezed == appearance + ? _value.appearance + : appearance // ignore: cast_nullable_to_non_nullable + as PaymentSheetAppearance?, + defaultValues: freezed == defaultValues + ? _value.defaultValues + : defaultValues // ignore: cast_nullable_to_non_nullable + as AddressDetails?, + additionalFields: freezed == additionalFields + ? _value.additionalFields + : additionalFields // ignore: cast_nullable_to_non_nullable + as AddressSheetAdditionalFields?, + allowedCountries: freezed == allowedCountries + ? _value.allowedCountries + : allowedCountries // ignore: cast_nullable_to_non_nullable + as List?, + autocompleteCountries: freezed == autocompleteCountries + ? _value.autocompleteCountries + : autocompleteCountries // ignore: cast_nullable_to_non_nullable + as List?, + primaryButtonTitle: freezed == primaryButtonTitle + ? _value.primaryButtonTitle + : primaryButtonTitle // ignore: cast_nullable_to_non_nullable + as String?, + sheetTitle: freezed == sheetTitle + ? _value.sheetTitle + : sheetTitle // ignore: cast_nullable_to_non_nullable + as String?, + googlePlacesApiKey: freezed == googlePlacesApiKey + ? _value.googlePlacesApiKey + : googlePlacesApiKey // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $PaymentSheetAppearanceCopyWith<$Res>? get appearance { + if (_value.appearance == null) { + return null; + } + + return $PaymentSheetAppearanceCopyWith<$Res>(_value.appearance!, (value) { + return _then(_value.copyWith(appearance: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $AddressDetailsCopyWith<$Res>? get defaultValues { + if (_value.defaultValues == null) { + return null; + } + + return $AddressDetailsCopyWith<$Res>(_value.defaultValues!, (value) { + return _then(_value.copyWith(defaultValues: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $AddressSheetAdditionalFieldsCopyWith<$Res>? get additionalFields { + if (_value.additionalFields == null) { + return null; + } + + return $AddressSheetAdditionalFieldsCopyWith<$Res>(_value.additionalFields!, + (value) { + return _then(_value.copyWith(additionalFields: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$AddressSheetParamsImplCopyWith<$Res> + implements $AddressSheetParamsCopyWith<$Res> { + factory _$$AddressSheetParamsImplCopyWith(_$AddressSheetParamsImpl value, + $Res Function(_$AddressSheetParamsImpl) then) = + __$$AddressSheetParamsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool visible, + AddressSheetPresentationStyle? presentationStyle, + AddressSheetAnimationStyle? animationStyle, + PaymentSheetAppearance? appearance, + AddressDetails? defaultValues, + AddressSheetAdditionalFields? additionalFields, + List? allowedCountries, + List? autocompleteCountries, + String? primaryButtonTitle, + String? sheetTitle, + String? googlePlacesApiKey}); + + @override + $PaymentSheetAppearanceCopyWith<$Res>? get appearance; + @override + $AddressDetailsCopyWith<$Res>? get defaultValues; + @override + $AddressSheetAdditionalFieldsCopyWith<$Res>? get additionalFields; +} + +/// @nodoc +class __$$AddressSheetParamsImplCopyWithImpl<$Res> + extends _$AddressSheetParamsCopyWithImpl<$Res, _$AddressSheetParamsImpl> + implements _$$AddressSheetParamsImplCopyWith<$Res> { + __$$AddressSheetParamsImplCopyWithImpl(_$AddressSheetParamsImpl _value, + $Res Function(_$AddressSheetParamsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? visible = null, + Object? presentationStyle = freezed, + Object? animationStyle = freezed, + Object? appearance = freezed, + Object? defaultValues = freezed, + Object? additionalFields = freezed, + Object? allowedCountries = freezed, + Object? autocompleteCountries = freezed, + Object? primaryButtonTitle = freezed, + Object? sheetTitle = freezed, + Object? googlePlacesApiKey = freezed, + }) { + return _then(_$AddressSheetParamsImpl( + visible: null == visible + ? _value.visible + : visible // ignore: cast_nullable_to_non_nullable + as bool, + presentationStyle: freezed == presentationStyle + ? _value.presentationStyle + : presentationStyle // ignore: cast_nullable_to_non_nullable + as AddressSheetPresentationStyle?, + animationStyle: freezed == animationStyle + ? _value.animationStyle + : animationStyle // ignore: cast_nullable_to_non_nullable + as AddressSheetAnimationStyle?, + appearance: freezed == appearance + ? _value.appearance + : appearance // ignore: cast_nullable_to_non_nullable + as PaymentSheetAppearance?, + defaultValues: freezed == defaultValues + ? _value.defaultValues + : defaultValues // ignore: cast_nullable_to_non_nullable + as AddressDetails?, + additionalFields: freezed == additionalFields + ? _value.additionalFields + : additionalFields // ignore: cast_nullable_to_non_nullable + as AddressSheetAdditionalFields?, + allowedCountries: freezed == allowedCountries + ? _value._allowedCountries + : allowedCountries // ignore: cast_nullable_to_non_nullable + as List?, + autocompleteCountries: freezed == autocompleteCountries + ? _value._autocompleteCountries + : autocompleteCountries // ignore: cast_nullable_to_non_nullable + as List?, + primaryButtonTitle: freezed == primaryButtonTitle + ? _value.primaryButtonTitle + : primaryButtonTitle // ignore: cast_nullable_to_non_nullable + as String?, + sheetTitle: freezed == sheetTitle + ? _value.sheetTitle + : sheetTitle // ignore: cast_nullable_to_non_nullable + as String?, + googlePlacesApiKey: freezed == googlePlacesApiKey + ? _value.googlePlacesApiKey + : googlePlacesApiKey // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +@JsonSerializable(explicitToJson: true) +class _$AddressSheetParamsImpl implements _AddressSheetParams { + const _$AddressSheetParamsImpl( + {this.visible = true, + this.presentationStyle, + this.animationStyle, + this.appearance, + this.defaultValues, + this.additionalFields, + final List? allowedCountries, + final List? autocompleteCountries, + this.primaryButtonTitle, + this.sheetTitle, + this.googlePlacesApiKey}) + : _allowedCountries = allowedCountries, + _autocompleteCountries = autocompleteCountries; + + factory _$AddressSheetParamsImpl.fromJson(Map json) => + _$$AddressSheetParamsImplFromJson(json); + + /// Whether the address sheet is visible + @override + @JsonKey() + final bool visible; + + /// Controls how the modal is presented (after animation). iOS only. + @override + final AddressSheetPresentationStyle? presentationStyle; + + /// Controls how the modal animates. iOS only. + @override + final AddressSheetAnimationStyle? animationStyle; + + /// Configuration for the appearance of the address sheet + @override + final PaymentSheetAppearance? appearance; + + /// Default values to prepoulate the address sheet + @override + final AddressDetails? defaultValues; + + /// Configuration for additional fields besides the physical address + @override + final AddressSheetAdditionalFields? additionalFields; + + /// A list of two-letter country codes representing countries the customers can select. If the list is empty (the default), we display all countries. + final List? _allowedCountries; + + /// A list of two-letter country codes representing countries the customers can select. If the list is empty (the default), we display all countries. + @override + List? get allowedCountries { + final value = _allowedCountries; + if (value == null) return null; + if (_allowedCountries is EqualUnmodifiableListView) + return _allowedCountries; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + /// A list of two-letter country codes representing countries that support address autocomplete. Defaults to a list of countries that Stripe has audited to ensure a good autocomplete experience. + final List? _autocompleteCountries; + + /// A list of two-letter country codes representing countries that support address autocomplete. Defaults to a list of countries that Stripe has audited to ensure a good autocomplete experience. + @override + List? get autocompleteCountries { + final value = _autocompleteCountries; + if (value == null) return null; + if (_autocompleteCountries is EqualUnmodifiableListView) + return _autocompleteCountries; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + /// The title of the primary button displayed at the bottom of the screen. Defaults to "Save Address" + @override + final String? primaryButtonTitle; + + /// The title of the address sheet. Defaults to "Address" + @override + final String? sheetTitle; + + /// Android only. Google Places api key used to provide autocomplete suggestions. When null, autocomplete is disabled on Android. + @override + final String? googlePlacesApiKey; + + @override + String toString() { + return 'AddressSheetParams(visible: $visible, presentationStyle: $presentationStyle, animationStyle: $animationStyle, appearance: $appearance, defaultValues: $defaultValues, additionalFields: $additionalFields, allowedCountries: $allowedCountries, autocompleteCountries: $autocompleteCountries, primaryButtonTitle: $primaryButtonTitle, sheetTitle: $sheetTitle, googlePlacesApiKey: $googlePlacesApiKey)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AddressSheetParamsImpl && + (identical(other.visible, visible) || other.visible == visible) && + (identical(other.presentationStyle, presentationStyle) || + other.presentationStyle == presentationStyle) && + (identical(other.animationStyle, animationStyle) || + other.animationStyle == animationStyle) && + (identical(other.appearance, appearance) || + other.appearance == appearance) && + (identical(other.defaultValues, defaultValues) || + other.defaultValues == defaultValues) && + (identical(other.additionalFields, additionalFields) || + other.additionalFields == additionalFields) && + const DeepCollectionEquality() + .equals(other._allowedCountries, _allowedCountries) && + const DeepCollectionEquality() + .equals(other._autocompleteCountries, _autocompleteCountries) && + (identical(other.primaryButtonTitle, primaryButtonTitle) || + other.primaryButtonTitle == primaryButtonTitle) && + (identical(other.sheetTitle, sheetTitle) || + other.sheetTitle == sheetTitle) && + (identical(other.googlePlacesApiKey, googlePlacesApiKey) || + other.googlePlacesApiKey == googlePlacesApiKey)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + visible, + presentationStyle, + animationStyle, + appearance, + defaultValues, + additionalFields, + const DeepCollectionEquality().hash(_allowedCountries), + const DeepCollectionEquality().hash(_autocompleteCountries), + primaryButtonTitle, + sheetTitle, + googlePlacesApiKey); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AddressSheetParamsImplCopyWith<_$AddressSheetParamsImpl> get copyWith => + __$$AddressSheetParamsImplCopyWithImpl<_$AddressSheetParamsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$AddressSheetParamsImplToJson( + this, + ); + } +} + +abstract class _AddressSheetParams implements AddressSheetParams { + const factory _AddressSheetParams( + {final bool visible, + final AddressSheetPresentationStyle? presentationStyle, + final AddressSheetAnimationStyle? animationStyle, + final PaymentSheetAppearance? appearance, + final AddressDetails? defaultValues, + final AddressSheetAdditionalFields? additionalFields, + final List? allowedCountries, + final List? autocompleteCountries, + final String? primaryButtonTitle, + final String? sheetTitle, + final String? googlePlacesApiKey}) = _$AddressSheetParamsImpl; + + factory _AddressSheetParams.fromJson(Map json) = + _$AddressSheetParamsImpl.fromJson; + + @override + + /// Whether the address sheet is visible + bool get visible; + @override + + /// Controls how the modal is presented (after animation). iOS only. + AddressSheetPresentationStyle? get presentationStyle; + @override + + /// Controls how the modal animates. iOS only. + AddressSheetAnimationStyle? get animationStyle; + @override + + /// Configuration for the appearance of the address sheet + PaymentSheetAppearance? get appearance; + @override + + /// Default values to prepoulate the address sheet + AddressDetails? get defaultValues; + @override + + /// Configuration for additional fields besides the physical address + AddressSheetAdditionalFields? get additionalFields; + @override + + /// A list of two-letter country codes representing countries the customers can select. If the list is empty (the default), we display all countries. + List? get allowedCountries; + @override + + /// A list of two-letter country codes representing countries that support address autocomplete. Defaults to a list of countries that Stripe has audited to ensure a good autocomplete experience. + List? get autocompleteCountries; + @override + + /// The title of the primary button displayed at the bottom of the screen. Defaults to "Save Address" + String? get primaryButtonTitle; + @override + + /// The title of the address sheet. Defaults to "Address" + String? get sheetTitle; + @override + + /// Android only. Google Places api key used to provide autocomplete suggestions. When null, autocomplete is disabled on Android. + String? get googlePlacesApiKey; + @override + @JsonKey(ignore: true) + _$$AddressSheetParamsImplCopyWith<_$AddressSheetParamsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/stripe_platform_interface/lib/src/models/address_sheet.g.dart b/packages/stripe_platform_interface/lib/src/models/address_sheet.g.dart new file mode 100644 index 000000000..bac5fbdb9 --- /dev/null +++ b/packages/stripe_platform_interface/lib/src/models/address_sheet.g.dart @@ -0,0 +1,110 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'address_sheet.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$AddressSheetAdditionalFieldsImpl _$$AddressSheetAdditionalFieldsImplFromJson( + Map json) => + _$AddressSheetAdditionalFieldsImpl( + phoneNumber: $enumDecode( + _$AddressSheetPhoneNumberFieldEnumMap, json['phoneNumber']), + checkboxLabel: json['checkboxLabel'] as String?, + ); + +Map _$$AddressSheetAdditionalFieldsImplToJson( + _$AddressSheetAdditionalFieldsImpl instance) => + { + 'phoneNumber': + _$AddressSheetPhoneNumberFieldEnumMap[instance.phoneNumber]!, + 'checkboxLabel': instance.checkboxLabel, + }; + +const _$AddressSheetPhoneNumberFieldEnumMap = { + AddressSheetPhoneNumberField.required: 'required', + AddressSheetPhoneNumberField.optional: 'optional', + AddressSheetPhoneNumberField.hidden: 'hidden', +}; + +_$CollectAddressResultImpl _$$CollectAddressResultImplFromJson( + Map json) => + _$CollectAddressResultImpl( + name: json['name'] as String, + address: Address.fromJson(json['address'] as Map), + phoneNumber: json['phoneNumber'] as String?, + ); + +Map _$$CollectAddressResultImplToJson( + _$CollectAddressResultImpl instance) => + { + 'name': instance.name, + 'address': instance.address, + 'phoneNumber': instance.phoneNumber, + }; + +_$AddressSheetParamsImpl _$$AddressSheetParamsImplFromJson( + Map json) => + _$AddressSheetParamsImpl( + visible: json['visible'] as bool? ?? true, + presentationStyle: $enumDecodeNullable( + _$AddressSheetPresentationStyleEnumMap, json['presentationStyle']), + animationStyle: $enumDecodeNullable( + _$AddressSheetAnimationStyleEnumMap, json['animationStyle']), + appearance: json['appearance'] == null + ? null + : PaymentSheetAppearance.fromJson( + json['appearance'] as Map), + defaultValues: json['defaultValues'] == null + ? null + : AddressDetails.fromJson( + json['defaultValues'] as Map), + additionalFields: json['additionalFields'] == null + ? null + : AddressSheetAdditionalFields.fromJson( + json['additionalFields'] as Map), + allowedCountries: (json['allowedCountries'] as List?) + ?.map((e) => e as String) + .toList(), + autocompleteCountries: (json['autocompleteCountries'] as List?) + ?.map((e) => e as String) + .toList(), + primaryButtonTitle: json['primaryButtonTitle'] as String?, + sheetTitle: json['sheetTitle'] as String?, + googlePlacesApiKey: json['googlePlacesApiKey'] as String?, + ); + +Map _$$AddressSheetParamsImplToJson( + _$AddressSheetParamsImpl instance) => + { + 'visible': instance.visible, + 'presentationStyle': + _$AddressSheetPresentationStyleEnumMap[instance.presentationStyle], + 'animationStyle': + _$AddressSheetAnimationStyleEnumMap[instance.animationStyle], + 'appearance': instance.appearance?.toJson(), + 'defaultValues': instance.defaultValues?.toJson(), + 'additionalFields': instance.additionalFields?.toJson(), + 'allowedCountries': instance.allowedCountries, + 'autocompleteCountries': instance.autocompleteCountries, + 'primaryButtonTitle': instance.primaryButtonTitle, + 'sheetTitle': instance.sheetTitle, + 'googlePlacesApiKey': instance.googlePlacesApiKey, + }; + +const _$AddressSheetPresentationStyleEnumMap = { + AddressSheetPresentationStyle.fullscreen: 'fullscreen', + AddressSheetPresentationStyle.popover: 'popover', + AddressSheetPresentationStyle.pageSheet: 'pageSheet', + AddressSheetPresentationStyle.formSheet: 'formSheet', + AddressSheetPresentationStyle.automatic: 'automatic', + AddressSheetPresentationStyle.overFullScreen: 'overFullScreen', +}; + +const _$AddressSheetAnimationStyleEnumMap = { + AddressSheetAnimationStyle.flip: 'flip', + AddressSheetAnimationStyle.curl: 'curl', + AddressSheetAnimationStyle.slide: 'slide', + AddressSheetAnimationStyle.dissolve: 'dissolve', +}; diff --git a/packages/stripe_platform_interface/lib/src/models/errors.dart b/packages/stripe_platform_interface/lib/src/models/errors.dart index a75217a06..82fafeaf8 100644 --- a/packages/stripe_platform_interface/lib/src/models/errors.dart +++ b/packages/stripe_platform_interface/lib/src/models/errors.dart @@ -10,6 +10,8 @@ enum CreateTokenError { unknown } enum PaymentSheetError { unknown } +enum AddressSheetError { unknown } + enum CustomerSheetError { unknown, failed, canceled } @freezed diff --git a/packages/stripe_platform_interface/lib/stripe_platform_interface.dart b/packages/stripe_platform_interface/lib/stripe_platform_interface.dart index 4d7d91c3e..a5e41735a 100644 --- a/packages/stripe_platform_interface/lib/stripe_platform_interface.dart +++ b/packages/stripe_platform_interface/lib/stripe_platform_interface.dart @@ -1,6 +1,7 @@ library stripe_platform_interface; export 'src/card_edit_controller.dart'; +export 'src/models/address_sheet.dart'; export 'src/models/ach_params.dart'; export 'src/models/address.dart'; export 'src/models/app_info.dart'; From a276f4d07011867c98bdc94064d80c7eaacd36a9 Mon Sep 17 00:00:00 2001 From: Remon Date: Sun, 15 Dec 2024 20:02:41 +0000 Subject: [PATCH 3/7] feat: add correct mapping for addressheet --- .../screens/address_sheet/address_sheet.dart | 57 +++++++++++ example/lib/screens/screens.dart | 8 ++ packages/stripe/lib/flutter_stripe.dart | 1 + .../stripe/lib/src/widgets/adresssheet.dart | 34 ++++--- .../stripe/AddressSheetPlatformView.kt | 49 ---------- .../stripe/StripeAddressSheetPlatformView.kt | 98 +++++++++++++++++++ .../StripeAddressSheetPlatformViewFactory.kt | 2 +- .../lib/src/models/address_sheet.dart | 3 +- 8 files changed, 189 insertions(+), 63 deletions(-) create mode 100644 example/lib/screens/address_sheet/address_sheet.dart delete mode 100644 packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt create mode 100644 packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformView.kt diff --git a/example/lib/screens/address_sheet/address_sheet.dart b/example/lib/screens/address_sheet/address_sheet.dart new file mode 100644 index 000000000..a8c6854df --- /dev/null +++ b/example/lib/screens/address_sheet/address_sheet.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_stripe/flutter_stripe.dart'; +import 'package:stripe_example/utils.dart'; +import 'package:stripe_example/widgets/example_scaffold.dart'; +import 'package:stripe_example/widgets/response_card.dart'; + +class AddressSheetExample extends StatefulWidget { + const AddressSheetExample({Key? key}) : super(key: key); + + @override + State createState() => _AddressSheetExampleState(); +} + +class _AddressSheetExampleState extends State { + bool isCompleted = false; + + String? result; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + title: 'Addresssheet', + tags: ['Address sheet'], + padding: EdgeInsets.symmetric(horizontal: 16), + children: [ + AddressSheet( + onError: (error) { + setState(() { + result = error.error.localizedMessage; + }); + }, + onSubmit: (details) { + setState(() { + result = details.toJson().toPrettyString(); + }); + }, + params: AddressSheetParams(), + ), + Divider(), + SizedBox(height: 20), + ResponseCard( + response: result ?? '', + ), + ], + ); + } +} diff --git a/example/lib/screens/screens.dart b/example/lib/screens/screens.dart index 88e1f00d3..621e963b4 100644 --- a/example/lib/screens/screens.dart +++ b/example/lib/screens/screens.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:stripe_example/screens/address_sheet/address_sheet.dart'; import 'package:stripe_example/screens/customer_sheet/customer_sheet_screen.dart'; import 'package:stripe_example/screens/others/can_add_to_wallet_screen.dart'; import 'package:stripe_example/screens/payment_sheet/express_checkout/express_checkout_element.dart'; @@ -146,6 +147,13 @@ class Example extends StatelessWidget { ) ], ), + ExampleSection(title: 'Address sheet', children: [ + Example( + title: 'Address sheet', + builder: (context) => AddressSheetExample(), + platformsSupported: [DevicePlatform.android, DevicePlatform.ios], + ), + ]), ExampleSection(title: 'Customer sheet', children: [ Example( title: 'Customer sheet', diff --git a/packages/stripe/lib/flutter_stripe.dart b/packages/stripe/lib/flutter_stripe.dart index 1dde31389..62c348dbb 100644 --- a/packages/stripe/lib/flutter_stripe.dart +++ b/packages/stripe/lib/flutter_stripe.dart @@ -2,6 +2,7 @@ export 'package:stripe_platform_interface/stripe_platform_interface.dart'; export 'src/model/apple_pay_button.dart'; export 'src/stripe.dart'; +export 'src/widgets/adresssheet.dart'; // export 'src/widgets/apple_pay_button.dart'; export 'src/widgets/aubecs_debit_form.dart'; export 'src/widgets/card_field.dart'; diff --git a/packages/stripe/lib/src/widgets/adresssheet.dart b/packages/stripe/lib/src/widgets/adresssheet.dart index 40a5169c0..836cf1442 100644 --- a/packages/stripe/lib/src/widgets/adresssheet.dart +++ b/packages/stripe/lib/src/widgets/adresssheet.dart @@ -18,7 +18,7 @@ class AddressSheet extends StatelessWidget { final int height; /// Called when the user submits their information - final OnAddressSheetError onSubmit; + final OnAddressSheetSubmit onSubmit; /// Called when the user taps the button to close the sheet before submitting their information, or when an error occurs final OnAddressSheetError onError; @@ -31,7 +31,7 @@ class AddressSheet extends StatelessWidget { return _AddressSheet( onSubmit: onSubmit, onError: onError, - params: params, + addressSheetParams: params, height: height, ); } @@ -42,11 +42,11 @@ class _AddressSheet extends StatefulWidget { required this.onSubmit, required this.onError, required this.height, - required this.params, + required this.addressSheetParams, }); - final AddressSheetParams params; - final OnAddressSheetError onSubmit; + final AddressSheetParams addressSheetParams; + final OnAddressSheetSubmit onSubmit; final OnAddressSheetError onError; final int height; @@ -61,11 +61,23 @@ class _AddressSheetState extends State<_AddressSheet> { void onPlatformViewCreated(int viewId) { _methodChannel = MethodChannel('flutter.stripe/address_sheet/$viewId'); _methodChannel?.setMethodCallHandler((call) async { - if (call.method == 'onSubmit') { - print('blaat details ${call.arguments}'); - // widget.onSubmit.call(); - } else if (call.method == 'onError') { - print('blaat details ${call.arguments}'); + if (call.method == 'onSubmitAction') { + final tmp = Map.from(call.arguments as Map); + final tmpAdress = Map.from(tmp['address'] as Map); + + widget.onSubmit( + CollectAddressResult( + address: Address.fromJson(tmpAdress), + name: tmp['name'] as String, + phoneNumber: tmp['phone'] as String?, + ), + ); + } else if (call.method == 'onErrorAction') { + final tmp = Map.from(call.arguments as Map); + final foo = Map.from(tmp['error'] as Map); + + widget.onError( + StripeException(error: LocalizedErrorMessage.fromJson(foo))); } }); } @@ -94,7 +106,7 @@ class _AddressSheetState extends State<_AddressSheet> { id: params.id, viewType: _viewType, layoutDirection: TextDirection.ltr, - creationParams: {}, + creationParams: widget.addressSheetParams.toJson(), creationParamsCodec: const StandardMessageCodec(), ) ..addOnPlatformViewCreatedListener( diff --git a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt deleted file mode 100644 index 0c1a13afe..000000000 --- a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/AddressSheetPlatformView.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.flutter.stripe - -import android.content.Context -import android.view.View -import com.facebook.react.uimanager.ThemedReactContext -import com.reactnativestripesdk.StripeSdkModule -import com.reactnativestripesdk.addresssheet.AddressSheetView -import com.reactnativestripesdk.addresssheet.AddressSheetViewManager -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.platform.PlatformView - -class AddressSheetPlatformView( - private val context: Context, - private val channel: MethodChannel, - id: Int, - private val creationParams: Map?, - private val addressSheetManager: AddressSheetViewManager, - private val sdkAccessor: () -> StripeSdkModule - -) : PlatformView, MethodChannel.MethodCallHandler { - private val themedContext = ThemedReactContext(sdkAccessor().reactContext, channel, sdkAccessor) - - - lateinit var addressSheetView: AddressSheetView - - init { - - addressSheetView = addressSheetManager.createViewInstance(themedContext) - channel.setMethodCallHandler(this) - } - - override fun getView(): View? { - return addressSheetView - } - - override fun dispose() { - addressSheetManager.onDropViewInstance(addressSheetView) - } - - override fun onFlutterViewAttached(flutterView: View) { - addressSheetManager.onAfterUpdateTransaction(addressSheetView) - } - - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - - } - -} \ No newline at end of file diff --git a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformView.kt b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformView.kt new file mode 100644 index 000000000..d08a51e67 --- /dev/null +++ b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformView.kt @@ -0,0 +1,98 @@ +package com.flutter.stripe + +import android.content.Context +import android.view.View +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.uimanager.ThemedReactContext +import com.reactnativestripesdk.StripeSdkModule +import com.reactnativestripesdk.addresssheet.AddressSheetView +import com.reactnativestripesdk.addresssheet.AddressSheetViewManager +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.platform.PlatformView + +class StripeAddressSheetPlatformView( + private val context: Context, + private val channel: MethodChannel, + id: Int, + private val creationParams: Map?, + private val addressSheetManager: AddressSheetViewManager, + private val sdkAccessor: () -> StripeSdkModule + +) : PlatformView, MethodChannel.MethodCallHandler { + private val themedContext = ThemedReactContext(sdkAccessor().reactContext, channel, sdkAccessor) + + + lateinit var addressSheetView: AddressSheetView + + init { + + addressSheetView = addressSheetManager.createViewInstance(themedContext) + channel.setMethodCallHandler(this) + + if (creationParams?.containsKey("visible") == true) { + addressSheetManager.setVisible(addressSheetView, creationParams?.containsKey("visible") as Boolean) + } + + if (creationParams?.containsKey("appearance") == true) { + addressSheetManager.setAppearance( + addressSheetView, ReadableMap(creationParams["appearance"] as Map) + ) + } + + if (creationParams?.containsKey("defaultValues") == true) { + addressSheetManager.setDefaultValues( + addressSheetView, ReadableMap(creationParams["defaultValues"] as Map) + ) + } + + if (creationParams?.containsKey("additionalFields") == true) { + addressSheetManager.setAdditionalFields( + addressSheetView, ReadableMap(creationParams["additionalFields"] as Map) + ) + } + + if (creationParams?.containsKey("allowedCountries") == true) { + addressSheetManager.setAllowedCountries( + addressSheetView, ReadableArray(creationParams["allowedCountries"] as List) + ) + } + + if (creationParams?.containsKey("autocompleteCountries") == true) { + addressSheetManager.setAllowedCountries( + addressSheetView, ReadableArray(creationParams["autocompleteCountries"] as List) + ) + } + + if (creationParams?.containsKey("primaryButtonTitle") == true) { + addressSheetManager.setPrimaryButtonTitle(addressSheetView, creationParams?.containsKey("primaryButtonTitle") as String) + } + + if (creationParams?.containsKey("sheetTitle") == true) { + addressSheetManager.setPrimaryButtonTitle(addressSheetView, creationParams?.containsKey("sheetTitle") as String) + } + + if (creationParams?.containsKey("googlePlacesApiKey") == true) { + addressSheetManager.setPrimaryButtonTitle(addressSheetView, creationParams?.containsKey("googlePlacesApiKey") as String) + } + + } + + override fun getView(): View? { + return addressSheetView + } + + override fun dispose() { + addressSheetManager.onDropViewInstance(addressSheetView) + } + + override fun onFlutterViewAttached(flutterView: View) { + addressSheetManager.onAfterUpdateTransaction(addressSheetView) + } + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + + } + +} \ No newline at end of file diff --git a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt index 1bbbe5a6c..9ad0964d6 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeAddressSheetPlatformViewFactory.kt @@ -21,6 +21,6 @@ class StripeAddressSheetPlatformViewFactory( if(context == null){ throw AssertionError("Context is not allowed to be null when launching aubecs view.") } - return AddressSheetPlatformView(context,channel,viewId, creationParams, addresSheetViewManager, sdkAccessor) + return StripeAddressSheetPlatformView(context,channel,viewId, creationParams, addresSheetViewManager, sdkAccessor) } } \ No newline at end of file diff --git a/packages/stripe_platform_interface/lib/src/models/address_sheet.dart b/packages/stripe_platform_interface/lib/src/models/address_sheet.dart index b41e1197a..d8dc1b465 100644 --- a/packages/stripe_platform_interface/lib/src/models/address_sheet.dart +++ b/packages/stripe_platform_interface/lib/src/models/address_sheet.dart @@ -88,8 +88,7 @@ class CollectAddressResult with _$CollectAddressResult { typedef OnAddressSheetSubmit = FutureOr Function( CollectAddressResult result); -typedef OnAddressSheetError = FutureOr Function( - StripeError error); +typedef OnAddressSheetError = FutureOr Function(StripeException error); @freezed class AddressSheetParams with _$AddressSheetParams { From 9b7b321a630bd80a5b9c311f7bc4c093944dec81 Mon Sep 17 00:00:00 2001 From: Remon Date: Mon, 16 Dec 2024 17:06:41 +0000 Subject: [PATCH 4/7] feat: create ios addressheet implementation --- .../stripe/lib/src/widgets/adresssheet.dart | 4 +- .../ios/Classes/AddressSheetFactory.swift | 130 ++++++++++++++++++ .../stripe_ios/ios/Classes/StripePlugin.swift | 5 + 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 packages/stripe_ios/ios/Classes/AddressSheetFactory.swift diff --git a/packages/stripe/lib/src/widgets/adresssheet.dart b/packages/stripe/lib/src/widgets/adresssheet.dart index 836cf1442..932f194e1 100644 --- a/packages/stripe/lib/src/widgets/adresssheet.dart +++ b/packages/stripe/lib/src/widgets/adresssheet.dart @@ -89,7 +89,9 @@ class _AddressSheetState extends State<_AddressSheet> { child: defaultTargetPlatform == TargetPlatform.iOS ? UiKitView( viewType: _viewType, - creationParams: const StandardMessageCodec(), + creationParamsCodec: const StandardMessageCodec(), + creationParams: widget.addressSheetParams.toJson(), + onPlatformViewCreated: onPlatformViewCreated, ) : PlatformViewLink( surfaceFactory: (context, controller) { diff --git a/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift b/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift new file mode 100644 index 000000000..f6e8afd87 --- /dev/null +++ b/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift @@ -0,0 +1,130 @@ +// +// AddressSheetFactory.swift +// stripe_ios +// +// Created by Remon on 16/12/2024. +// + +import Foundation + +public class AddressSheetViewFactory: NSObject, FlutterPlatformViewFactory{ + + private var messenger: FlutterBinaryMessenger + private var delegate: ViewManagerDelegate + + init(messenger: FlutterBinaryMessenger, delegate: ViewManagerDelegate) { + self.messenger = messenger + self.delegate = delegate + super.init() + } + + public func create( + withFrame frame: CGRect, + viewIdentifier viewId: Int64, + arguments args: Any? + ) -> FlutterPlatformView { + let view = AddressSheetPlatformView( + frame: frame, + viewIdentifier: viewId, + arguments: args, + binaryMessenger: messenger) + return view + } + + public func createArgsCodec() -> any FlutterMessageCodec & NSObjectProtocol { + return FlutterStandardMessageCodec.sharedInstance() + } +} + +class AddressSheetPlatformView: NSObject, FlutterPlatformView { + let formView: AddressSheetView + + private let channel: FlutterMethodChannel + func view() -> UIView { + return formView + } + + init( frame: CGRect, + viewIdentifier viewId: Int64, + arguments args: Any?, + binaryMessenger messenger: FlutterBinaryMessenger){ + + channel = FlutterMethodChannel(name: "flutter.stripe/address_sheet/\(viewId)", + binaryMessenger: messenger) + + formView = AddressSheetView() + super.init() + channel.setMethodCallHandler(handle) + + formView.onSubmitAction = onCompleteAction + formView.onErrorAction = onCancelAction + + updateProps(args) + + } + + public func onCompleteAction(addressData: Dictionary?) { + channel.invokeMethod("onSubmitAction", arguments: addressData) + } + + public func onCancelAction(errorData: Dictionary?) { + channel.invokeMethod("onErrorAction", arguments: errorData) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + default: + result(FlutterMethodNotImplemented) + } + } + + func updateProps (_ args : Any? ) { + guard let arguments = args as? [String: Any] else { + return + } + + if let presentationStyle = arguments["presentationStyle"] as? String{ + formView.presentationStyle = presentationStyle + } + + if let animationStyle = arguments["animationStyle"] as? String{ + formView.animationStyle = animationStyle + } + + if let visible = arguments["visible"] as? Bool { + formView.visible = visible + } + + if let appearance = arguments["appearance"] as? NSDictionary { + formView.appearance = appearance + } + + if let defaultValues = arguments["defaultValues"] as? NSDictionary { + formView.defaultValues = defaultValues + } + + if let additionalFields = arguments["additionalFields"] as? NSDictionary { + formView.additionalFields = additionalFields + } + + if let allowedCountries = arguments["allowedCountries"] as? [String] { + formView.allowedCountries = allowedCountries + } + + if let autocompleteCountries = arguments["autocompleteCountries"] as? [String] { + formView.autocompleteCountries = autocompleteCountries + } + + if let primaryButtonTitle = arguments["primaryButtonTitle"] as? String { + formView.primaryButtonTitle = primaryButtonTitle + } + + if let sheetTitle = arguments["sheetTitle"] as? String { + formView.sheetTitle = sheetTitle + } + + formView.didSetProps(nil) + + + } +} diff --git a/packages/stripe_ios/ios/Classes/StripePlugin.swift b/packages/stripe_ios/ios/Classes/StripePlugin.swift index e92fc950a..f9ccede51 100644 --- a/packages/stripe_ios/ios/Classes/StripePlugin.swift +++ b/packages/stripe_ios/ios/Classes/StripePlugin.swift @@ -42,6 +42,11 @@ class StripePlugin: StripeSdk, FlutterPlugin, ViewManagerDelegate { // Apple Pay Button let applePayFactory = ApplePayButtonViewFactory(messenger: registrar.messenger(),stripeSdk: instance) registrar.register(applePayFactory, withId: "flutter.stripe/apple_pay") + + // Addressheet + let addressSheetFactory = AddressSheetViewFactory(messenger: registrar.messenger(), delegate: instance) + registrar.register(addressSheetFactory, withId: "flutter.stripe/address_sheet") + } init(channel : FlutterMethodChannel) { From ffcba780072fc7efbdd787d5b48b350c4c460e3a Mon Sep 17 00:00:00 2001 From: Remon Date: Sun, 22 Dec 2024 14:20:21 +0000 Subject: [PATCH 5/7] fix auto close ios addressheet --- .../screens/address_sheet/address_sheet.dart | 2 +- packages/stripe/lib/flutter_stripe.dart | 2 +- .../{adresssheet.dart => adress_sheet.dart} | 71 ++++++++----------- .../ios/Classes/AddressSheetFactory.swift | 4 ++ 4 files changed, 36 insertions(+), 43 deletions(-) rename packages/stripe/lib/src/widgets/{adresssheet.dart => adress_sheet.dart} (60%) diff --git a/example/lib/screens/address_sheet/address_sheet.dart b/example/lib/screens/address_sheet/address_sheet.dart index a8c6854df..e51976701 100644 --- a/example/lib/screens/address_sheet/address_sheet.dart +++ b/example/lib/screens/address_sheet/address_sheet.dart @@ -5,7 +5,7 @@ import 'package:stripe_example/widgets/example_scaffold.dart'; import 'package:stripe_example/widgets/response_card.dart'; class AddressSheetExample extends StatefulWidget { - const AddressSheetExample({Key? key}) : super(key: key); + const AddressSheetExample({super.key}); @override State createState() => _AddressSheetExampleState(); diff --git a/packages/stripe/lib/flutter_stripe.dart b/packages/stripe/lib/flutter_stripe.dart index 62c348dbb..98acdb688 100644 --- a/packages/stripe/lib/flutter_stripe.dart +++ b/packages/stripe/lib/flutter_stripe.dart @@ -2,7 +2,7 @@ export 'package:stripe_platform_interface/stripe_platform_interface.dart'; export 'src/model/apple_pay_button.dart'; export 'src/stripe.dart'; -export 'src/widgets/adresssheet.dart'; +export 'src/widgets/adress_sheet.dart'; // export 'src/widgets/apple_pay_button.dart'; export 'src/widgets/aubecs_debit_form.dart'; export 'src/widgets/card_field.dart'; diff --git a/packages/stripe/lib/src/widgets/adresssheet.dart b/packages/stripe/lib/src/widgets/adress_sheet.dart similarity index 60% rename from packages/stripe/lib/src/widgets/adresssheet.dart rename to packages/stripe/lib/src/widgets/adress_sheet.dart index 932f194e1..577d5e024 100644 --- a/packages/stripe/lib/src/widgets/adresssheet.dart +++ b/packages/stripe/lib/src/widgets/adress_sheet.dart @@ -10,13 +10,9 @@ class AddressSheet extends StatelessWidget { required this.onSubmit, required this.onError, required this.params, - this.height = 300, super.key, }); - /// The height of the address sheet - final int height; - /// Called when the user submits their information final OnAddressSheetSubmit onSubmit; @@ -32,7 +28,6 @@ class AddressSheet extends StatelessWidget { onSubmit: onSubmit, onError: onError, addressSheetParams: params, - height: height, ); } } @@ -41,14 +36,12 @@ class _AddressSheet extends StatefulWidget { const _AddressSheet({ required this.onSubmit, required this.onError, - required this.height, required this.addressSheetParams, }); final AddressSheetParams addressSheetParams; final OnAddressSheetSubmit onSubmit; final OnAddressSheetError onError; - final int height; @override State<_AddressSheet> createState() => _AddressSheetState(); @@ -84,39 +77,35 @@ class _AddressSheetState extends State<_AddressSheet> { @override Widget build(BuildContext context) { - return SizedBox( - height: widget.height.toDouble(), - child: defaultTargetPlatform == TargetPlatform.iOS - ? UiKitView( - viewType: _viewType, - creationParamsCodec: const StandardMessageCodec(), - creationParams: widget.addressSheetParams.toJson(), - onPlatformViewCreated: onPlatformViewCreated, - ) - : PlatformViewLink( - surfaceFactory: (context, controller) { - return AndroidViewSurface( - controller: controller as AndroidViewController, - hitTestBehavior: PlatformViewHitTestBehavior.opaque, - gestureRecognizers: const >{}, - ); - }, - onCreatePlatformView: (params) { - onPlatformViewCreated(params.id); - return PlatformViewsService.initExpensiveAndroidView( - id: params.id, - viewType: _viewType, - layoutDirection: TextDirection.ltr, - creationParams: widget.addressSheetParams.toJson(), - creationParamsCodec: const StandardMessageCodec(), - ) - ..addOnPlatformViewCreatedListener( - params.onPlatformViewCreated) - ..create(); - }, - viewType: _viewType, - ), - ); + return defaultTargetPlatform == TargetPlatform.iOS + ? UiKitView( + viewType: _viewType, + creationParamsCodec: const StandardMessageCodec(), + creationParams: widget.addressSheetParams.toJson(), + onPlatformViewCreated: onPlatformViewCreated, + ) + : PlatformViewLink( + surfaceFactory: (context, controller) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + gestureRecognizers: const >{}, + ); + }, + onCreatePlatformView: (params) { + onPlatformViewCreated(params.id); + return PlatformViewsService.initExpensiveAndroidView( + id: params.id, + viewType: _viewType, + layoutDirection: TextDirection.ltr, + creationParams: widget.addressSheetParams.toJson(), + creationParamsCodec: const StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + viewType: _viewType, + ); } } diff --git a/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift b/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift index f6e8afd87..fa4f299cc 100644 --- a/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift +++ b/packages/stripe_ios/ios/Classes/AddressSheetFactory.swift @@ -65,10 +65,14 @@ class AddressSheetPlatformView: NSObject, FlutterPlatformView { public func onCompleteAction(addressData: Dictionary?) { channel.invokeMethod("onSubmitAction", arguments: addressData) + formView.visible=false + formView.didSetProps(["visible"]) } public func onCancelAction(errorData: Dictionary?) { channel.invokeMethod("onErrorAction", arguments: errorData) + formView.visible=false + formView.didSetProps(["visible"]) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { From aa09c1059e3a461d9a0f52161834239e66b842a4 Mon Sep 17 00:00:00 2001 From: Remon Date: Sun, 22 Dec 2024 14:30:22 +0000 Subject: [PATCH 6/7] fix: give widget small height to fix flutter requirements --- .../stripe/lib/src/widgets/adress_sheet.dart | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/packages/stripe/lib/src/widgets/adress_sheet.dart b/packages/stripe/lib/src/widgets/adress_sheet.dart index 577d5e024..5583748d0 100644 --- a/packages/stripe/lib/src/widgets/adress_sheet.dart +++ b/packages/stripe/lib/src/widgets/adress_sheet.dart @@ -77,35 +77,39 @@ class _AddressSheetState extends State<_AddressSheet> { @override Widget build(BuildContext context) { - return defaultTargetPlatform == TargetPlatform.iOS - ? UiKitView( - viewType: _viewType, - creationParamsCodec: const StandardMessageCodec(), - creationParams: widget.addressSheetParams.toJson(), - onPlatformViewCreated: onPlatformViewCreated, - ) - : PlatformViewLink( - surfaceFactory: (context, controller) { - return AndroidViewSurface( - controller: controller as AndroidViewController, - hitTestBehavior: PlatformViewHitTestBehavior.opaque, - gestureRecognizers: const >{}, - ); - }, - onCreatePlatformView: (params) { - onPlatformViewCreated(params.id); - return PlatformViewsService.initExpensiveAndroidView( - id: params.id, - viewType: _viewType, - layoutDirection: TextDirection.ltr, - creationParams: widget.addressSheetParams.toJson(), - creationParamsCodec: const StandardMessageCodec(), - ) - ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) - ..create(); - }, - viewType: _viewType, - ); + return SizedBox( + height: 10, + child: defaultTargetPlatform == TargetPlatform.iOS + ? UiKitView( + viewType: _viewType, + creationParamsCodec: const StandardMessageCodec(), + creationParams: widget.addressSheetParams.toJson(), + onPlatformViewCreated: onPlatformViewCreated, + ) + : PlatformViewLink( + surfaceFactory: (context, controller) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + gestureRecognizers: const >{}, + ); + }, + onCreatePlatformView: (params) { + onPlatformViewCreated(params.id); + return PlatformViewsService.initExpensiveAndroidView( + id: params.id, + viewType: _viewType, + layoutDirection: TextDirection.ltr, + creationParams: widget.addressSheetParams.toJson(), + creationParamsCodec: const StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener( + params.onPlatformViewCreated) + ..create(); + }, + viewType: _viewType, + ), + ); } } From 0f340c6b105c473000ace045cf895cfa16023b38 Mon Sep 17 00:00:00 2001 From: Remon Date: Sun, 22 Dec 2024 14:41:21 +0000 Subject: [PATCH 7/7] chore: add documentation --- docs.json | 1 + docs/address-sheet.mdx | 167 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 docs/address-sheet.mdx diff --git a/docs.json b/docs.json index f3e810e01..f61d807af 100644 --- a/docs.json +++ b/docs.json @@ -12,6 +12,7 @@ ] ], ["Mobile elements", + ["Address Sheet", "/address_sheet"], ["Customer Sheet", "/customer_sheet"] ], ["Regional payments", [ diff --git a/docs/address-sheet.mdx b/docs/address-sheet.mdx new file mode 100644 index 000000000..078190d94 --- /dev/null +++ b/docs/address-sheet.mdx @@ -0,0 +1,167 @@ +# Address Sheet Implementation Guide + +The Address Sheet is a pre-built UI component that allows you to collect shipping or billing addresses from your users in a Flutter application using Stripe's native UI components. + +## Installation + +Make sure you have the `flutter_stripe` package installed in your `pubspec.yaml`: + +```yaml +dependencies: + flutter_stripe: ^latest_version +``` + +### Set up Stripe [Server Side] [Client Side] + +First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). + +#### Server-side + +This integration requires endpoints on your server that talk to the Stripe API. Use one official libraries for access to the Stripe API from your server. [Follow the steps here](https://stripe.com/docs/payments/accept-a-payment?platform=ios&ui=payment-sheet#setup-server-side) + +#### Client-side + +The Flutter SDK is open source, fully documented. + +To install the SDK, follow these steps: + - Run the commmand `flutter pub add flutter_stripe` + - This will add a line like this to your project's pubspec.yaml with the latest package version + + +For details on the latest SDK release and past versions, see the [Releases](https://github.com/flutter-stripe/flutter_stripe/releases) page on GitHub. To receive notifications when a new release is published, [watch releases for the repository](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/viewing-your-subscriptions#watching-releases-for-a-repository). + + +When your app starts, configure the SDK with your Stripe [publishable key](https://dashboard.stripe.com/) so that it can make requests to the Stripe API. + +```dart +void main() async { + Stripe.publishableKey = stripePublishableKey; + runApp(const App()); +} +``` + +Use your [test mode](https://stripe.com/docs/keys#obtain-api-keys) keys while you test and develop, and your [live mode](https://stripe.com/docs/keys#test-live-modes) keys when you publish your app. + + +### Basic Implementation + +Here's a basic example of how to implement the Address Sheet: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_stripe/flutter_stripe.dart'; + +AddressSheet( + onSubmit: (details) { + // Handle the submitted address details + print(details.toJson()); + }, + onError: (error) { + // Handle any errors that occur + print(error.error.localizedMessage); + }, + params: AddressSheetParams(), +) +``` + +## Parameters + +### Required Parameters + +- `onSubmit`: Callback function that is called when the user successfully submits their address information. Receives a `CollectAddressResult` object containing: + - `address`: The collected address details + - `name`: The customer's name + - `phoneNumber`: The customer's phone number (optional) + +- `onError`: Callback function that is called when an error occurs or when the user closes the sheet before submitting. Receives a `StripeException` object. + +- `params`: An `AddressSheetParams` object that configures the address sheet behavior and appearance. + +### Address Result Structure + +The `CollectAddressResult` object contains the following information: + +```dart +class CollectAddressResult { + final Address address; + final String name; + final String? phoneNumber; +} +``` + +The `Address` object contains standard address fields like street, city, state, postal code, and country. + +## Example Implementation + +Here's a complete example showing how to implement the Address Sheet in a Flutter screen: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_stripe/flutter_stripe.dart'; + +class AddressSheetExample extends StatefulWidget { + const AddressSheetExample({super.key}); + + @override + State createState() => _AddressSheetExampleState(); +} + +class _AddressSheetExampleState extends State { + String? result; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Address Sheet Example'), + ), + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + AddressSheet( + onError: (error) { + setState(() { + result = error.error.localizedMessage; + }); + }, + onSubmit: (details) { + setState(() { + result = details.toJson().toString(); + }); + }, + params: AddressSheetParams(), + ), + SizedBox(height: 20), + Text(result ?? ''), + ], + ), + ), + ); + } +} +``` + +### Customization + +You can customize the Address Sheet behavior by configuring the `AddressSheetParams`. This allows you to: +- Set default values +- Configure which fields are required +- Customize the appearance +- Set specific country restrictions + +## Platform Support + +The Address Sheet is supported on both iOS and Android platforms, providing a native UI experience on each platform. + +## Best Practices + +1. Always handle both the `onSubmit` and `onError` callbacks to ensure a good user experience. +2. Validate the collected address information before using it in your application. +3. Consider implementing proper error handling and display appropriate error messages to users. +4. Store the collected address information securely if you need to reuse it later. + +## Related Resources + +- [Stripe Documentation](https://stripe.com/docs) +- [Flutter Stripe Package](https://pub.dev/packages/flutter_stripe)