Skip to content

Commit 18ef329

Browse files
committed
* updated example app
* updated README.md
1 parent 7ef56cd commit 18ef329

85 files changed

Lines changed: 2765 additions & 235 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 101 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -217,155 +217,176 @@ void _verifyPhoneNumber() async {
217217

218218
#### Sample Usage
219219
```dart
220-
import 'package:firebase_core/firebase_core.dart';
221220
import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart';
222221
import 'package:flutter/material.dart';
222+
import 'package:phone_auth_handler_demo/screens/home_screen.dart';
223+
import 'package:phone_auth_handler_demo/utils/helpers.dart';
224+
import 'package:phone_auth_handler_demo/widgets/custom_loader.dart';
225+
import 'package:phone_auth_handler_demo/widgets/pin_input_field.dart';
223226
224-
void main() async {
225-
WidgetsFlutterBinding.ensureInitialized();
226-
await Firebase.initializeApp();
227-
runApp(_MainApp());
227+
class VerifyPhoneNumberScreen extends StatefulWidget {
228+
static const id = 'VerifyPhoneNumberScreen';
229+
230+
final String phoneNumber;
231+
232+
const VerifyPhoneNumberScreen({
233+
Key? key,
234+
required this.phoneNumber,
235+
}) : super(key: key);
236+
237+
@override
238+
State<VerifyPhoneNumberScreen> createState() =>
239+
_VerifyPhoneNumberScreenState();
228240
}
229241
230-
class _MainApp extends StatelessWidget {
242+
class _VerifyPhoneNumberScreenState extends State<VerifyPhoneNumberScreen>
243+
with WidgetsBindingObserver {
244+
bool isKeyboardVisible = false;
245+
246+
late final ScrollController scrollController;
247+
231248
@override
232-
Widget build(BuildContext context) {
233-
return FirebasePhoneAuthProvider(
234-
child: MaterialApp(
235-
debugShowCheckedModeBanner: false,
236-
home: VerifyPhoneNumberScreen(phoneNumber: "+919876543210"),
237-
),
238-
);
249+
void initState() {
250+
scrollController = ScrollController();
251+
WidgetsBinding.instance?.addObserver(this);
252+
super.initState();
239253
}
240-
}
241254
242-
// ignore: must_be_immutable
243-
class VerifyPhoneNumberScreen extends StatelessWidget {
244-
final String phoneNumber;
255+
@override
256+
void dispose() {
257+
WidgetsBinding.instance?.removeObserver(this);
258+
scrollController.dispose();
259+
super.dispose();
260+
}
245261
246-
String? _enteredOTP;
262+
@override
263+
void didChangeMetrics() {
264+
final bottomViewInsets = WidgetsBinding.instance!.window.viewInsets.bottom;
265+
isKeyboardVisible = bottomViewInsets > 0;
266+
}
247267
248-
VerifyPhoneNumberScreen({
249-
Key? key,
250-
required this.phoneNumber,
251-
}) : super(key: key);
268+
// scroll to bottom of screen, when pin input field is in focus.
269+
Future<void> _scrollToBottomOnKeyboardOpen() async {
270+
while (!isKeyboardVisible) {
271+
await Future.delayed(const Duration(milliseconds: 50));
272+
}
273+
274+
await Future.delayed(const Duration(milliseconds: 250));
252275
253-
void _showSnackBar(BuildContext context, String text) {
254-
ScaffoldMessenger.of(context).showSnackBar(
255-
SnackBar(content: Text(text)),
276+
await scrollController.animateTo(
277+
scrollController.position.maxScrollExtent,
278+
duration: const Duration(milliseconds: 250),
279+
curve: Curves.easeIn,
256280
);
257281
}
258282
259283
@override
260284
Widget build(BuildContext context) {
261285
return SafeArea(
262286
child: FirebasePhoneAuthHandler(
263-
phoneNumber: phoneNumber,
264-
timeOutDuration: const Duration(seconds: 60),
287+
phoneNumber: widget.phoneNumber,
265288
onLoginSuccess: (userCredential, autoVerified) async {
266-
_showSnackBar(
267-
context,
268-
'Phone number verified successfully!',
289+
log(
290+
VerifyPhoneNumberScreen.id,
291+
msg: autoVerified
292+
? 'OTP was fetched automatically!'
293+
: 'OTP was verified manually!',
269294
);
270295
271-
debugPrint(
272-
autoVerified
273-
? "OTP was fetched automatically"
274-
: "OTP was verified manually",
296+
showSnackBar('Phone number verified successfully!');
297+
298+
log(
299+
VerifyPhoneNumberScreen.id,
300+
msg: 'Login Success UID: ${userCredential.user?.uid}',
275301
);
276302
277-
debugPrint("Login Success UID: ${userCredential.user?.uid}");
278-
},
279-
onLoginFailed: (authException) {
280-
_showSnackBar(
303+
Navigator.pushNamedAndRemoveUntil(
281304
context,
282-
'Something went wrong (${authException.message})',
305+
HomeScreen.id,
306+
(route) => false,
283307
);
284-
285-
debugPrint(authException.message);
308+
},
309+
onLoginFailed: (authException) {
310+
showSnackBar('Something went wrong!');
311+
log(VerifyPhoneNumberScreen.id, error: authException.message);
286312
// handle error further if needed
287313
},
288314
builder: (context, controller) {
289315
return Scaffold(
290316
appBar: AppBar(
291317
leadingWidth: 0,
292318
leading: const SizedBox.shrink(),
293-
title: const Text("Verify Phone Number"),
319+
title: const Text('Verify Phone Number'),
294320
actions: [
295321
if (controller.codeSent)
296322
TextButton(
297323
child: Text(
298324
controller.timerIsActive
299-
? "${controller.timerCount.inSeconds}s"
300-
: "RESEND",
301-
style: const TextStyle(
302-
color: Colors.blue,
303-
fontSize: 18,
304-
),
325+
? '${controller.timerCount.inSeconds}s'
326+
: 'Resend',
327+
style: const TextStyle(color: Colors.blue, fontSize: 18),
305328
),
306329
onPressed: controller.timerIsActive
307330
? null
308-
: () async => await controller.sendOTP(),
331+
: () async {
332+
log(VerifyPhoneNumberScreen.id, msg: 'Resend OTP');
333+
await controller.sendOTP();
334+
},
309335
),
310336
const SizedBox(width: 5),
311337
],
312338
),
313339
body: controller.codeSent
314340
? ListView(
315341
padding: const EdgeInsets.all(20),
342+
controller: scrollController,
316343
children: [
317344
Text(
318-
"We've sent an SMS with a verification code to $phoneNumber",
319-
style: const TextStyle(
320-
fontSize: 25,
321-
),
345+
"We've sent an SMS with a verification code to ${widget.phoneNumber}",
346+
style: const TextStyle(fontSize: 25),
322347
),
323348
const SizedBox(height: 10),
324349
const Divider(),
325-
AnimatedContainer(
326-
duration: const Duration(seconds: 1),
327-
height: controller.timerIsActive ? null : 0,
328-
child: Column(
350+
if (controller.timerIsActive)
351+
Column(
329352
children: const [
330-
CircularProgressIndicator.adaptive(),
353+
CustomLoader(),
331354
SizedBox(height: 50),
332355
Text(
333-
"Listening for OTP",
356+
'Listening for OTP',
334357
textAlign: TextAlign.center,
335358
style: TextStyle(
336359
fontSize: 25,
337360
fontWeight: FontWeight.w600,
338361
),
339362
),
363+
SizedBox(height: 15),
340364
Divider(),
341-
Text("OR", textAlign: TextAlign.center),
365+
Text('OR', textAlign: TextAlign.center),
342366
Divider(),
343367
],
344368
),
345-
),
369+
const SizedBox(height: 15),
346370
const Text(
347-
"Enter OTP",
371+
'Enter OTP',
348372
style: TextStyle(
349373
fontSize: 20,
350374
fontWeight: FontWeight.w600,
351375
),
352376
),
353-
TextField(
354-
maxLength: 6,
355-
keyboardType: TextInputType.number,
356-
onChanged: (String v) async {
357-
_enteredOTP = v;
358-
if (_enteredOTP?.length == 6) {
359-
final isValidOTP = await controller.verifyOTP(
360-
otp: _enteredOTP!,
361-
);
362-
// Incorrect OTP
363-
if (!isValidOTP) {
364-
_showSnackBar(
365-
context,
366-
"Please enter the correct OTP sent to $phoneNumber",
367-
);
368-
}
377+
const SizedBox(height: 15),
378+
PinInputField(
379+
length: 6,
380+
onFocusChange: (hasFocus) async {
381+
if (hasFocus) await _scrollToBottomOnKeyboardOpen();
382+
},
383+
onSubmit: (enteredOTP) async {
384+
final isValidOTP = await controller.verifyOTP(
385+
otp: enteredOTP,
386+
);
387+
// Incorrect OTP
388+
if (!isValidOTP) {
389+
showSnackBar('The entered OTP is invalid!');
369390
}
370391
},
371392
),
@@ -375,11 +396,11 @@ class VerifyPhoneNumberScreen extends StatelessWidget {
375396
mainAxisAlignment: MainAxisAlignment.center,
376397
crossAxisAlignment: CrossAxisAlignment.center,
377398
children: const [
378-
CircularProgressIndicator.adaptive(),
399+
CustomLoader(),
379400
SizedBox(height: 50),
380401
Center(
381402
child: Text(
382-
"Sending OTP",
403+
'Sending OTP',
383404
style: TextStyle(fontSize: 25),
384405
),
385406
),
@@ -391,7 +412,6 @@ class VerifyPhoneNumberScreen extends StatelessWidget {
391412
);
392413
}
393414
}
394-
395415
```
396416

397417
See the [`example`](https://github.com/rithik-dev/firebase_phone_auth_handler/blob/master/example) directory for a complete sample app.

example/.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# The .vscode folder contains launch configuration and tasks you configure in
19+
# VS Code which you may wish to be included in version control, so this line
20+
# is commented out by default.
21+
#.vscode/
22+
23+
# Flutter/Dart/Pub related
24+
**/doc/api/
25+
**/ios/Flutter/.last_build_id
26+
.dart_tool/
27+
.flutter-plugins
28+
.flutter-plugins-dependencies
29+
.packages
30+
.pub-cache/
31+
.pub/
32+
/build/
33+
34+
# Web related
35+
lib/generated_plugin_registrant.dart
36+
37+
# Symbolication related
38+
app.*.symbols
39+
40+
# Obfuscation related
41+
app.*.map.json
42+
43+
# Android Studio will place build artifacts here
44+
/android/app/debug
45+
/android/app/profile
46+
/android/app/release

example/.metadata

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: 7e9793dee1b85a243edd0e06cb1658e98b077561
8+
channel: stable
9+
10+
project_type: app

example/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# phone_auth_handler_demo
2+
3+
Demo for the package firebase_phone_auth_handler on pub.dev
4+
5+
## Getting Started
6+
7+
This project is a starting point for a Flutter application.
8+
9+
A few resources to get you started if this is your first Flutter project:
10+
11+
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12+
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13+
14+
For help getting started with Flutter, view our
15+
[online documentation](https://flutter.dev/docs), which offers tutorials,
16+
samples, guidance on mobile development, and a full API reference.

example/analysis_options.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# This file configures the analyzer, which statically analyzes Dart code to
2+
# check for errors, warnings, and lints.
3+
#
4+
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5+
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6+
# invoked from the command line by running `flutter analyze`.
7+
8+
# The following line activates a set of recommended lints for Flutter apps,
9+
# packages, and plugins designed to encourage good coding practices.
10+
include: package:flutter_lints/flutter.yaml
11+
12+
linter:
13+
# The lint rules applied to this project can be customized in the
14+
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
15+
# included above or to enable additional rules. A list of all available lints
16+
# and their documentation is published at
17+
# https://dart-lang.github.io/linter/lints/index.html.
18+
#
19+
# Instead of disabling a lint rule for the entire project in the
20+
# section below, it can also be suppressed for a single line of code
21+
# or a specific dart file by using the `// ignore: name_of_lint` and
22+
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
23+
# producing the lint.
24+
rules:
25+
prefer_single_quotes: true
26+
# avoid_print: false # Uncomment to disable the `avoid_print` rule
27+
28+
# Additional information about this file can be found at
29+
# https://dart.dev/guides/language/analysis-options

example/android/.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
gradle-wrapper.jar
2+
/.gradle
3+
/captures/
4+
/gradlew
5+
/gradlew.bat
6+
/local.properties
7+
GeneratedPluginRegistrant.java
8+
9+
# Remember to never publicly share your keystore.
10+
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11+
key.properties
12+
**/*.keystore
13+
**/*.jks

0 commit comments

Comments
 (0)