flutter : How to use shared preferences to keep user logged in - flutter

I want to keep the user logged in after the user successfully logsin in flutter. I am using a REST API to retrieve the user name and password of the user. But I want to save those details so that the user can stay logged in. My current situation is i can successfully log the user in but when i restart the app i have to login again so i need to save the details of the user in a shared preference so that the user can stay logged for the entire session until logout.But i am unable to do that so please help me with it. Thanks in advance
Login.dart
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(3, 9, 23, 1),
body: Container(
padding: EdgeInsets.only(
top: 100,
right: 30,
left: 30,
),
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FadeAnimation(
1.2,
Container(
padding: EdgeInsets.all(70.0),
margin: EdgeInsets.only(bottom: 50.0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/trakinglogo.png'))),
)),
SizedBox(
height: 30,
),
FadeAnimation(
1.5,
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey[300]))),
child: TextFormField(
controller: _emailController,
onFieldSubmitted: (_) =>
FocusScope.of(context).nextFocus(),
textInputAction: TextInputAction.done,
validator: (value) {
if (value.isEmpty) {
return 'Email is required';
}
return null;
},
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Colors.grey.withOpacity(.8)),
hintText: "Votre adresse mail"),
),
),
Container(
decoration: BoxDecoration(),
child: TextFormField(
controller: _passwordController,
validator: (value) {
if (value.isEmpty) {
return 'Password is required';
}
return null;
},
obscureText: _isHidden,
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Colors.grey.withOpacity(.8)),
hintText: "Mot de passe",
suffix: InkWell(
onTap: _togglePasswordView,
child: Icon(
_isHidden
? (CommunityMaterialIcons
.eye_outline)
: (CommunityMaterialIcons.eye_off),
color: Color(0xFF939394),
SizedBox(
height: 40,
),
FadeAnimation(
1.8,
Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: RaisedButton(
textColor: Colors.white,
color: kPrimaryColor,
child: Text("Se connecter"),
onPressed: () async {
if (_formKey.currentState.validate()) {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child:
CircularProgressIndicator(),
);
});
await loginUser();
}
/* Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyWidget()),
);*/
// Navigator.pushNamed(context, 'Mywidget');
},
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(30.0),
),
),
width: 250,
padding: EdgeInsets.all(15),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AlreadyHaveAnAccountCheck(
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return SignUp();
void loginUser() async {
// if (_formKey.currentState.validate()) {
setState(() {
_isLoading = true;
});
String email = _emailController.text;
String password = _passwordController.text;
authentication.login(email, password).then((user) {
if (user != null)
Navigator.push(
context, MaterialPageRoute(builder: (context) => MyWidget()));
}).catchError((error) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.toString())));
});
setState(() {
_isLoading = false;
});
}
and this is login function :
Future<User> login(String email, String password) async {
await checkInternet();
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
Map<String, String> body = {'email': email, 'password': password};
var response = await http.post(Uri.parse(ApiUtil.AUTH_LOGIN),
headers: headers, body: jsonEncode(body));
switch (response.statusCode) {
case 200:
var body = jsonDecode(response.body);
var data = body['user'];
User user = User.fromJson(data);
Track track = Track.fromJson(body);
if (body['code'] == 0) {
SharedPreferences localStorage =
await SharedPreferences.getInstance();
localStorage.setInt('id', body['user']['id']);
localStorage.setString('adress', body['user']['adress']);
localStorage.setString('phone', body['user']['phone']);
localStorage.setString('access_token', body['access_token']);
localStorage.setString('user', json.encode(body['user']));
String user = localStorage.getString('user');
}
return user;
case 500:
throw ('Erreur serveur');
break;
case 400:
throw LoginFailed();
default:
throw ('connection timeout');
break;
}
}

