Skip to content

Commit e1cf8f6

Browse files
committed
feat(auth): email link sign-in
- Removed password field - Added send sign-in link button - Used BlocConsumer for side effects - Improved UI and error handling
1 parent 5cba6bd commit e1cf8f6

File tree

1 file changed

+136
-78
lines changed

1 file changed

+136
-78
lines changed

lib/authentication/view/authentication_page.dart

Lines changed: 136 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -17,98 +17,156 @@ class AuthenticationPage extends StatelessWidget {
1717
}
1818
}
1919

20-
class _AuthenticationView extends StatelessWidget {
21-
_AuthenticationView();
20+
class _AuthenticationView extends StatefulWidget {
21+
@override
22+
__AuthenticationViewState createState() => __AuthenticationViewState();
23+
}
2224

25+
class __AuthenticationViewState extends State<_AuthenticationView> {
2326
final _emailController = TextEditingController();
24-
final _passwordController = TextEditingController();
27+
// Removed password controller
28+
29+
@override
30+
void dispose() {
31+
_emailController.dispose();
32+
// Removed password controller disposal
33+
super.dispose();
34+
}
2535

2636
@override
2737
Widget build(BuildContext context) {
38+
// Use BlocConsumer to listen for state changes for side effects (SnackBar)
2839
return Scaffold(
2940
body: SafeArea(
30-
child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
31-
builder: (context, state) {
32-
if (state is AuthenticationLoading) {
33-
return const Center(child: CircularProgressIndicator());
34-
}
41+
child: BlocConsumer<AuthenticationBloc, AuthenticationState>(
42+
listener: (context, state) {
3543
if (state is AuthenticationFailure) {
36-
WidgetsBinding.instance.addPostFrameCallback((_) {
37-
ScaffoldMessenger.of(context).showSnackBar(
38-
SnackBar(content: Text(state.errorMessage)),
44+
ScaffoldMessenger.of(context)
45+
..hideCurrentSnackBar()
46+
..showSnackBar(
47+
SnackBar(
48+
content: Text(state.errorMessage),
49+
backgroundColor: Theme.of(context).colorScheme.error,
50+
),
51+
);
52+
} else if (state is AuthenticationLinkSentSuccess) {
53+
ScaffoldMessenger.of(context)
54+
..hideCurrentSnackBar()
55+
..showSnackBar(
56+
const SnackBar(
57+
content: Text('Check your email for the sign-in link.'),
58+
),
3959
);
40-
});
60+
// Optionally clear email field or navigate
4161
}
62+
},
63+
builder: (context, state) {
64+
// Determine if loading indicator should be shown
65+
final isLoading = state is AuthenticationLoading ||
66+
state is AuthenticationLinkSending;
67+
4268
return Padding(
43-
padding: const EdgeInsets.all(16),
44-
child: SingleChildScrollView(
45-
child: Column(
46-
mainAxisAlignment: MainAxisAlignment.center,
47-
children: [
48-
const Text(
49-
'Sign In',
50-
style:
51-
TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
52-
),
53-
const SizedBox(height: 32),
54-
TextFormField(
55-
controller: _emailController,
56-
decoration: const InputDecoration(
57-
labelText: 'Email',
58-
border: OutlineInputBorder(),
69+
padding: const EdgeInsets.all(16), // Use AppSpacing later
70+
child: Center(
71+
// Center content vertically
72+
child: SingleChildScrollView(
73+
// Allow scrolling if needed
74+
child: Column(
75+
// Use CrossAxisAlignment.stretch for full-width buttons
76+
crossAxisAlignment: CrossAxisAlignment.stretch,
77+
children: [
78+
Text(
79+
'Sign In / Register', // Updated title
80+
style: Theme.of(context)
81+
.textTheme
82+
.headlineMedium, // Use theme typography
83+
textAlign: TextAlign.center,
84+
),
85+
const SizedBox(height: 32), // Use AppSpacing later
86+
TextFormField(
87+
controller: _emailController,
88+
decoration: const InputDecoration(
89+
labelText: 'Email', // Needs localization
90+
border: OutlineInputBorder(),
91+
),
92+
keyboardType: TextInputType.emailAddress,
93+
autocorrect: false,
94+
textInputAction:
95+
TextInputAction.done, // Improve keyboard action
96+
enabled: !isLoading, // Disable field when loading
5997
),
60-
keyboardType: TextInputType.emailAddress,
61-
),
62-
const SizedBox(height: 16),
63-
TextFormField(
64-
controller: _passwordController,
65-
decoration: const InputDecoration(
66-
labelText: 'Password',
67-
border: OutlineInputBorder(),
98+
// Removed Password Field
99+
const SizedBox(height: 32), // Use AppSpacing later
100+
// Show loading indicator within the button if sending link
101+
ElevatedButton(
102+
onPressed: isLoading // Disable button when loading
103+
? null
104+
: () {
105+
context.read<AuthenticationBloc>().add(
106+
AuthenticationSendSignInLinkRequested(
107+
email: _emailController.text
108+
.trim(), // Trim whitespace
109+
),
110+
);
111+
},
112+
child: state is AuthenticationLinkSending
113+
? const SizedBox(
114+
// Consistent height loading indicator
115+
height: 24,
116+
width: 24,
117+
child:
118+
CircularProgressIndicator(strokeWidth: 2),
119+
)
120+
: const Text(
121+
'Send Sign-In Link'), // Needs localization
68122
),
69-
obscureText: true,
70-
),
71-
const SizedBox(height: 32),
72-
ElevatedButton(
73-
onPressed: () {
74-
context.read<AuthenticationBloc>().add(
75-
AuthenticationEmailSignInRequested(
76-
email: _emailController.text,
77-
password: _passwordController.text,
78-
),
79-
);
80-
},
81-
child: const Text('Sign In with Email'),
82-
),
83-
const SizedBox(height: 16),
84-
ElevatedButton(
85-
onPressed: () {
86-
context
87-
.read<AuthenticationBloc>()
88-
.add(const AuthenticationGoogleSignInRequested());
89-
},
90-
style: ElevatedButton.styleFrom(
91-
backgroundColor: Colors.white,
92-
foregroundColor: Colors.black,
123+
const SizedBox(height: 16), // Use AppSpacing later
124+
// Add divider for clarity
125+
const Row(
126+
children: [
127+
Expanded(child: Divider()),
128+
Padding(
129+
padding: EdgeInsets.symmetric(horizontal: 8),
130+
child: Text('OR'), // Needs localization
131+
),
132+
Expanded(child: Divider()),
133+
],
93134
),
94-
child: const Text('Sign In with Google'),
95-
),
96-
const SizedBox(height: 16),
97-
ElevatedButton(
98-
onPressed: () {
99-
context.read<AuthenticationBloc>().add(
100-
const AuthenticationAnonymousSignInRequested(),
101-
);
102-
},
103-
child: const Text('Sign In Anonymously'),
104-
),
105-
],
106-
),
107-
),
108-
);
135+
const SizedBox(height: 16), // Use AppSpacing later
136+
ElevatedButton(
137+
// Removed duplicate onPressed here
138+
// Style adjustments for Google button might be needed via Theme
139+
onPressed: isLoading // Disable button when loading
140+
? null
141+
: () {
142+
context.read<AuthenticationBloc>().add(
143+
const AuthenticationGoogleSignInRequested());
144+
},
145+
// Consider adding Google icon
146+
child: const Text(
147+
'Sign In with Google'), // Needs localization
148+
),
149+
const SizedBox(height: 16), // Use AppSpacing later
150+
OutlinedButton(
151+
// Use OutlinedButton for less emphasis
152+
onPressed: isLoading // Disable button when loading
153+
? null
154+
: () {
155+
context.read<AuthenticationBloc>().add(
156+
const AuthenticationAnonymousSignInRequested(),
157+
);
158+
},
159+
child: const Text(
160+
'Continue Anonymously'), // Needs localization
161+
),
162+
],
163+
), // Column
164+
), // SingleChildScrollView
165+
), // Center
166+
); // Padding
109167
},
110-
),
111-
),
112-
);
168+
), // BlocConsumer
169+
), // SafeArea
170+
); // Scaffold
113171
}
114172
}

0 commit comments

Comments
 (0)