Skip to content

Commit 03f8741

Browse files
Resolve swiftpm conflicts (#2041)
* Address sheet (#2020) * feat: create android binding for addresssheet * feat: create addressheet model * feat: add correct mapping for addressheet * feat: create ios addressheet implementation * fix auto close ios addressheet * fix: give widget small height to fix flutter requirements * chore: add documentation --------- Co-authored-by: Remon <remonhelmond+dev@gmail.com> * chore: added missing imports --------- Co-authored-by: Rémon <remonhelmond@gmail.com> Co-authored-by: Remon <remonhelmond+dev@gmail.com>
1 parent f1a8ad2 commit 03f8741

File tree

16 files changed

+1770
-0
lines changed

16 files changed

+1770
-0
lines changed

docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
]
1313
],
1414
["Mobile elements",
15+
["Address Sheet", "/address_sheet"],
1516
["Customer Sheet", "/customer_sheet"]
1617
],
1718
["Regional payments", [

docs/address-sheet.mdx

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Address Sheet Implementation Guide
2+
3+
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.
4+
5+
## Installation
6+
7+
Make sure you have the `flutter_stripe` package installed in your `pubspec.yaml`:
8+
9+
```yaml
10+
dependencies:
11+
flutter_stripe: ^latest_version
12+
```
13+
14+
### Set up Stripe [Server Side] [Client Side]
15+
16+
First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register).
17+
18+
#### Server-side
19+
20+
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)
21+
22+
#### Client-side
23+
24+
The Flutter SDK is open source, fully documented.
25+
26+
To install the SDK, follow these steps:
27+
- Run the commmand `flutter pub add flutter_stripe`
28+
- This will add a line like this to your project's pubspec.yaml with the latest package version
29+
30+
31+
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).
32+
33+
34+
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.
35+
36+
```dart
37+
void main() async {
38+
Stripe.publishableKey = stripePublishableKey;
39+
runApp(const App());
40+
}
41+
```
42+
43+
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.
44+
45+
46+
### Basic Implementation
47+
48+
Here's a basic example of how to implement the Address Sheet:
49+
50+
```dart
51+
import 'package:flutter/material.dart';
52+
import 'package:flutter_stripe/flutter_stripe.dart';
53+
54+
AddressSheet(
55+
onSubmit: (details) {
56+
// Handle the submitted address details
57+
print(details.toJson());
58+
},
59+
onError: (error) {
60+
// Handle any errors that occur
61+
print(error.error.localizedMessage);
62+
},
63+
params: AddressSheetParams(),
64+
)
65+
```
66+
67+
## Parameters
68+
69+
### Required Parameters
70+
71+
- `onSubmit`: Callback function that is called when the user successfully submits their address information. Receives a `CollectAddressResult` object containing:
72+
- `address`: The collected address details
73+
- `name`: The customer's name
74+
- `phoneNumber`: The customer's phone number (optional)
75+
76+
- `onError`: Callback function that is called when an error occurs or when the user closes the sheet before submitting. Receives a `StripeException` object.
77+
78+
- `params`: An `AddressSheetParams` object that configures the address sheet behavior and appearance.
79+
80+
### Address Result Structure
81+
82+
The `CollectAddressResult` object contains the following information:
83+
84+
```dart
85+
class CollectAddressResult {
86+
final Address address;
87+
final String name;
88+
final String? phoneNumber;
89+
}
90+
```
91+
92+
The `Address` object contains standard address fields like street, city, state, postal code, and country.
93+
94+
## Example Implementation
95+
96+
Here's a complete example showing how to implement the Address Sheet in a Flutter screen:
97+
98+
```dart
99+
import 'package:flutter/material.dart';
100+
import 'package:flutter_stripe/flutter_stripe.dart';
101+
102+
class AddressSheetExample extends StatefulWidget {
103+
const AddressSheetExample({super.key});
104+
105+
@override
106+
State<AddressSheetExample> createState() => _AddressSheetExampleState();
107+
}
108+
109+
class _AddressSheetExampleState extends State<AddressSheetExample> {
110+
String? result;
111+
112+
@override
113+
Widget build(BuildContext context) {
114+
return Scaffold(
115+
appBar: AppBar(
116+
title: Text('Address Sheet Example'),
117+
),
118+
body: Padding(
119+
padding: EdgeInsets.symmetric(horizontal: 16),
120+
child: Column(
121+
children: [
122+
AddressSheet(
123+
onError: (error) {
124+
setState(() {
125+
result = error.error.localizedMessage;
126+
});
127+
},
128+
onSubmit: (details) {
129+
setState(() {
130+
result = details.toJson().toString();
131+
});
132+
},
133+
params: AddressSheetParams(),
134+
),
135+
SizedBox(height: 20),
136+
Text(result ?? ''),
137+
],
138+
),
139+
),
140+
);
141+
}
142+
}
143+
```
144+
145+
### Customization
146+
147+
You can customize the Address Sheet behavior by configuring the `AddressSheetParams`. This allows you to:
148+
- Set default values
149+
- Configure which fields are required
150+
- Customize the appearance
151+
- Set specific country restrictions
152+
153+
## Platform Support
154+
155+
The Address Sheet is supported on both iOS and Android platforms, providing a native UI experience on each platform.
156+
157+
## Best Practices
158+
159+
1. Always handle both the `onSubmit` and `onError` callbacks to ensure a good user experience.
160+
2. Validate the collected address information before using it in your application.
161+
3. Consider implementing proper error handling and display appropriate error messages to users.
162+
4. Store the collected address information securely if you need to reuse it later.
163+
164+
## Related Resources
165+
166+
- [Stripe Documentation](https://stripe.com/docs)
167+
- [Flutter Stripe Package](https://pub.dev/packages/flutter_stripe)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_stripe/flutter_stripe.dart';
3+
import 'package:stripe_example/utils.dart';
4+
import 'package:stripe_example/widgets/example_scaffold.dart';
5+
import 'package:stripe_example/widgets/response_card.dart';
6+
7+
class AddressSheetExample extends StatefulWidget {
8+
const AddressSheetExample({super.key});
9+
10+
@override
11+
State<AddressSheetExample> createState() => _AddressSheetExampleState();
12+
}
13+
14+
class _AddressSheetExampleState extends State<AddressSheetExample> {
15+
bool isCompleted = false;
16+
17+
String? result;
18+
19+
@override
20+
void initState() {
21+
super.initState();
22+
}
23+
24+
@override
25+
void dispose() {
26+
super.dispose();
27+
}
28+
29+
@override
30+
Widget build(BuildContext context) {
31+
return ExampleScaffold(
32+
title: 'Addresssheet',
33+
tags: ['Address sheet'],
34+
padding: EdgeInsets.symmetric(horizontal: 16),
35+
children: [
36+
AddressSheet(
37+
onError: (error) {
38+
setState(() {
39+
result = error.error.localizedMessage;
40+
});
41+
},
42+
onSubmit: (details) {
43+
setState(() {
44+
result = details.toJson().toPrettyString();
45+
});
46+
},
47+
params: AddressSheetParams(),
48+
),
49+
Divider(),
50+
SizedBox(height: 20),
51+
ResponseCard(
52+
response: result ?? '',
53+
),
54+
],
55+
);
56+
}
57+
}

example/lib/screens/screens.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:stripe_example/screens/address_sheet/address_sheet.dart';
23
import 'package:stripe_example/screens/customer_sheet/customer_sheet_screen.dart';
34
import 'package:stripe_example/screens/others/can_add_to_wallet_screen.dart';
45
import 'package:stripe_example/screens/payment_sheet/express_checkout/express_checkout_element.dart';
@@ -146,6 +147,13 @@ class Example extends StatelessWidget {
146147
)
147148
],
148149
),
150+
ExampleSection(title: 'Address sheet', children: [
151+
Example(
152+
title: 'Address sheet',
153+
builder: (context) => AddressSheetExample(),
154+
platformsSupported: [DevicePlatform.android, DevicePlatform.ios],
155+
),
156+
]),
149157
ExampleSection(title: 'Customer sheet', children: [
150158
Example(
151159
title: 'Customer sheet',

packages/stripe/lib/flutter_stripe.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export 'package:stripe_platform_interface/stripe_platform_interface.dart';
22

33
export 'src/model/apple_pay_button.dart';
44
export 'src/stripe.dart';
5+
export 'src/widgets/adress_sheet.dart';
56
// export 'src/widgets/apple_pay_button.dart';
67
export 'src/widgets/aubecs_debit_form.dart';
78
export 'src/widgets/card_field.dart';
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter/gestures.dart';
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter/rendering.dart';
5+
import 'package:flutter/services.dart';
6+
import 'package:flutter_stripe/flutter_stripe.dart';
7+
8+
class AddressSheet extends StatelessWidget {
9+
const AddressSheet({
10+
required this.onSubmit,
11+
required this.onError,
12+
required this.params,
13+
super.key,
14+
});
15+
16+
/// Called when the user submits their information
17+
final OnAddressSheetSubmit onSubmit;
18+
19+
/// Called when the user taps the button to close the sheet before submitting their information, or when an error occurs
20+
final OnAddressSheetError onError;
21+
22+
/// The parameters for the address sheet
23+
final AddressSheetParams params;
24+
25+
@override
26+
Widget build(BuildContext context) {
27+
return _AddressSheet(
28+
onSubmit: onSubmit,
29+
onError: onError,
30+
addressSheetParams: params,
31+
);
32+
}
33+
}
34+
35+
class _AddressSheet extends StatefulWidget {
36+
const _AddressSheet({
37+
required this.onSubmit,
38+
required this.onError,
39+
required this.addressSheetParams,
40+
});
41+
42+
final AddressSheetParams addressSheetParams;
43+
final OnAddressSheetSubmit onSubmit;
44+
final OnAddressSheetError onError;
45+
46+
@override
47+
State<_AddressSheet> createState() => _AddressSheetState();
48+
}
49+
50+
class _AddressSheetState extends State<_AddressSheet> {
51+
static const _viewType = 'flutter.stripe/address_sheet';
52+
MethodChannel? _methodChannel;
53+
54+
void onPlatformViewCreated(int viewId) {
55+
_methodChannel = MethodChannel('flutter.stripe/address_sheet/$viewId');
56+
_methodChannel?.setMethodCallHandler((call) async {
57+
if (call.method == 'onSubmitAction') {
58+
final tmp = Map<String, dynamic>.from(call.arguments as Map);
59+
final tmpAdress = Map<String, dynamic>.from(tmp['address'] as Map);
60+
61+
widget.onSubmit(
62+
CollectAddressResult(
63+
address: Address.fromJson(tmpAdress),
64+
name: tmp['name'] as String,
65+
phoneNumber: tmp['phone'] as String?,
66+
),
67+
);
68+
} else if (call.method == 'onErrorAction') {
69+
final tmp = Map<String, dynamic>.from(call.arguments as Map);
70+
final foo = Map<String, dynamic>.from(tmp['error'] as Map);
71+
72+
widget.onError(
73+
StripeException(error: LocalizedErrorMessage.fromJson(foo)));
74+
}
75+
});
76+
}
77+
78+
@override
79+
Widget build(BuildContext context) {
80+
return SizedBox(
81+
height: 10,
82+
child: defaultTargetPlatform == TargetPlatform.iOS
83+
? UiKitView(
84+
viewType: _viewType,
85+
creationParamsCodec: const StandardMessageCodec(),
86+
creationParams: widget.addressSheetParams.toJson(),
87+
onPlatformViewCreated: onPlatformViewCreated,
88+
)
89+
: PlatformViewLink(
90+
surfaceFactory: (context, controller) {
91+
return AndroidViewSurface(
92+
controller: controller as AndroidViewController,
93+
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
94+
gestureRecognizers: const <Factory<
95+
OneSequenceGestureRecognizer>>{},
96+
);
97+
},
98+
onCreatePlatformView: (params) {
99+
onPlatformViewCreated(params.id);
100+
return PlatformViewsService.initExpensiveAndroidView(
101+
id: params.id,
102+
viewType: _viewType,
103+
layoutDirection: TextDirection.ltr,
104+
creationParams: widget.addressSheetParams.toJson(),
105+
creationParamsCodec: const StandardMessageCodec(),
106+
)
107+
..addOnPlatformViewCreatedListener(
108+
params.onPlatformViewCreated)
109+
..create();
110+
},
111+
viewType: _viewType,
112+
),
113+
);
114+
}
115+
}

0 commit comments

Comments
 (0)