Make a class AppSharedPreference and in that class make these methods:
User is your modal class.
Save User:
static saveUser(String key, User value) async {
final prefs = await SharedPreferences.getInstance();
//We cannot save custom objects.
//So json.encode is used to convert User Object to Json a string.
prefs.setString(key, json.encode(value));
}
Get User:
static readUser(String key) async {
final prefs = await SharedPreferences.getInstance();
//json.decode to convert json string back to Json Object
return json.decode(prefs.getString(key));
}
Make Sure your modal User class contains toJson method:
Map<String, String> toJson() => {
'id': Id,
'address': address,
'phone': phone,
// rest of your attributes...
};
To call the saveUser method:
await AppSharedPreference.saveUser('your_key', user);
To call the readUser method:
(make sure your modal User class contains fromJson method)
User user = User.fromJson(await AppSharedPreference.readUser('your_key'));
And after that you can check whether the user is null or not. If it is null it means there is no data and the user is not logged-in.
In case if you want to logout user you can create a method to remove the user from SharedPreference:
Remove User:
static removeUser(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}
To call removeUser method:
await AppSharedPreference.removeUser('your_key');

You have to check if there is data in the sharedPreference at the starting of the app and then redirect users based on that. An example would be implementing this in splashscreen
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'home_page.dart';
import 'login_page.dart';
class SplashScreen extends StatefulWidget {
static const String splashRoute = '/';
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
void timeToShowSplashScreenOnScreen() async {
Timer(Duration(seconds: 3), () async {
if (!mounted) return;
SharedPreferences _prefs = await SharedPreferences.getInstance();
bool isLoggedIn = _prefs.getBool("loggedIn");
if (isLoggedIn != null && isLoggedIn) {
Navigator.of(context).pushNamedAndRemoveUntil(HomePage.homePageRoute, (route) => false);
} else {
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.loginRoute, (route) => false);
}
});
}
#override
void initState() {
super.initState();
timeToShowSplashScreenOnScreen();
}
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
double _height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
width: _width,
height: _height,
child: Stack(
children: [
// Your UI for splash screen
],
),
),
);
}
}
This example uses boolean values stored in prefs but you can do similar things with strings too.

Related

Error: NoSuchMethodError: 'then' Dynamic call of null. Receiver: null

Does anyone know the cause of this error? I have tried many ways but still don't know where the problem is.
Database.dart
import 'package:fitness_app/Login/login_data.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
static final DatabaseService _databaseService = DatabaseService._internal();
factory DatabaseService() => _databaseService;
DatabaseService._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'conference.database');
return await openDatabase(
path,
onCreate: _onCreate,
version: 1,
onConfigure: (db) async => await db.execute('PRAGMA foreign_keys = ON'),
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE login(id INTEGER PRIMARY KEY, name TEXT, username TEXT, password TEXT)',
);
}
verifyuser(String user, String pass) {}
insertLoginData(Logindata logindata) {}
}
Login.dart
import 'package:fitness_app/Login/signup.dart';
import 'package:flutter/material.dart';
import 'login_data.dart';
import 'package:fitness_app/Database/database.dart';
import 'package:fitness_app/home_page.dart';
class Login extends StatefulWidget {
const Login({Key? key, this.login}) : super(key: key);
final Logindata? login;
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
String email = "a";
String pass = "a";
TextEditingController emails = TextEditingController();
TextEditingController password = TextEditingController();
final _formKey = GlobalKey<FormState>();
static final List<Logindata> _login = [];
final DatabaseService _databaseService = DatabaseService();
Future<List<Logindata>> _getLogin() async {
await _databaseService.verifyuser(email, pass).then((value) {
if (value) {
AlertDialog alert = AlertDialog(
title: const Text('Login successful!'),
content: const Text('Welcome!'),
actions: <Widget>[
TextButton(
onPressed: () => (Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
)),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
} else {
AlertDialog alert = AlertDialog(
title: const Text('Error!'),
content: const Text('Wrong Email or Password'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
});
return _login;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
Container(
padding: EdgeInsets.all(10),
child: Form(
key: _formKey,
child: Column(
children: [
Text(
'BeFit:Fitness Activity Tracker Progress\n\n',
style: TextStyle(fontSize: 24),
textAlign: TextAlign.start,
),
Text(
'Welcome',
style: TextStyle(fontSize: 40),
textAlign: TextAlign.center,
),
SizedBox(
height: 30,
),
Container(
child: TextFormField(
controller: emails,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
child: TextFormField(
controller: password,
obscureText: true,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
width: MediaQuery.of(context).size.width,
height: 50,
child: FlatButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
email = emails.text;
pass = password.text;
_getLogin();
print(email);
print(pass);
print('success');
}
},
child: Text("Login"),
textColor: Colors.white,
color: Colors.deepPurple[400],
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(10)),
),
),
SizedBox(
height: 20,
),
Container(
child: Row(
children: <Widget>[
Text('Does not have account?'),
FlatButton(
textColor: Colors.deepPurpleAccent[100],
child: Text(
'Sign up',
style: TextStyle(fontSize: 16),
),
onPressed: () {
//signup screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SignUp()),
);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
))
],
),
),
)
],
),
),
),
);
}
}
Here is the error
enter image description here
I'm using this flutter to complete my project but there are some errors that I can't solve it. Sorry if my coding looks not right because still lacks in coding
Your verifyuser function does not return anything so your value returns null. You either need to return something or check for null values in your then statement.
Future<String> verifyuser(String user, String pass)async {
return user + pass;
}

