Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address sheet #2020

Merged
merged 7 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
]
],
["Mobile elements",
["Address Sheet", "/address_sheet"],
["Customer Sheet", "/customer_sheet"]
],
["Regional payments", [
Expand Down
167 changes: 167 additions & 0 deletions docs/address-sheet.mdx
Original file line number Diff line number Diff line change
@@ -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`

Check warning on line 27 in docs/address-sheet.mdx

View workflow job for this annotation

GitHub Actions / Typo CI

commmand

"commmand" is a typo. Did you mean "command"?
- 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<AddressSheetExample> createState() => _AddressSheetExampleState();
}

class _AddressSheetExampleState extends State<AddressSheetExample> {
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)
57 changes: 57 additions & 0 deletions example/lib/screens/address_sheet/address_sheet.dart
Original file line number Diff line number Diff line change
@@ -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({super.key});

@override
State<AddressSheetExample> createState() => _AddressSheetExampleState();
}

class _AddressSheetExampleState extends State<AddressSheetExample> {
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 ?? '',
),
],
);
}
}
8 changes: 8 additions & 0 deletions example/lib/screens/screens.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions packages/stripe/lib/flutter_stripe.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/adress_sheet.dart';
// export 'src/widgets/apple_pay_button.dart';
export 'src/widgets/aubecs_debit_form.dart';
export 'src/widgets/card_field.dart';
Expand Down
115 changes: 115 additions & 0 deletions packages/stripe/lib/src/widgets/adress_sheet.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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,
super.key,
});

/// Called when the user submits their information
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;

/// The parameters for the address sheet
final AddressSheetParams params;

@override
Widget build(BuildContext context) {
return _AddressSheet(
onSubmit: onSubmit,
onError: onError,
addressSheetParams: params,
);
}
}

class _AddressSheet extends StatefulWidget {
const _AddressSheet({
required this.onSubmit,
required this.onError,
required this.addressSheetParams,
});

final AddressSheetParams addressSheetParams;
final OnAddressSheetSubmit onSubmit;
final OnAddressSheetError onError;

@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 == 'onSubmitAction') {
final tmp = Map<String, dynamic>.from(call.arguments as Map);
final tmpAdress = Map<String, dynamic>.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<String, dynamic>.from(call.arguments as Map);
final foo = Map<String, dynamic>.from(tmp['error'] as Map);

widget.onError(
StripeException(error: LocalizedErrorMessage.fromJson(foo)));
}
});
}

@override
Widget build(BuildContext context) {
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 <Factory<
OneSequenceGestureRecognizer>>{},
);
},
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,
),
);
}
}
Loading
Loading