how to create a login and signup screen with google map In a flutter.
In this article, we will look into how to make a beautiful Login and Google Sign-In screen and authenticate the same with Google Maps.
We use it for everything from finding directions to a destination to searching for a nearby shopping mall and stores, and much more, to just zooming in and out of the map to see the street view of any location on the planet and flowing this get the location.
Implementing these services in your apps can be done through the use of the Google Maps API. It allows you to display a map, and customize its attributes and markers of places at their geo-location among other things. Covers this article to create a beautiful UI in iOS and Android devices app layout.
Login-
Every app used this screen as per the requirement of their service’s authentication access their users.
Google Maps –
Around the world, Google Maps has been available anywhere, and have access to this easy day-to-day life and search their location and many more like pizza food stores and food stores, and grocery store nearby. Flutter provides an easy solution for developers to implement the map on their app.
Let’s start main steps
Environment
- 1.71.0 (Universal)
- Dart Programming
- Flutter 3.0.5+ Version
- Build iOS
- Build Android
- Run iPhone 11 Pro
- Android SDK
- XCODE
- Terminal (on Mac/Linux) or CMD (on Windows)
- IDE (Android Studio/IntelliJ/Visual Studio Code)
Features structure
- MVC structure
- The navigation screen each other
- Validation covers
- Delegate pass value
- ListView UI Design
- Widgets – Button/text field/link / Text
- Scrolling with full screen
- Google map implementation
Pods(Pub.dev)
- path_provider: ^2.0.8
- http: ^0.13.0
- shared_preferences: ^2.0.7
- fluttertoast: ^8.0.8
- flutter_spinkit: ^5.1.0
- intl_utils: ^2.7.0
- package_info: ^2.0.0
- permission_handler: ^6.0.1+1
- google_fonts: ^2.1.0
- progress_dialog_null_safe: ^1.0.6
- intl_phone_field: ^3.1.0
- pin_code_fields: ^7.4.0
- google_maps_flutter: ^2.0.1
Login screen –
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:securisk_app/other_desgin/screens/login_and_reg/forget_pass.dart';
import 'package:securisk_app/other_desgin/screens/reg_form.dart';
import 'package:securisk_app/utlity/colors.dart';
import 'package:securisk_app/widgets/bg_image.dart';
class loginMain extends StatefulWidget {
@override
State<loginMain> createState() => _loginMainState();
}
class _loginMainState extends State<loginMain> {
final emailController = TextEditingController();
final passController = TextEditingController();
bool isShowPass = false;
bool onError = false;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/bg.png"),
fit: BoxFit.cover,
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BgImage(),
SizedBox(
height: 15,
),
SizedBox(
height: 200,
),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: Text('E-mail'),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: TextFormField(
style: TextStyle(color: Colors.black, fontSize: 14),
controller: emailController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email_outlined),
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
labelText: "Your email",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle:
TextStyle(color: Colors.grey.shade400, fontSize: 14),
),
// inputFormatters: [new LengthLimitingTextInputFormatter(10)],
validator: (String? val) {
String patttern = r'(^[0-9]*$)';
RegExp regExp = new RegExp(patttern);
if (val!.isEmpty) {
return "Mobile is Required";
} else if (val.length != 10) {
return "Mobile number must 10 digits";
} else if (!regExp.hasMatch(val)) {
return "Mobile Number must be digits";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: Text('Password'),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: TextFormField(
controller: passController,
obscureText: isShowPass,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
labelText: "Your Password",
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle:
TextStyle(color: Colors.grey.shade400, fontSize: 14),
suffixIcon: GestureDetector(
onTap: () {
setState(() {
isShowPass = !isShowPass;
});
},
child: Icon(
isShowPass
? Icons.visibility
: Icons.visibility_off,
size: 16,
)),
),
validator: (String? val) {
String patttern = r'(^[0-9]*$)';
RegExp regExp = new RegExp(patttern);
if (val!.isEmpty) {
return "Mobile is Required";
} else if (val.length != 10) {
return "Mobile number must 10 digits";
} else if (!regExp.hasMatch(val)) {
return "Mobile Number must be digits";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child: Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 10,
),
GestureDetector(
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => ForgetPassword())),
child: Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.only(right: 35.0),
child: Text(
"Forgot password?",
style: TextStyle(color: AppColors.blueColor),
),
),
),
),
SizedBox(
height: 25,
),
Padding(
padding: EdgeInsets.only(left: 35, right: 35),
child: Container(
padding: EdgeInsets.all(8.0),
// height: 100,
child: Row(
children: [
Expanded(
child: Container(
//height: 100,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: AppColors.buttonColor,
onPrimary: Colors.white,
side: BorderSide(
color: AppColors.buttonColor, width: 5),
),
onPressed: () async {
//next screen
},
child: Text("Login"),
),
)),
Container(
padding: EdgeInsets.only(left: 8.0),
//height: 100,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: AppColors.buttonColor,
onPrimary: Colors.white,
side: BorderSide(
color: AppColors.buttonColor, width: 5),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => regForm()));
},
child: Text("SingUp")))
],
),
),
),
Spacer(),
SizedBox(
height: 10,
),
],
),
),
),
);
}
}
Sing-Up Screen code
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:securisk_app/utlity/colors.dart';
import 'package:securisk_app/widgets/bg_image.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
class regForm extends StatefulWidget {
@override
State<regForm> createState() => _regFormState();
}
class _regFormState extends State<regForm> {
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final mobileController = TextEditingController();
final emailController = TextEditingController();
final confirmPassController = TextEditingController();
final passController = TextEditingController();
String googleApikey = "your google map api key";
GoogleMapController? mapController;
CameraPosition? cameraPosition;
LatLng startLocation = LatLng(26.4839, 80.27508);
String location = "Location Name:";
bool isShowPass = false;
bool onError = false;
Widget googleMapUI() {
double width = MediaQuery.of(context).size.width;
return SizedBox(
// height: MediaQuery.of(context).size.height / 1.8,
height: MediaQuery.of(context).size.height / 3,
width: width,
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 30),
child: Stack(children: [
GoogleMap(
//Map widget from google_maps_flutter package
zoomGesturesEnabled: true,
myLocationEnabled: true,
//enable Zoom in, out on map
initialCameraPosition: CameraPosition(
//innital position in map
target: startLocation, //initial position
zoom: 14.0, //initial zoom level
),
mapType: MapType.normal, //map type
onMapCreated: (controller) {
//method called when map is created
setState(() {
mapController = controller;
});
},
onCameraMove: (CameraPosition cameraPositiona) {
cameraPosition = cameraPositiona; //when map is dragging
},
onCameraIdle: () async {
//when map drag stops
List<Placemark> placemarks = await placemarkFromCoordinates(
cameraPosition!.target.latitude,
cameraPosition!.target.longitude);
setState(() {
//get place name from lat and lang
location = placemarks.first.street.toString() +
', ' +
placemarks.first.subAdministrativeArea.toString() +
',' +
placemarks.first.locality.toString() +
'-' +
placemarks.first.postalCode.toString() +
',' +
placemarks.first.administrativeArea.toString();
});
},
),
Center(
//picker image on google map
child: Image.asset(
"assets/images/picker.png",
width: 80,
),
),
])),
],
),
);
}
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/bg.png"),
fit: BoxFit.cover,
),
),
child: Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: Text('Sign-up', style: TextStyle(color: Colors.black)),
backgroundColor: Colors.transparent,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.red),
onPressed: () => Navigator.of(context).pop(),
),
elevation: 0,
),
backgroundColor: Colors.transparent,
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
googleMapUI(),
Container(
height: 2,
width: double.infinity,
color: Colors.amber,
),
Container(
height: 90,
child: Padding(
padding: EdgeInsets.only(left: 27, right: 29, bottom: 5),
child: Card(
child: Container(
padding: EdgeInsets.all(0),
width: MediaQuery.of(context).size.width - 40,
child: ListTile(
leading: Image.asset(
"assets/images/picker.png",
width: 25,
),
title: Text(
location,
style: TextStyle(fontSize: 18),
),
dense: true,
)),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 4),
child: TextFormField(
style: TextStyle(color: Colors.black, fontSize: 14),
controller: emailController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.account_circle_sharp),
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
labelText: "Enter your name",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: Colors.grey.shade400, fontSize: 14),
),
// inputFormatters: [new LengthLimitingTextInputFormatter(10)],
validator: (String? val) {
String patttern = r'(^[0-9]*$)';
RegExp regExp = new RegExp(patttern);
if (val!.isEmpty) {
return "Mobile is Required";
} else if (val.length != 10) {
return "Mobile number must 10 digits";
} else if (!regExp.hasMatch(val)) {
return "Mobile Number must be digits";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child:
Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 5,
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: TextFormField(
style: TextStyle(color: Colors.black, fontSize: 14),
controller: emailController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email_rounded),
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
labelText: "Enter your email",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: Colors.grey.shade400, fontSize: 14),
),
// inputFormatters: [new LengthLimitingTextInputFormatter(10)],
validator: (String? val) {
String patttern = r'(^[0-9]*$)';
RegExp regExp = new RegExp(patttern);
if (val!.isEmpty) {
return "Mobile is Required";
} else if (val.length != 10) {
return "Mobile number must 10 digits";
} else if (!regExp.hasMatch(val)) {
return "Mobile Number must be digits";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child:
Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 5,
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: Column(children: <Widget>[
Container(
child: IntlPhoneField(
decoration: InputDecoration(
//decoration for Input Field
labelText: 'Phone Number',
border: OutlineInputBorder(
borderSide: BorderSide(),
),
),
initialCountryCode:
'IN', //default contry code, NP for Nepal
onChanged: (phone) {
//when phone number country code is changed
print(phone.completeNumber); //get complete number
print(phone.countryCode); // get country code only
print(phone.number); // only phone number
},
)),
])),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child:
Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 5,
),
Container(
padding: EdgeInsets.only(left: 30, right: 35, top: 10),
child: TextFormField(
controller: nameController,
obscureText: isShowPass,
decoration: InputDecoration(
labelText: "Password",
alignLabelWithHint: true,
prefixIcon: Icon(Icons.lock),
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: EdgeInsets.fromLTRB(8, 5, 10, 5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(2.0),
),
labelStyle: TextStyle(
color: Colors.grey.shade400, fontSize: 14),
suffixIcon: GestureDetector(
onTap: () {
setState(() {
isShowPass = !isShowPass;
});
},
child: Icon(
isShowPass
? Icons.visibility
: Icons.visibility_off,
size: 16,
)),
),
// inputFormatters: [new LengthLimitingTextInputFormatter(10)],
validator: (String? val) {
String patttern = r'(^[0-9]*$)';
RegExp regExp = new RegExp(patttern);
if (val!.isEmpty) {
return "Mobile is Required";
} else if (val.length != 10) {
return "Mobile number must 10 digits";
} else if (!regExp.hasMatch(val)) {
return "Mobile Number must be digits";
}
return null;
},
)),
onError
? Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.only(left: 30),
child:
Text('', style: TextStyle(color: Colors.red)),
))
: Container(),
SizedBox(
height: 5,
),
Padding(
padding: EdgeInsets.only(left: 35, right: 35),
child: Container(
padding: EdgeInsets.all(8.0),
// height: 100,
child: Row(
children: [
Expanded(
child: Container(
//height: 100,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: AppColors.buttonColor,
onPrimary: Colors.white,
side: BorderSide(
color: AppColors.buttonColor, width: 5),
),
onPressed: () async {
//next screen
},
child: Text("Next"),
),
)),
],
),
),
),
],
),
),
)),
);
}
}
Adding the Google Maps Plugin
To get started using Google Maps Plugin open the pubspec.yaml file and under dev_dependencies paste the following, using the same indentation as below and it’s to be added to the terminal.
Android Setup in the flutter app
Next, open android ‣ app ‣ src ‣ main ‣ AndroidManifest.xml and paste the Google Maps metadata tag into your application tag before the activity tag, placing the API key you copied before to replace {your_api_key_here}.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.login_app">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<queries>
<!-- If your app opens https URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<!-- If your app makes calls -->
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<!-- If your app emails -->
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
</intent>
</queries>
<application
android:label="login_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<!-- <meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" /> -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="your_api_key_here"/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
</application>
</manifest>
iOS Setup in the flutter app
Next, open the ios ‣ Runner ‣ AppDelegate.m file and paste the following code before the comment and return statement.
import UIKit
import Flutter
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("your_api_key_here")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
main. dart file
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:login_app/other_desgin/screens/google_map.dart';
import 'package:login_app/other_desgin/screens/home/main_screen.dart';
import 'package:login_app/other_desgin/screens/login_and_reg/login.dart';
import 'package:login_app/utlity/colors.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: '',
debugShowCheckedModeBanner: false,
theme: ThemeData(
// primarySwatch: Colors.blue,
// platform: TargetPlatform.iOS,
),
home: MyHomePage(),
routes: {
"/login": (context) => MainLogin(),
"/singUp": (context) => SignupForm()
"/successScreen": (context) => SuccessScreen(),
});
}
}
BgImage Class
import 'package:flutter/material.dart';
class BgImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.only(top: 100.0, left: 24, right: 20),
child: Image.asset(
"assets/images/bg.png",
fit: BoxFit.cover,
),
),
);
}
}
App Colors Class
import 'package:flutter/material.dart';
class AppColors {
static final Color spleshColor = Color(0XFFDEDEDE);
static final Color blueColor = Color(0XFF0c99c3);
static final Color greenColor = Color(0XFF3dc39d);
static final Color yellowColor = Color(0XFFdac007);
static final Color redColor = Color(0XFFbe0b2b);
static final Color orangeColor = Color(0XFFbe740b);
}
See the final output
I hope it was a useful article, please share and subscribe to my channel, Thanks for reading and if you have any questions or comments, See you soon.