Skip to content

Commit f285899

Browse files
authored
feat(authenticator): Add TextEditingController support to form fields (#6424)
1 parent ae22ffb commit f285899

18 files changed

+2021
-60
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
5+
import 'package:amplify_authenticator/amplify_authenticator.dart';
6+
import 'package:amplify_flutter/amplify_flutter.dart';
7+
import 'package:flutter/material.dart';
8+
9+
import 'amplifyconfiguration.dart';
10+
11+
/// Example demonstrating the use of TextEditingController with
12+
/// Amplify Authenticator form fields.
13+
///
14+
/// This allows programmatic control over form field values, enabling
15+
/// use cases such as:
16+
/// - Pre-populating fields with data from APIs (e.g., GPS location)
17+
/// - Auto-filling verification codes from SMS
18+
/// - Dynamic form validation and manipulation
19+
class AuthenticatorWithControllers extends StatefulWidget {
20+
const AuthenticatorWithControllers({super.key});
21+
22+
@override
23+
State<AuthenticatorWithControllers> createState() =>
24+
_AuthenticatorWithControllersState();
25+
}
26+
27+
class _AuthenticatorWithControllersState
28+
extends State<AuthenticatorWithControllers> {
29+
// Controllers for programmatic access to form fields
30+
final _usernameController = AuthenticatorTextFieldController();
31+
final _emailController = AuthenticatorTextFieldController();
32+
final _addressController = AuthenticatorTextFieldController();
33+
final _phoneController = AuthenticatorTextFieldController();
34+
35+
@override
36+
void initState() {
37+
super.initState();
38+
_configureAmplify();
39+
40+
// Example: Pre-populate fields with default/fetched data
41+
_usernameController.text = 'amplify_user';
42+
_emailController.text = 'user@amplify.example.com';
43+
}
44+
45+
@override
46+
void dispose() {
47+
// Clean up controllers when the widget is disposed
48+
_usernameController.dispose();
49+
_emailController.dispose();
50+
_addressController.dispose();
51+
_phoneController.dispose();
52+
super.dispose();
53+
}
54+
55+
void _configureAmplify() async {
56+
final authPlugin = AmplifyAuthCognito(
57+
// FIXME: In your app, make sure to remove this line and set up
58+
/// Keychain Sharing in Xcode as described in the docs:
59+
/// https://docs.amplify.aws/lib/project-setup/platform-setup/q/platform/flutter/#enable-keychain
60+
secureStorageFactory: AmplifySecureStorage.factoryFrom(
61+
macOSOptions:
62+
// ignore: invalid_use_of_visible_for_testing_member
63+
MacOSSecureStorageOptions(useDataProtection: false),
64+
),
65+
);
66+
try {
67+
await Amplify.addPlugin(authPlugin);
68+
await Amplify.configure(amplifyconfig);
69+
safePrint('Successfully configured');
70+
} on Exception catch (e) {
71+
safePrint('Error configuring Amplify: $e');
72+
}
73+
}
74+
75+
/// Simulates fetching user location and populating the address field
76+
Future<void> _fetchAndPopulateAddress() async {
77+
// In a real app, you would use a geolocation service here
78+
await Future<void>.delayed(const Duration(seconds: 1));
79+
80+
// Simulate fetched address
81+
final fetchedAddress = '123 Main Street, Seattle, WA 98101';
82+
83+
// Update the address field programmatically
84+
_addressController.text = fetchedAddress;
85+
86+
if (mounted) {
87+
ScaffoldMessenger.of(context).showSnackBar(
88+
SnackBar(content: Text('Address populated: $fetchedAddress')),
89+
);
90+
}
91+
}
92+
93+
@override
94+
Widget build(BuildContext context) {
95+
return Authenticator(
96+
// Custom sign-up form with controller support
97+
signUpForm: SignUpForm.custom(
98+
fields: [
99+
// Username field with controller - can be pre-populated or modified
100+
SignUpFormField.username(
101+
authenticatorTextFieldController: _usernameController,
102+
),
103+
104+
// Email field with controller
105+
SignUpFormField.email(
106+
authenticatorTextFieldController: _emailController,
107+
required: true,
108+
),
109+
110+
SignUpFormField.password(),
111+
SignUpFormField.passwordConfirmation(),
112+
113+
// Address field with controller - can be populated from GPS/API
114+
SignUpFormField.address(
115+
authenticatorTextFieldController: _addressController,
116+
),
117+
118+
// Phone number field with controller
119+
SignUpFormField.phoneNumber(
120+
authenticatorTextFieldController: _phoneController,
121+
),
122+
],
123+
),
124+
125+
child: MaterialApp(
126+
title: 'Authenticator with Controllers',
127+
theme: ThemeData.light(useMaterial3: true),
128+
darkTheme: ThemeData.dark(useMaterial3: true),
129+
debugShowCheckedModeBanner: false,
130+
builder: Authenticator.builder(),
131+
home: Scaffold(
132+
appBar: AppBar(title: const Text('Controller Example')),
133+
body: Center(
134+
child: Column(
135+
mainAxisAlignment: MainAxisAlignment.center,
136+
children: [
137+
const Text(
138+
'You are logged in!',
139+
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
140+
),
141+
const SizedBox(height: 20),
142+
143+
// Display current controller values
144+
Card(
145+
margin: const EdgeInsets.all(16),
146+
child: Padding(
147+
padding: const EdgeInsets.all(16),
148+
child: Column(
149+
crossAxisAlignment: CrossAxisAlignment.start,
150+
children: [
151+
const Text(
152+
'Form Field Values:',
153+
style: TextStyle(
154+
fontSize: 18,
155+
fontWeight: FontWeight.bold,
156+
),
157+
),
158+
const SizedBox(height: 10),
159+
Text('Username: ${_usernameController.text}'),
160+
Text('Email: ${_emailController.text}'),
161+
Text('Address: ${_addressController.text}'),
162+
Text('Phone: ${_phoneController.text}'),
163+
],
164+
),
165+
),
166+
),
167+
168+
const SizedBox(height: 20),
169+
170+
ElevatedButton.icon(
171+
onPressed: _fetchAndPopulateAddress,
172+
icon: const Icon(Icons.location_on),
173+
label: const Text('Fetch GPS Address'),
174+
),
175+
176+
const SizedBox(height: 20),
177+
const SignOutButton(),
178+
],
179+
),
180+
),
181+
),
182+
),
183+
);
184+
}
185+
}
186+
187+
void main() {
188+
runApp(const AuthenticatorWithControllers());
189+
}

packages/authenticator/amplify_authenticator/example/lib/main.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ class _MyAppState extends State<MyApp> {
210210
// Widget build(BuildContext context) {
211211
// return const AuthenticatorWithCustomAuthFlow();
212212
// }
213+
214+
// Below is an example showing TextEditingController support for
215+
// programmatic form field control
216+
// @override
217+
// Widget build(BuildContext context) {
218+
// return const AuthenticatorWithControllers();
219+
// }
213220
}
214221

215222
/// The screen which is shown once the user is logged in. We can use

packages/authenticator/amplify_authenticator/lib/amplify_authenticator.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ export 'package:amplify_authenticator/src/utils/dial_code.dart' show DialCode;
4242
export 'package:amplify_authenticator/src/utils/dial_code_options.dart'
4343
show DialCodeOptions;
4444

45-
export 'src/enums/enums.dart' show AuthenticatorStep, Gender;
45+
export 'src/controllers/authenticator_text_field_controller.dart';
46+
export 'src/enums/enums.dart'
47+
show AuthenticatorStep, AuthenticatorTextEnabledOverride, Gender;
4648
export 'src/l10n/auth_strings_resolver.dart' hide ButtonResolverKeyType;
4749
export 'src/models/authenticator_exception.dart';
4850
export 'src/models/totp_options.dart';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import 'package:flutter/widgets.dart';
5+
6+
/// Controller for text driven Authenticator form fields.
7+
///
8+
/// Wraps Flutter's [TextEditingController] so developers can opt in to
9+
/// programmatic control over prebuilt Authenticator fields while keeping
10+
/// identical semantics to a regular controller.
11+
class AuthenticatorTextFieldController extends TextEditingController {
12+
AuthenticatorTextFieldController({super.text});
13+
14+
AuthenticatorTextFieldController.fromValue(super.value) : super.fromValue();
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/// Enum for overriding the enabled state of a form field.
5+
enum AuthenticatorTextEnabledOverride {
6+
/// Use the default enabled state.
7+
defaultSetting,
8+
9+
/// Force the field to be disabled.
10+
disabled,
11+
}

packages/authenticator/amplify_authenticator/lib/src/enums/enums.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
export 'authenticator_step.dart';
5+
export 'authenticator_text_enabled_override.dart';
56
export 'confirm_signin_types.dart';
67
export 'confirm_signup_types.dart';
78
export 'email_setup_types.dart';

0 commit comments

Comments
 (0)