@@ -9,8 +9,6 @@ import 'package:ui_kit/ui_kit.dart';
99/// {@template account_linking_page}
1010/// Displays options for an anonymous user to link their account to an email.
1111///
12- /// This page is presented as a modal bottom sheet, allowing the previous
13- /// page to remain visible underneath and providing a dismissible experience.
1412/// {@endtemplate}
1513class AccountLinkingPage extends StatelessWidget {
1614 /// {@macro account_linking_page}
@@ -22,188 +20,85 @@ class AccountLinkingPage extends StatelessWidget {
2220 final textTheme = Theme .of (context).textTheme;
2321 final colorScheme = Theme .of (context).colorScheme;
2422
25- // GestureDetector allows dismissing the modal by tapping the scrim.
26- return GestureDetector (
27- onTap: () => context.pop (),
28- child: Scaffold (
29- // A transparent background is crucial for the modal effect.
30- backgroundColor: Colors .transparent,
31- // The DraggableScrollableSheet provides the bottom sheet behavior.
32- body: DraggableScrollableSheet (
33- // The initial height of the bottom sheet (approx 2/3 of the screen).
34- initialChildSize: 0.66 ,
35- // The minimum height the sheet can be dragged down to before dismissing.
36- minChildSize: 0.4 ,
37- // The maximum height the sheet can be dragged up to.
38- maxChildSize: 0.9 ,
39- expand: false , // Do not expand to full screen by default
40- builder: (BuildContext context, ScrollController scrollController) {
41- // A second GestureDetector prevents the inner content taps from
42- // bubbling up and closing the modal.
43- return GestureDetector (
44- onTap: () {}, // Absorb taps within the sheet
45- child: Container (
46- // Apply styling for the bottom sheet container.
47- decoration: BoxDecoration (
48- color: colorScheme.surface,
49- borderRadius:
50- const BorderRadius .vertical (top: Radius .circular (16 )),
23+ return Scaffold (
24+ appBar: AppBar (title: Text (l10n.accountLinkingHeadline)),
25+ body: BlocConsumer <AuthenticationBloc , AuthenticationState >(
26+ listener: (context, state) {
27+ if (state.status == AuthenticationStatus .failure) {
28+ ScaffoldMessenger .of (context)
29+ ..hideCurrentSnackBar ()
30+ ..showSnackBar (
31+ SnackBar (
32+ content: Text (state.exception! .toFriendlyMessage (context)),
33+ backgroundColor: colorScheme.error,
5134 ),
52- child: Stack (
53- children: [
54- Column (
55- children: [
56- // --- Drag Handle ---
57- Padding (
58- padding:
59- const EdgeInsets .symmetric (vertical: AppSpacing .md),
60- child: Center (
61- child: Container (
62- height: 4 ,
63- width: 40 ,
64- decoration: BoxDecoration (
65- color: colorScheme.onSurface.withOpacity (0.4 ),
66- borderRadius: BorderRadius .circular (8 ),
67- ),
68- ),
69- ),
70- ),
71- // --- Main Content ---
72- Expanded (
73- child: BlocConsumer <AuthenticationBloc ,
74- AuthenticationState >(
75- listener: (context, state) {
76- if (state.status ==
77- AuthenticationStatus .failure) {
78- ScaffoldMessenger .of (context)
79- ..hideCurrentSnackBar ()
80- ..showSnackBar (
81- SnackBar (
82- content: Text (
83- state.exception!
84- .toFriendlyMessage (context),
85- ),
86- backgroundColor: colorScheme.error,
87- ),
88- );
89- }
90- },
91- builder: (context, state) {
92- final isLoading =
93- state.status == AuthenticationStatus .loading;
94-
95- return Padding (
96- padding: const EdgeInsets .all (
97- AppSpacing .paddingLarge,
98- ),
99- child: Center (
100- child: SingleChildScrollView (
101- // Link the scroll controller to enable dragging the sheet.
102- controller: scrollController,
103- child: Column (
104- mainAxisAlignment:
105- MainAxisAlignment .center,
106- crossAxisAlignment:
107- CrossAxisAlignment .stretch,
108- children: [
109- // --- Icon ---
110- Padding (
111- padding: const EdgeInsets .only (
112- bottom: AppSpacing .xl,
113- ),
114- child: Icon (
115- Icons .sync ,
116- size: AppSpacing .xxl * 2 ,
117- color: colorScheme.primary,
118- ),
119- ),
120- // --- Headline and Subheadline ---
121- Text (
122- l10n.accountLinkingHeadline,
123- style: textTheme.headlineMedium
124- ? .copyWith (
125- fontWeight: FontWeight .bold,
126- ),
127- textAlign: TextAlign .center,
128- ),
129- const SizedBox (height: AppSpacing .md),
130- Text (
131- l10n.accountLinkingBody,
132- style: textTheme.bodyLarge? .copyWith (
133- color:
134- colorScheme.onSurfaceVariant,
135- ),
136- textAlign: TextAlign .center,
137- ),
138- const SizedBox (height: AppSpacing .xxl),
35+ );
36+ }
37+ },
38+ builder: (context, state) {
39+ final isLoading = state.status == AuthenticationStatus .loading;
13940
140- // --- Email Linking Button ---
141- ElevatedButton .icon (
142- icon:
143- const Icon (Icons .email_outlined),
144- onPressed: isLoading
145- ? null
146- : () {
147- // Navigate to the request code page for linking.
148- context.goNamed (
149- Routes
150- .accountLinkingRequestCodeName,
151- );
152- },
153- label: Text (
154- l10n.accountLinkingSendLinkButton,
155- ),
156- style: ElevatedButton .styleFrom (
157- padding:
158- const EdgeInsets .symmetric (
159- vertical: AppSpacing .md,
160- ),
161- textStyle: textTheme.labelLarge,
162- ),
163- ),
164-
165- // --- Loading Indicator ---
166- if (isLoading) ...[
167- const Padding (
168- padding: EdgeInsets .only (
169- top: AppSpacing .xl,
170- ),
171- child: Center (
172- child:
173- CircularProgressIndicator (),
174- ),
175- ),
176- ],
177- ],
178- ),
179- ),
180- ),
41+ return Padding (
42+ padding: const EdgeInsets .all (AppSpacing .paddingLarge),
43+ child: Center (
44+ child: SingleChildScrollView (
45+ child: Column (
46+ mainAxisAlignment: MainAxisAlignment .center,
47+ crossAxisAlignment: CrossAxisAlignment .stretch,
48+ children: [
49+ Padding (
50+ padding: const EdgeInsets .only (bottom: AppSpacing .xl),
51+ child: Icon (
52+ Icons .sync ,
53+ size: AppSpacing .xxl * 2 ,
54+ color: colorScheme.primary,
55+ ),
56+ ),
57+ Text (
58+ l10n.accountLinkingHeadline,
59+ style: textTheme.headlineMedium? .copyWith (
60+ fontWeight: FontWeight .bold,
61+ ),
62+ textAlign: TextAlign .center,
63+ ),
64+ const SizedBox (height: AppSpacing .md),
65+ Text (
66+ l10n.accountLinkingBody,
67+ style: textTheme.bodyLarge? .copyWith (
68+ color: colorScheme.onSurfaceVariant,
69+ ),
70+ textAlign: TextAlign .center,
71+ ),
72+ const SizedBox (height: AppSpacing .xxl),
73+ ElevatedButton .icon (
74+ icon: const Icon (Icons .email_outlined),
75+ onPressed: isLoading
76+ ? null
77+ : () {
78+ context.goNamed (
79+ Routes .accountLinkingRequestCodeName,
18180 );
18281 },
183- ),
82+ label: Text (l10n.accountLinkingSendLinkButton),
83+ style: ElevatedButton .styleFrom (
84+ padding: const EdgeInsets .symmetric (
85+ vertical: AppSpacing .md,
18486 ),
185- ],
186- ),
187- // --- Close Button ---
188- Positioned (
189- top: AppSpacing .sm,
190- right: AppSpacing .sm,
191- child: IconButton (
192- icon: const Icon (Icons .close),
193- tooltip: MaterialLocalizations .of (context)
194- .closeButtonTooltip,
195- onPressed: () {
196- // Dismiss the modal bottom sheet.
197- context.pop ();
198- },
87+ textStyle: textTheme.labelLarge,
19988 ),
20089 ),
90+ if (isLoading) ...[
91+ const Padding (
92+ padding: EdgeInsets .only (top: AppSpacing .xl),
93+ child: Center (child: CircularProgressIndicator ()),
94+ ),
95+ ],
20196 ],
20297 ),
20398 ),
204- );
205- },
206- ) ,
99+ ),
100+ );
101+ } ,
207102 ),
208103 );
209104 }
0 commit comments