How to pass variable and get data from API in flutter?

This the first UI in there when user enter channel name and after click join then should it pass "loadData" method in there that channel name should pass to "API" and get channelname and appId from that url.
join button code
Future<void> onJoin() async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => loadData(myController.text)),
);
}
loadData method
import 'dart:convert';
import 'package:http/http.dart';
import '../model/appIdModel.dart';
class ApiService {
loadData(String myController) async {
final String url ='https://jsonplaceholder.typicode.com/posts/1=$myController';
Future<List<Data>> getData() async {
Response response = await get(Uri.parse(url));
if (response.statusCode == 2000) {
Map<String, dynamic> json = jsonDecode(response.body);
List<dynamic> body = json['data'];
List<Data> datas = body.map((dynamic item) => Data.fromJson(item).toList();
return datas;
} else {
throw ('cannot fetch data');
}
}
}
}
Data model code
class Data {
String appId;
String channelName;
Data({
required this.appId,
required this.channelName,
});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
appId: json['appId'] == null ? null : json['appId'],
channelName: json['channelName'] == null ? null : json['channelName']);
}
}
then appId and channelName should fetch
FutureBuilder widget code show channelId and channelName (Home page code)
class _MyHomePageState extends State<MyHomePage> {
final myController = TextEditingController();
bool _validateError = false;
ApiService client = ApiService();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
clipBehavior: Clip.antiAliasWithSaveLayer,
physics: BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 20)),
Padding(padding: EdgeInsets.symmetric(vertical: 20)),
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: TextFormField(
controller: myController,
decoration: InputDecoration(
labelText: 'Channel Name',
labelStyle: TextStyle(color: Colors.blue),
hintText: 'test',
hintStyle: TextStyle(color: Colors.black45),
errorText:
_validateError ? 'Channel name is mandatory' : null,
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
),
),
),
Padding(padding: EdgeInsets.symmetric(vertical: 30)),
Container(
width: MediaQuery.of(context).size.width * 0.25,
child: MaterialButton(
onPressed: onJoin,
height: 40,
color: Colors.blueAccent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'Join',
style: TextStyle(color: Colors.white),
),
Icon(
Icons.arrow_forward,
color: Colors.white,
),
],
),
),
),
Center(
child: FutureBuilder(
future: client.getData(),
builder: (BuildContext context,
AsyncSnapshot<List<Data>> snapshot) {
if (snapshot.hasData) {
List<Data>? data = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) => Column(
children: [
Text(
data![index].channelName.toString(),
),
Text(
data[index].appId.toString(),
),
],
));
}
return const Center(
child: CircularProgressIndicator(),
);
}),
)
],
),
),
),
),
);
}
Future<void> onJoin() async {
setState(() {
myController.text.isEmpty
? _validateError = true
: _validateError = false;
});
// await _handleCameraAndMic(Permission.camera);
// await _handleCameraAndMic(Permission.microphone);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => loadData(myController.text)),
);
}
this is the my code to fetch data.when I run the url with channel name then data show nicely.
I tired to fetch "channelName" and "appId" using this url.
You are doing wrong when you call onJoin, change it to this:
Future<void> onJoin() async {
future = client.getData(myController.text);
}
then define new variable like this:
Future<List<Data>>? future;
then change ApiService to this:
class ApiService {
final String url =
'https://jsonplaceholder.typicode.com/posts/1';
Future<List<Data>> getData(String myController) async {
Response response = await get(Uri.parse(url + myController));
if (response.statusCode == 200) { // <--- change this
Map<String, dynamic> json = jsonDecode(response.body);
List<dynamic> body = json['data'];
List<Data> datas = body.map((dynamic item) => Data.fromJson(item).toList();
return datas;
} else {
throw ('cannot fetch data');
}
}
}
then change your FutureBuilder to this
future != null ? FutureBuilder(//<--- add this
future: future,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
future = null; //<--- add this
List<Data> data = snapshot.data ?? []; //<-- change this
return ListView.builder(
itemCount: data.length, //<-- add this
itemBuilder: (context, index) => Column(
children: [
Text(
data[index].channelName.toString(),
),
Text(
data[index].appId.toString(),
),
],
));
}
}
},
)
: SizedBox(),//<--- add this
also as you can see in your postman result the data contain a map, not list of map, so if you expect a list you need to contact to your backend but if not you can parse it like this:
Map<String, dynamic> body = json['data'];
List<Data> datas = [Data.fromJson(body)];
also for ui issue you can't use listview inside SingleChildScrollView, for that you need set shrinkWrap to true, also set its physics to NeverScrollableScrollPhysics too:
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: ...
)

