Login Page code Flutter Android Studio with Firebase
This tutorial will guide you through the process of enabling Google Sign
In authentication to flutter application using firebase authentication
with step-by-step and show’s the user’s data.I am just writing the login
code I think you know how to setup if not then ping me in the comments
Firebase setup
Before Firebase app setup, enable the Google sign-in method in the authentication section. Build> Authentication> Sign-in method.
Just make a Simple main.dart file:
import 'package:flutter/material.dart';
import 'package:petlove/utils/LoginPage.dart';
import 'mainkk.dart';
import 'models/User_model.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(); // Make sure this is called
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Dummy user for testing
final dummyUser = UserModel(
uid: 'abc123',
email: 'test@example.com',
displayName: 'Test User',
ngo_uid: 'ngo_001',
photoURL: 'https://example.com/photo.jpg',
);
return MaterialApp(
title: 'PetLove',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: LoginPage(),
);
}
}
Now make a LoginPage():
import 'package:flutter/material.dart';
import 'package:petlove/utils/auth_service.dart';
import 'package:petlove/models/User_model.dart';
import '../mainkk.dart';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final AuthService authService = AuthService();
bool isLoading = false;
void _signIn() async {
setState(() {
isLoading = true;
});
final user = await authService.signInWithGoogle();
setState(() {
isLoading = false;
});
if (user != null) {
final userModel = UserModel(
uid: user.uid,
email: user.email ?? '',
displayName: user.displayName ?? '',
photoURL: user.photoURL ?? '',
ngo_uid: 'example_ngo',
);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => MyCustomForm(user: userModel)),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple[50], // Light purple background
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"A care for Your",
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.deepPurple,
),
),
const SizedBox(height: 40),
Image.asset(
'assets/petcare_image.PNG', // You can add a cute image here
height: 180,
),
const SizedBox(height: 40),
isLoading
? const CircularProgressIndicator(
color: Colors.deepPurple,
)
: ElevatedButton.icon(
icon: const Icon(Icons.login, color: Colors.white),
label: const Text(
"Sign in with Google",
style: TextStyle(fontSize: 18),
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
minimumSize: Size(double.infinity, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: _signIn,
),
],
),
),
),
);
}
}
Check this line in above code-
MaterialPageRoute(builder: (context) => MyCustomForm(user: userModel)),
CAlling MyCustomForm (in my case this is Mainkk.dart file)
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:petlove/models/User_model.dart';
import 'package:petlove/screens/home_page.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path/path.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geoflutterfire2/geoflutterfire2.dart';
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key, required UserModel user})
: _user = user,
super(key: key);
final UserModel _user;
@override
MyCustomFormState createState() => MyCustomFormState();
}
class MyCustomFormState extends State<MyCustomForm> {
GeoFlutterFire geo = GeoFlutterFire();
final _formKey = GlobalKey<FormState>();
late UserModel _user;
File? image;
String? imageURL;
int uploadStatus = 0;
late LatLng location;
late GeoFirePoint geopoint;
FirebaseStorage storage = FirebaseStorage.instance;
final descriptionController = TextEditingController();
final animalController = TextEditingController();
final locationController = TextEditingController();
@override
void initState() {
super.initState();
_user = widget._user;
// Initialize with default location to avoid null issues
location = const LatLng(37.7749, -122.4194); // Default: San Francisco
geopoint = geo.point(latitude: location.latitude, longitude: location.longitude);
}
@override
void dispose() {
descriptionController.dispose();
animalController.dispose();
locationController.dispose();
super.dispose();
}
Future<void> uploadImage() async {
if (image == null) return;
setState(() {
uploadStatus = 1;
});
try {
// Enforce 30-second timeout for the entire upload process
String response = await uploadFile().timeout(
const Duration(seconds: 30),
onTimeout: () {
throw TimeoutException('Image upload timed out after 30 seconds');
},
);
setState(() {
imageURL = response;
uploadStatus = 2;
});
} catch (e) {
print('Upload error: $e');
// Show feedback to user
ScaffoldMessenger.of(context as BuildContext).showSnackBar(
SnackBar(content: Text('Upload failed: $e')),
);
// Redirect to HomePage after a brief delay to show SnackBar
await Future.delayed(const Duration(seconds: 1));
if (mounted) {
Navigator.of(context as BuildContext).pushReplacement(
MaterialPageRoute(builder: (context) => HomePage(user: _user)),
);
}
setState(() {
uploadStatus = 0;
});
}
}
Future<String> uploadFile() async {
final fileName = basename(image!.path);
final destination = 'files/$fileName';
final ref = FirebaseStorage.instance.ref(destination).child('file/');
// Wrap the upload task in a Future to ensure timeout applies
final uploadTask = ref.putFile(image!);
final snapshot = await uploadTask;
final imgUrl = await snapshot.ref.getDownloadURL();
return imgUrl;
}
Future<void> pickImage() async {
try {
final pickedImage = await ImagePicker().pickImage(source: ImageSource.gallery);
if (pickedImage == null) return;
setState(() {
image = File(pickedImage.path);
});
} catch (e) {
print('Failed to pick image: $e');
}
}
Future<void> pickImageC() async {
try {
final pickedImage = await ImagePicker().pickImage(
source: ImageSource.camera,
imageQuality: 50,
);
if (pickedImage == null) return;
setState(() {
image = File(pickedImage.path);
});
} catch (e) {
print('Failed to pick image: $e');
}
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SetCurrentLocation(user: _user),
),
);
if (result != null) {
setState(() {
location = result;
geopoint = geo.point(latitude: location.latitude, longitude: location.longitude);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
elevation: 0,
backgroundColor: const Color.fromARGB(255, 4, 50, 88),
title: const Text(
'New Request',
style: TextStyle(color: Colors.white),
),
centerTitle: true,
),
body: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: TextFormField(
controller: animalController,
decoration: const InputDecoration(
icon: Icon(Icons.pets),
hintText: 'Enter Species of Animal',
labelText: 'Animal',
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: TextFormField(
controller: descriptionController,
decoration: const InputDecoration(
icon: Icon(Icons.report_sharp),
hintText: 'Give description of the Animal',
labelText: 'Description',
),
),
),
const SizedBox(height: 10),
Center(
child: MaterialButton(
color: const Color.fromARGB(255, 4, 50, 88),
child: const Text(
"Choose current location",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
onPressed: () => _awaitReturnValueFromSecondScreen(context),
),
),
const SizedBox(height: 10),
Center(
child: Column(
children: [
MaterialButton(
color: const Color.fromARGB(255, 4, 50, 88),
child: const Text(
"Pick Image from Camera",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
onPressed: pickImageC,
),
MaterialButton(
color: const Color.fromARGB(255, 4, 50, 88),
child: const Text(
"Pick Image from Gallery",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
onPressed: pickImage,
),
const SizedBox(height: 20),
if (image != null)
SizedBox(
height: 300,
width: 300,
child: Image.file(image!, fit: BoxFit.contain),
),
const SizedBox(height: 10),
if (uploadStatus == 0)
MaterialButton(
color: const Color.fromARGB(255, 4, 50, 88),
child: const Text('Submit Image', style: TextStyle(color: Colors.white)),
onPressed: uploadImage,
)
else if (uploadStatus == 1)
const CircularProgressIndicator()
else if (uploadStatus == 2)
ElevatedButton(
onPressed: () async {
try {
// Firestore insertion with 30-second timeout
await FirebaseFirestore.instance
.collection('Request')
.add({
'Description': descriptionController.text,
'UserID': _user.uid,
'Location': geopoint.data,
'ImageURL': imageURL,
'Animal': animalController.text,
'IsCompleted': false,
'HelperUID': '',
})
.timeout(
const Duration(seconds: 30),
onTimeout: () {
throw TimeoutException(
'Firestore insertion timed out after 30 seconds');
},
);
// Navigate to success screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RequestFormSubmitted(user: _user),
),
);
} catch (e) {
print('Firestore insertion error: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Submission failed: $e')),
);
// Redirect to HomePage
await Future.delayed(const Duration(seconds: 1));
if (mounted) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => HomePage(user: _user),
),
);
}
}
},
child: const Text('Submit'),
),
],
),
),
],
),
),
),
);
}
}
class RequestFormSubmitted extends StatefulWidget {
const RequestFormSubmitted({Key? key, required UserModel user})
: _user = user,
super(key: key);
final UserModel _user;
@override
_RequestFormSubmittedState createState() => _RequestFormSubmittedState();
}
class _RequestFormSubmittedState extends State<RequestFormSubmitted> {
late UserModel _user;
@override
void initState() {
super.initState();
_user = widget._user;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Request Submitted Successfully!",
style: TextStyle(fontSize: 20, color: Colors.white),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color.fromARGB(255, 4, 50, 88),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => HomePage(user: _user)),
);
},
child: const Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Text(
'Go Home',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 2,
),
),
),
),
],
),
),
);
}
}
class SetCurrentLocation extends StatefulWidget {
const SetCurrentLocation({Key? key, required UserModel user})
: _user = user,
super(key: key);
final UserModel _user;
@override
_SetCurrentLocationState createState() => _SetCurrentLocationState();
}
class _SetCurrentLocationState extends State<SetCurrentLocation> {
late UserModel _user;
late GoogleMapController _googleMapController;
late Position currentPosition;
bool locationSetFlag = false;
double newLatitude = 0.0;
double newLongitude = 0.0;
// Default coordinates (San Francisco)
static const LatLng defaultLocation = LatLng(37.7749, -122.4194);
@override
void initState() {
super.initState();
_user = widget._user;
}
@override
void dispose() {
_googleMapController.dispose();
super.dispose();
}
Future<Position> _determinePosition() async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
throw 'Location services are disabled.';
}
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
throw 'Location permissions are denied';
}
}
if (permission == LocationPermission.deniedForever) {
throw 'Location permissions are permanently denied.';
}
currentPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
return currentPosition;
} catch (e) {
print('Error fetching location: $e');
return Position(
latitude: defaultLocation.latitude,
longitude: defaultLocation.longitude,
timestamp: DateTime.now(),
accuracy: 0.0,
altitude: 0.0,
heading: 0.0,
speed: 0.0,
speedAccuracy: 0.0,
altitudeAccuracy: 0.0,
headingAccuracy: 0.0,
);
}
}
void _sendDataBack(BuildContext context) {
if (!locationSetFlag) {
newLatitude = currentPosition.latitude;
newLongitude = currentPosition.longitude;
}
LatLng latLngToSendBack = LatLng(newLatitude, newLongitude);
Navigator.pop(context, latLngToSendBack);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
elevation: 0,
backgroundColor: const Color.fromARGB(255, 4, 50, 88),
title: const Text(
'Set Current Location',
style: TextStyle(color: Colors.white),
),
centerTitle: true,
),
body: FutureBuilder<Position>(
future: _determinePosition(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Stack(
alignment: Alignment.center,
children: [
GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(snapshot.data!.latitude, snapshot.data!.longitude),
zoom: 15,
),
onMapCreated: (controller) => _googleMapController = controller,
markers: {
Marker(
markerId: const MarkerId('currentLocation'),
position: locationSetFlag
? LatLng(newLatitude, newLongitude)
: LatLng(snapshot.data!.latitude, snapshot.data!.longitude),
infoWindow: const InfoWindow(title: 'Your Current Location'),
),
},
onTap: (LatLng position) {
setState(() {
locationSetFlag = true;
newLatitude = position.latitude;
newLongitude = position.longitude;
});
},
),
Positioned(
bottom: 15,
child: MaterialButton(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0),
),
elevation: 10,
color: const Color.fromARGB(255, 4, 50, 88),
child: const Text(
"Submit current location",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => _sendDataBack(context),
),
),
],
);
} else if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Error: ${snapshot.error}'),
const SizedBox(height: 10),
const Text('Using default location.'),
ElevatedButton(
onPressed: () {
setState(() {
newLatitude = defaultLocation.latitude;
newLongitude = defaultLocation.longitude;
locationSetFlag = true;
});
_sendDataBack(context);
},
child: const Text('Use Default Location'),
),
],
),
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}