how to store Strings from TextFormField to another page

I have three text form fields on the sign-in page
There are two of them for username and password that work well, I have no problem with them
But the first one is for entering the domain (it is programmed so that each client has its own domain), which is my problem.
I want that when the user enters his domain, the URL of the program, which is in the IP page, is changed and the sign-in page is called only once, without the need to call other pages.
thsis is signIn page code:
I suggest that you pay attention to _signIn and ElevatedButton.
import 'package:flutter/material.dart';
import 'package:mis_project/api/my_api.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:mis_project/conts/myString.dart';
import '../auth/auth_page.dart';
import '../conts/colors.dart';
import '../conts/mSize.dart';
class SignIn extends StatefulWidget {
const SignIn({Key key}) : super(key: key);
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
String postDomain;
bool isLoading = false;
PageController _pageController;
List<TextEditingController> tecList;
#override
void initState() {
super.initState();
_pageController = PageController();
tecList = List.generate(3, (index) {
return TextEditingController();
});
WidgetsBinding.instance.addPostFrameCallback((_) {
_showModalBottomSheet(context);
});
}
_signIn() async {
print('Sign is is called');
var data = {
'email': tecList[1].text,
'password': tecList[2].text,
};
var jsonResponse = null;
SharedPreferences localStorage = await SharedPreferences.getInstance();
var response = await CallApi().postData(data, 'login');
jsonResponse = json.decode(response.body);
if (jsonResponse != null) {
setState(() {});
localStorage.setString('token', jsonResponse['token']);
Navigator.push(
context, MaterialPageRoute(builder: (context) => AuthPage()));
} else {
setState(() {});
}
}
final _formKey = GlobalKey<FormState>();
#override
void dispose() {
for (var tec in tecList) {
tec.dispose();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: background_page,
body: isLoading
? Center(
child: CircularProgressIndicator(),
)
: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/image/background.jpg'),
fit: BoxFit.cover),
),
),
);
}
void _showModalBottomSheet(BuildContext context) {
showModalBottomSheet<void>(
elevation: 30,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(30),
),
),
context: context,
builder: (BuildContext context) {
return SizedBox(
height: mDeviceSize(context).height * 0.71,
width: mDeviceSize(context).width,
child: PageView.builder(
controller: _pageController,
itemCount: 3,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
top: 40, left: 25, right: 25, bottom: 15),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
textAlign: TextAlign.left,
textInputAction: TextInputAction.go,
style: TextStyle(color: Color(0xFF000000)),
cursorColor: view_all_color_HP,
controller: tecList[index],
keyboardType: TextInputType.text,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: view_all_color_HP,
),
),
contentPadding: EdgeInsets.symmetric(
vertical: 5.0, horizontal: 10),
labelText: signing_input_label[index],
labelStyle: TextStyle(color: view_all_color_HP),
floatingLabelBehavior: FloatingLabelBehavior.auto,
border: OutlineInputBorder(),
alignLabelWithHint: true,
hintStyle: TextStyle(
color: view_all_color_HP,
fontSize: 15,
fontWeight: FontWeight.normal),
),
autofocus: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
int nextPage = index + 1;
if (nextPage < 3) {
_pageController.animateToPage(nextPage,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
}
if (index == 0) {
postDomain = tecList[0].text;
print('domain post is : $postDomain');
print('domain value is: ${tecList[0].text}');
}
if (index == 1) {
print('email value is: ${tecList[1].text}');
}
if (index == 2) {
print('pass: ${tecList[2].text}');
_signIn();
}
},
child: Text(signing_button_label[index]),
style: ElevatedButton.styleFrom(
elevation: 0,
primary: view_all_color_HP,
fixedSize:
Size(mDeviceSize(context).width * 0.3, 5),
minimumSize: Size(0, 45),
),
),
SizedBox(
height: MediaQuery.of(context).viewInsets.bottom,
),
],
),
),
),
],
);
},
),
);
},
);
}
}
and this is my APi page
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class CallApi{
final String _urlF = 'https://';
final String _urlE = '.lajwardsoftwares.com/api/v1/';
postData(data, apiUrl) async {
var fullUrl = _urlF + _urlE + apiUrl + await _getToken();
print('full url is: $fullUrl');
return await http.post(
Uri.parse(fullUrl),
body: jsonEncode(data),
headers: _setHeaders()
);
}
getData(apiUrl) async {
var fullUrl = _urlF + apiUrl + await _getToken();
return await http.get(
Uri.parse(fullUrl),
headers: _setHeaders()
);
}
_setHeaders() => {
'Content-type' : 'application/json',
'Accept' : 'application/json',
};
_getToken() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var token = localStorage.getString('token');
return '?token=$token';
// print("saied: getToken ${localStorage.getString('token')}"),};
}
}
i want this .lajwardsoftwares.com to stored from TextFromField
thanks

I'm implementing google sign in using flutter and firestore. On click, user should should sign in with google and navigate user to a create user page

I have a google_sign_in button which when clicked should present the google sign in then the user should be redirected to a CreateAccountPage. The page has an input and a submit button. When the user submits their preferred username, the google account data together with the created username should be stored as a document in users collection cloud firestore database and the user redirected to another page, say TimelinePage.
Below is my home.dart file:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:musagram/pages/activity_feed.dart';
import 'package:musagram/pages/create_account.dart';
import 'package:musagram/pages/profile.dart';
import 'package:musagram/pages/search.dart';
import 'package:musagram/pages/timeline.dart';
import 'package:musagram/pages/upload.dart';
final GoogleSignIn googleSignIn = GoogleSignIn();
final usersRef = FirebaseFirestore.instance.collection('users');
final DateTime timestamp = DateTime.now();
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isAuth = false;
PageController pageController;
int pageIndex = 0;
#override
void initState() {
super.initState();
pageController = PageController();
// Detects when user signed in
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
// Reauthenticate user when app is opened
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) {
if (account != null) {
createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
});
}
}
createUserInFirestore() async {
// 1) check if user exists in users collection in database (according to their id)
final GoogleSignInAccount user = googleSignIn.currentUser;
final DocumentSnapshot doc = await usersRef.doc(user.id).get();
if (!doc.exists) {
// 2) if the user doesn't exist, then we want to take them to the create account page
final username = await Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateAccount()));
// 3) get username from create account, use it to make new user document in users collection
usersRef.doc(user.id).set({
"id": user.id,
"username": username,
"photoUrl": user.photoUrl,
"email": user.email,
"displayName": user.displayName,
"bio": "",
"timestamp": timestamp
});
}
}
#override
void dispose() {
pageController.dispose();
super.dispose();
}
login() {
googleSignIn.signIn();
}
logout() {
googleSignIn.signOut();
}
onPageChanged(int pageIndex) {
setState(() {
this.pageIndex = pageIndex;
});
}
onTap(int pageIndex) {
pageController.animateToPage(
pageIndex,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
Scaffold buildAuthScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
// Timeline(),
ElevatedButton(
child: Text('Logout'),
onPressed: logout,
),
ActivityFeed(),
Upload(),
Search(),
Profile(),
],
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: pageIndex,
onTap: onTap,
activeColor: Theme.of(context).primaryColor,
items: [
BottomNavigationBarItem(icon: Icon(Icons.whatshot)),
BottomNavigationBarItem(icon: Icon(Icons.notifications_active)),
BottomNavigationBarItem(
icon: Icon(
Icons.photo_camera,
size: 35.0,
),
),
BottomNavigationBarItem(icon: Icon(Icons.search)),
BottomNavigationBarItem(icon: Icon(Icons.account_circle)),
]),
);
// return RaisedButton(
// child: Text('Logout'),
// onPressed: logout,
// );
}
Scaffold buildUnAuthScreen() {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).accentColor,
Theme.of(context).primaryColor,
],
),
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'MusaGram',
style: TextStyle(
fontFamily: "Signatra",
fontSize: 90.0,
color: Colors.white,
),
),
GestureDetector(
onTap: login,
child: Container(
width: 260.0,
height: 60.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/google_signin_button.png',
),
fit: BoxFit.cover,
),
),
),
)
],
),
),
);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
}
And here's the create_account.dart
import 'package:flutter/material.dart';
import 'package:musagram/widgets/header.dart';
class CreateAccount extends StatefulWidget {
#override
_CreateAccountState createState() => _CreateAccountState();
}
class _CreateAccountState extends State<CreateAccount> {
final _formKey = GlobalKey<FormState>();
String username;
submit() {
_formKey.currentState.save();
Navigator.pop(context, username);
}
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: header(context, titleText: "Set up your profile"),
body: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 25.0),
child: Center(
child: Text(
"Create a username",
style: TextStyle(fontSize: 25.0),
),
),
),
Padding(
padding: EdgeInsets.all(16.0),
child: Container(
child: Form(
key: _formKey,
child: TextFormField(
onSaved: (val) => username = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Username",
labelStyle: TextStyle(fontSize: 15.0),
hintText: "Must be at least 3 characters",
),
),
),
),
),
GestureDetector(
onTap: submit,
child: Container(
height: 50.0,
width: 350.0,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(7.0),
),
child: Center(
child: Text(
"Submit",
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),),
),
),
],
),
),
],
),
);
}
}
method of google sign in looks like this. It will first authenticate the user then it will check is users data present on the firestore. if it's present data will not be overridden and else data will be added.
also at the end you can navigate to your createAccountPage. and you can pass the required values to the constructor. directly map of data or values.
Future<UserCredential> signInWithGoogle() async {
//first trigger authentication
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
//obtain the auth details
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//create a new credentials
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
//saving the user data to shared preferences
SharedPrefsHelper _sharedpref = new SharedPrefsHelper();
// // sign in method
try {
UserCredential firebaseUser =
await FirebaseAuth.instance.signInWithCredential(credential);
if (firebaseUser != null) {
// Check is already sign up
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('users')
.where('id', isEqualTo: firebaseUser.user.uid)
.get();
//userdata to update at firebase
//passing this via constructor to the next page
Map<String, dynamic> userdata = {
'useremail': firebaseUser.user.email,
'displayname': firebaseUser.user.displayName,
'photoUrl': firebaseUser.user.photoURL,
'userid': firebaseUser.user.uid,
};
// navigate . if you don't have context here you can pass this as a paramater to the
method. no issue if you call inside build or init
//userdata is map. just for handling easily
//you can also create simple variables and pass each value to them.
Navigator.pushReplacement(context, CreateUserAccount(userdata));
}
return firebaseUser;
} catch (error) {
print(error);
}
}
Inside the CreateUserAccount
class CreateUserAccount extends StatefulWidget {
final Map<String, dynamic> userdata;
CreateUserAccount(this.userdata);
#override
CreateUserAccountState createState() => CreateUserAccountState();
}
class CreateUserAccountState extends State<CreateUserAccount> {
TextEditingController _textController = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: TextField(
controller: _textController,
),
),
ElevatedButton(
onPressed: () async{
//get the username from textfield
//and update data to firebase
widget.userdata['username'] = _textController.text;
await FirebaseFirestore.instance
.collection('users')
.doc(widget.userdata['userId'])
.set(
widget.userdata,
SetOptions(merge: true),
);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => TimeLinePage());
},
child: Text("submit"),
),
],
),
));
}
}
Inside the CreateUserAccount we simply added the username in map and added record in firebase. Then you can navigate to the timeline after this.

How keep user logged in flutter

I posted yesterday this question but i didn't get any valid answer. My current situation is i can successfully log the user in but when i restart the app i have to login again so i need to save the details of the user in a shared preference so that the user can stay logged for the entire session until logout.But i am unable to do that so please help me with it. Thanks in advance
login.dart :
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(3, 9, 23, 1),
body: Container(
padding: EdgeInsets.only(
top: 100,
right: 30,
left: 30,
),
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FadeAnimation(
1.2,
Container(
padding: EdgeInsets.all(70.0),
margin: EdgeInsets.only(bottom: 50.0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/trakinglogo.png'))),
)),
SizedBox(
height: 30,
),
FadeAnimation(
1.5,
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey[300]))),
child: TextFormField(
controller: _emailController,
onFieldSubmitted: (_) =>
FocusScope.of(context).nextFocus(),
textInputAction: TextInputAction.done,
validator: (value) {
if (value.isEmpty) {
return 'Email is required';
}
return null;
},
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Colors.grey.withOpacity(.8)),
hintText: "Votre adresse mail"),
),
),
Container(
decoration: BoxDecoration(),
child: TextFormField(
controller: _passwordController,
validator: (value) {
if (value.isEmpty) {
return 'Password is required';
}
return null;
},
obscureText: _isHidden,
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Colors.grey.withOpacity(.8)),
hintText: "Mot de passe",
suffix: InkWell(
onTap: _togglePasswordView,
child: Icon(
_isHidden
? (CommunityMaterialIcons
.eye_outline)
: (CommunityMaterialIcons.eye_off),
color: Color(0xFF939394),
SizedBox(
height: 40,
),
FadeAnimation(
1.8,
Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: RaisedButton(
textColor: Colors.white,
color: kPrimaryColor,
child: Text("Se connecter"),
onPressed: () async {
if (_formKey.currentState.validate()) {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child:
CircularProgressIndicator(),
);
});
await loginUser();
}
/* Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyWidget()),
);*/
// Navigator.pushNamed(context, 'Mywidget');
},
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(30.0),
),
),
width: 250,
padding: EdgeInsets.all(15),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AlreadyHaveAnAccountCheck(
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return SignUp();
void loginUser() async {
// if (_formKey.currentState.validate()) {
setState(() {
_isLoading = true;
});
String email = _emailController.text;
String password = _passwordController.text;
authentication.login(email, password).then((user) {
if (user != null)
Navigator.push(
context, MaterialPageRoute(builder: (context) => MyWidget()));
}).catchError((error) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.toString())));
});
setState(() {
_isLoading = false;
});
}
and this is login function :
Future<User> login(String email, String password) async {
await checkInternet();
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
Map<String, String> body = {'email': email, 'password': password};
var response = await http.post(Uri.parse(ApiUtil.AUTH_LOGIN),
headers: headers, body: jsonEncode(body));
switch (response.statusCode) {
case 200:
var body = jsonDecode(response.body);
var data = body['user'];
User user = User.fromJson(data);
Track track = Track.fromJson(body);
if (body['code'] == 0) {
SharedPreferences localStorage =
await SharedPreferences.getInstance();
localStorage.setInt('id', body['user']['id']);
localStorage.setString('adress', body['user']['adress']);
localStorage.setString('phone', body['user']['phone']);
localStorage.setString('access_token', body['access_token']);
localStorage.setString('user', json.encode(body['user']));
String user = localStorage.getString('user');
}
return user;
case 500:
throw ('Erreur serveur');
break;
case 400:
throw LoginFailed();
default:
throw ('connection timeout');
break;
}
}
When i do the first login i'm saving the data on the shared preferences
ApiRepository.get().login(LoginRequest(username: _emailController.text, password: _passwordController.text)).then((response) async {
if (response != null) {
//save on the shared preferences that the user is logged in
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool(SHARED_LOGGED, true);
await prefs.setString(SHARED_USER, _emailController.text);
await prefs.setString(SHARED_PASSWORD, _passwordController.text);
}
}).catchError((error) {
});
Everytime i'm opening the app i have a splashscreen, here i do an implicit login and than i make skip the login page and bring the user on the home page
void checkUserIsLogged() async {
final prefs = await SharedPreferences.getInstance();
if ((prefs.getBool(SHARED_LOGGED) != null) && prefs.getBool(SHARED_LOGGED)) {
ApiRepository.get().login(LoginRequest(username: prefs.getString(SHARED_USER), password: prefs.getString(SHARED_PASSWORD))).then((response) {
if (response != null) {
//"do something"
}
}).catchError((error) {
});
} else {
}
}
FULL code
SplashPageLoading.dart
class SplashPageLoading extends StatefulWidget {
#override
_SplashPageLoadingState createState() => _SplashPageLoadingState();
}
class _SplashPageLoadingState extends State<SplashPageLoading> {
bool _doLogin = false;
#override
void initState() {
super.initState();
new Future.delayed(const Duration(seconds: 3), () => checkUserIsLogged());
}
void checkUserIsLogged() async {
final prefs = await SharedPreferences.getInstance();
if ((prefs.getBool(SHARED_LOGGED) != null) && prefs.getBool(SHARED_LOGGED)) {
setState(() {
_doLogin = true;
});
ApiRepository.get().login(LoginRequest(username: prefs.getString(SHARED_USER), password: prefs.getString(SHARED_PASSWORD))).then((response) {
if (response != null) {
Navigator.of(context).pushReplacementNamed(HomePage.routeName);
}
}).catchError((error) {
Navigator.of(context).pushReplacementNamed(LoginPage.routeName);
});
} else {
new Future.delayed(const Duration(seconds: 1), () => Navigator.of(context).pushReplacementNamed(LoginPage.routeName));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(
_doLogin ? "Login.." : "No data for login",
style: TextStyle(color: Colors.black),
),
),
);
}
}
Strings.dart
const String SHARED_LOGGED = "USER_IS_LOGGED";
const String SHARED_USER = "USER";
const String SHARED_PASSWORD = "PASSWORD";
shared preferences library
SplashPageLoading.dart :
class SplashPageLoading extends StatefulWidget {
#override
_SplashPageLoadingState createState() => _SplashPageLoadingState();
}
class _SplashPageLoadingState extends State<SplashPageLoading> {
bool _doLogin = false;
Authentication authentication = Authentication();
static const String SHARED_LOGGED = "USER_IS_LOGGED";
static const String SHARED_USER = "USER";
static const String SHARED_PASSWORD = "PASSWORD";
#override
void initState() {
super.initState();
new Future.delayed(const Duration(seconds: 3), () => checkUserIsLogged());
}
void checkUserIsLogged() async {
final prefs = await SharedPreferences.getInstance();
if ((prefs.getBool(SHARED_LOGGED) != null) &&
prefs.getBool(SHARED_LOGGED)) {
setState(() {
_doLogin = true;
});
authentication
.login(prefs.getString(SHARED_USER), prefs.getString(SHARED_PASSWORD))
.then((user) {
if (user != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyWidget()),
);
// Navigator.of(context).pushReplacementNamed('/splash');
}
}).catchError((error) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyWidget()),
);
// Navigator.of(context).pushReplacementNamed('/splash');
});
} else {
new Future.delayed(const Duration(seconds: 1),
() => Navigator.of(context).pushReplacementNamed('/'));
}
}
#override
Widget build(BuildContext context) {
return Text(
_doLogin ? "Login.." : "",
style: TextStyle(color: Colors.white),
);
}
}