Capture data from TextFormField on flutter for http POST request - dart

i am trying to make a login with flutter.
I am consulting a web service.
I want to send in the body of the Post request the username and the password from different TextFormField. how can i do that?
Here is the code i've been working on.
import 'package:flutter/material.dart';
import 'package:device_id/device_id.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
class SignIn extends StatefulWidget {
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
Future<String> getData() async {
final response = await http.post(
Uri.encodeFull("The route i'm consulting),
body: {
"username": user,
"password": password
},
I want to retrieve the input texts from username and password TextFormField below in here
headers: {
"Accept": "application/json",
});
print(response.statusCode);
print(response.body);
}
String _deviceid = 'Unknown';
String user = '';
String password = '';
TextEditingController controller = new TextEditingController();
TextEditingController controller2 = new TextEditingController();
#override
void dispose() {
controller.dispose();
controller2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
username = TextFormField(
This is the first TextFormField i want to retrieve to send it in the body of the request
controller: controller,
keyboardType: TextInputType.text,
autofocus: false,
decoration: InputDecoration(
hintText: "Username",
hintStyle: TextStyle(fontSize: 16.0),
contentPadding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
border:
UnderlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
final password = TextFormField(
This is the second TextFormField i want to retrieve to send it in the body of the request
controller: controller2,
autofocus: false,
obscureText: true,
decoration: InputDecoration(
hintText: "Password",
hintStyle: TextStyle(fontSize: 16.0),
contentPadding: EdgeInsets.fromLTRB(20.0, 25.0, 20.0, 10.0),
border:
UnderlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
final loginButton = Padding(
padding: EdgeInsets.symmetric(vertical: 25.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
shadowColor: Colors.blueAccent.shade100,
elevation: 10.0,
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
color: Colors.blueAccent,
onPressed: (){
},
child: Text(
"Login",
style: TextStyle(color: Colors.white),
),
),
),
);
return Form(
child: new Center(
child: ListView(
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 10.0),
children: <Widget>[
username,
SizedBox(height: 8.0),
password,
SizedBox(height: 24.0),
loginButton
]),
),
);
}
}

See Retrieve the value of a text field.
Wrap a StatefulWidget around your form
Add two TextEditingController fields in your State, one for each TextFormField
Pass the controllers to your form fields (controller constructor parameter)
Retrieve the values, for example in a button click listener using myController.text
I'm not sure if you are also asking how to send a HTTP post request.
Here is a very minimal example:
class LoginScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextFormField(controller: _usernameController,),
TextFormField(controller: _passwordController, obscureText: true,),
RaisedButton(
onPressed: _performLogin,
child: Text('Login'),
)
],
);
}
void _performLogin() {
String username = _usernameController.text;
String password = _passwordController.text;
print('login attempt: $username with $password');
}
}

Here is a full example of a login Screen ... where you can validate inputs and submit the data after passing the validation.
import 'package:flutter/material.dart';
import '../mixins/validate_mixin.dart';
class LoginScreen extends StatefulWidget{
final GlobalKey<ScaffoldState> scaffoldKey;
LoginScreen(this.scaffoldKey);
#override
State<StatefulWidget> createState() {
return LoginScreenState(scaffoldKey);
}
}
class LoginScreenState extends State<LoginScreen> with ValidateMixin{
final formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> scaffoldKey;
LoginScreenState(this.scaffoldKey);
String _email;
String _password;
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(40.0),
child: Form(
key: formKey,
child: Column(
children: <Widget>[
emailField(),
passwordField(),
Container(margin: EdgeInsets.only(bottom: 25.0),),
submitButton(),
],
),
),
);
}
Widget emailField() {
return TextFormField(
decoration: InputDecoration(hintText: 'ali#gmail.co', labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: validateEmail,
onSaved: (String value) {
_email = value;
},
);
}
Widget passwordField() {
return TextFormField(
obscureText: true,
decoration: InputDecoration(hintText: '*****', labelText: 'Password'),
onSaved: (String value) {
_password = value;
},
);
}
Widget submitButton() {
return RaisedButton.icon(
color: Colors.cyan[900],
textColor: Colors.white,
label: Text('Submit'),
icon: Icon(Icons.save),
onPressed: () {
final bool v = formKey.currentState.validate();
if (v) {
formKey.currentState.save();
_performLogin();
print('object');
}
},);
}
void _performLogin () {
var snackbar = new SnackBar(
content: Text('Email: $_email and Password $_password'),
);
scaffoldKey.currentState.showSnackBar(snackbar);
}
}
You can back to the full example.
https://github.com/anbturki/flutter_login_screen

Related

Receiver:null error when trying to save form using globalKey

I'm following this https://medium.com/#c_innovative/simple-firebase-login-flow-in-flutter-6f44c2b5c58a tutorial and i am having some trouble with getting the login in to work. I have already connected my app to firebase and enabled email and password authentication on firebase console!
I have tried entering the key argument into the TextFormfield but so far im unable to do so.
class LoginPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _LoginPageState();
}
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
String _email;
String _password;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Login Page Flutter Firebase"),
),
body: Container(
padding: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Spacer(flex: 5),
Text(
"Login Information",
style: new TextStyle(fontSize: 30.0),
),
Spacer(
flex: 5,
),
TextFormField(
key: _formKey,
onSaved: (value) => _email = value,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(labelText: "Email Address"),
),
TextFormField(
onSaved: (value) => _password = value,
obscureText: true,
decoration: InputDecoration(labelText: "Password"),
),
Spacer(flex: 3),
RaisedButton(
child: Text("Login"),
onPressed: () async {
//Getting the error here
final form = _formKey.currentState;
form.save();
if (form.validate()) {
print('$_email $_password');
var result = await
Provider.of<AuthService(context).loginUser(email: _email, password:_password);
if (result == null) {
print("result is null");
}
}
}),
Spacer(
flex: 20,
)
],
),
));
}
}
[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The method 'save' was called on null.
Receiver: null
Tried calling: save()
this error message is what i get. somehow i understand that i have to initialise the receiver object(?) but i am confused on how to do so. Thanks for the help!
you put the _formKey on the TextFormField associated with the email field, it doesn't belong there
body: Container(
padding: EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
....
]
) // Form
) // Container
From the blog post..
Instead of using onSaved I'd just use TextEditingControllers on your fields and get the values of the text from those when you call your signIn function.

How to pass down data from Stateful classes to another Stateful class that exists in another file?

I'm having trouble passing the data that's been filled in a textformfields and selected in a dropdown menu.
I'm trying to use the Map function to pass down String values so that I can also pass down all types of values in the future (ex. int, bool, double etc.), however it's not working so I need someone to check it out.
main.dart
import 'package:flutter/material.dart';
import 'package:workoutapp/auth/auth.dart';
import 'package:workoutapp/auth/root_page.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';
void main(List<String> args) {
runApp(
WorkoutManager(),
);
}
class WorkoutManager extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AuthProvider(
auth: Auth(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Workout Manager',
home: RootPage(),
theme: ThemeData(
primaryColor: Colors.indigo,
primarySwatch: Colors.indigo,
accentColor: Colors.indigoAccent,
hintColor: Colors.indigo,
brightness: Brightness.dark,
),
),
);
}
}
HomePage
import 'package:flutter/material.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';
import './profile_account_page.dart';
import './routines_create_page.dart';
import '../objects/Routines/routines_manager.dart';
import '../tools/custom_drawer.dart';
class HomePage extends StatelessWidget {
final VoidCallback onSignedOut;
final List<Map<String, String>> routines;
HomePage({Key key, this.onSignedOut, this.routines}) : super(key: key);
void _signedOut(BuildContext context) async {
try {
var auth = AuthProvider.of(context).auth;
await auth.signOut();
onSignedOut();
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Workout Manager', style: TextStyle(color: Colors.white)),
centerTitle: false,
actions: <Widget>[
FlatButton(
child: Text('Logout'),
onPressed: () {
return _signedOut(context);
},
),
IconButton(
icon: Icon(Icons.account_box),
tooltip: 'Profile Account',
color: Colors.white,
onPressed: () {
return Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return ProfileAccountPage();
}));
},
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return RoutinesPageCreate();
}));
},
),
body: RoutinesManager(),
drawer: CustomDrawer(),
);
}
}
RoutineManager
import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines.dart';
class RoutinesManager extends StatefulWidget {
final Map<String, String> startingRoutine;
RoutinesManager({this.startingRoutine});
#override
_RoutinesManagerState createState() => _RoutinesManagerState();
}
class _RoutinesManagerState extends State<RoutinesManager> {
List<Map<String, String>> _routines = [];
#override
void initState() {
if (widget.startingRoutine != null) {
_routines.add(widget.startingRoutine);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Routines(_routines),
)
],
);
}
}
RoutinesCreatePage
import 'package:flutter/material.dart';
import 'package:workoutapp/pages/home_page.dart';
class RoutinesPageCreate extends StatefulWidget {
#override
_RoutinesPageCreateState createState() => _RoutinesPageCreateState();
}
class _RoutinesPageCreateState extends State<RoutinesPageCreate> {
final formKey = GlobalKey<FormState>();
List<Map<String, String>> _routines = [];
String _routineName, _routineDescription;
var _routineNameController = TextEditingController();
var _routineDescriptionController = TextEditingController();
List<DropdownMenuItem<String>> _dropdownListBodyPartMenuItem = [];
List<String> _dropdownListBodyPart = [
'Chest',
'Back',
'Leg',
'Shoulder',
'Abs',
];
String _selectedBodyPart;
List<DropdownMenuItem<String>> _dropdownListDayOfWeekMenuItem = [];
List<String> _dropdownListDayOfWeek = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
];
String _selectedDayOfWeek;
void loadBodyPartData() {
_dropdownListBodyPartMenuItem = [];
_dropdownListBodyPartMenuItem = _dropdownListBodyPart.map((val) {
return DropdownMenuItem<String>(
child: Text(val),
value: val,
);
}).toList();
}
void loadDayOfWeekData() {
_dropdownListDayOfWeekMenuItem = [];
_dropdownListDayOfWeekMenuItem = _dropdownListDayOfWeek.map((val) {
return DropdownMenuItem<String>(
child: Text(val),
value: val,
);
}).toList();
}
final _scaffoldState = GlobalKey<ScaffoldState>();
void _showSnakBarReset() {
_scaffoldState.currentState.showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).accentColor,
content: Text('Showing SnackBar TEST'),
),
);
}
void _showSnakBarCreateWorkoutRoutine() {
_scaffoldState.currentState.showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).accentColor,
content: Text('Workout Routine has been created'),
),
);
}
void _addRoutine(Map<String, String> routine) {
setState(() {
_routines.add(routine);
});
}
#override
Widget build(BuildContext context) {
loadBodyPartData();
loadDayOfWeekData();
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text('Create Routines'),
),
body: Container(
padding: EdgeInsets.all(15.0),
child: Form(
key: formKey,
child: ListView(children: buildInputs() + buildCreateButtons()),
),
),
);
}
List<Widget> buildInputs() {
TextStyle textStyle = Theme.of(context).textTheme.title;
return [
TextFormField(
controller: _routineNameController,
validator: (value) {
if (value.length > 20) {
return 'Not a valid Routine Name';
}
},
onSaved: (value) {
return _routineName = value;
},
decoration: InputDecoration(
labelStyle: textStyle,
labelText: 'Routine Name',
hintText: 'Enter the Routine Name for this day',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
))),
Padding(padding: EdgeInsets.all(7.0)),
TextFormField(
controller: _routineDescriptionController,
validator: (value) {
if (value.length > 50) {
return 'Invalid: The Description must be 50 characters or less.';
}
},
onSaved: (value) {
return _routineDescription = value;
},
decoration: InputDecoration(
labelStyle: textStyle,
labelText: 'Description',
hintText: 'Enter the description of the Routine.',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
))),
Padding(padding: const EdgeInsets.all(7.0)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedBodyPart,
items: _dropdownListBodyPartMenuItem,
hint: Text('Select Body Part', style: textStyle),
onChanged: (value) {
setState(() {
_selectedBodyPart = value;
});
})),
Padding(
padding: const EdgeInsets.all(1.0),
),
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedDayOfWeek,
items: _dropdownListDayOfWeekMenuItem,
hint: Text('Select Day of Week', style: textStyle),
onChanged: (value) {
setState(() {
_selectedDayOfWeek = value;
});
},
),
),
Padding(
padding: const EdgeInsets.all(4.0),
)
],
),
];
}
List<Widget> buildCreateButtons() {
return [
Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
textColor: Theme.of(context).primaryColorDark,
color: Theme.of(context).accentColor,
child: Text('Create Workout Routine'),
onPressed: () {
if (formKey.currentState.validate()) {
_showSnakBarCreateWorkoutRoutine();
formKey.currentState.save();
_addRoutine({
'routineName': 'Chest Workout',
'description': 'Heavy',
'bodyPart': 'Chest',
'week': 'Monday',
});
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return HomePage();
}));
} else {
return null;
}
}),
),
Expanded(
child: RaisedButton(
textColor: Theme.of(context).primaryColorLight,
color: Theme.of(context).primaryColorDark,
child: Text('Reset'),
onPressed: () {
setState(() {
_showSnakBarReset();
formKey.currentState.reset();
_selectedBodyPart = null;
_selectedDayOfWeek = null;
});
},
),
),
],
),
),
];
}
}
Routines
import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines_detail.dart';
class Routines extends StatelessWidget {
final List<Map<String, String>> routines;
Routines(this.routines);
Widget _buildRoutinesItem(BuildContext context, int index) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Expanded(
child: Card(
margin: EdgeInsets.all(5.0),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['routineName'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['description'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['bodyPart'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['week'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
child: Text('Details'),
onPressed: () {
return Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return RoutinesDetail(
routines[index]['routineName'],
routines[index]['description'],
routines[index]['bodyPart'],
routines[index]['week']);
}));
},
)
],
),
)
],
),
),
);
}
Widget _buildRoutinesList(context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
Widget routinesCards = Container(
child: Container(
child: Center(
child: Text("No routines found, please add some.", style: textStyle),
),
),
);
if (routines.length > 0 || routines.length <= 7) {
ListView.builder(
itemBuilder: _buildRoutinesItem,
itemCount: routines.length,
);
}
return routinesCards;
}
#override
Widget build(BuildContext context) {
return _buildRoutinesList(context);
}
}
RoutineDetailPage
import 'package:flutter/material.dart';
class RoutinesDetail extends StatelessWidget {
final String routineName, description, bodyPart, week;
RoutinesDetail(this.routineName, this.description, this.bodyPart, this.week);
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text(routineName),
centerTitle: true,
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routineName, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(description, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(bodyPart, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(week, style: textStyle)),
Container(
padding: EdgeInsets.all(5.0),
child: RaisedButton(
child: Text('Delete'),
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
),
);
}
}
As you can see, I'm trying to separate the code into multiple files as much as possible, so it's more "readable" and make it easy for myself to make changes to the code whenever I have to in the future.
The problem is, it's quite spit up, I don't understand how to use the data and pass it down or up to the pages or widgets as there are multiple stateful and stateless widgets that are suppose to work together to make this app possible.
You'll notice on the HomePage file (StatelessWidget), I'm trying to show the Scaffold body argument with the RoutinesManager StatefulWidget, which is in a different file. At the same time in the HomePage file, I have a Scaffold floatingActionButton argument that will take you to the RoutinesCreatePage StatefulWidget to create a List of Cards (StatelessWidget) using the ListView.builder(). However, no Card gets created under the HomePage after the "Create Workout Routine" RaisedButton gets pressed in the RoutinesCreatePage and no data gets passed.
Can someone please help me out here as I am totally clueless. Also, I'm fairly a beginner regarding flutter/dart so a solution with a relatively easy to understand explanation would be very helpful.
Note: I do have other files that contribute to this app, however I don't think they're part of the problem so I left them out intentionally.
If more information is needed, please do let me know.
Thanks you!
it looks like you misunderstand what state in Flutter is. To explain in short, state is the internal status/data/... that belongs that that specific widget. StatefulWidget has state to determine if UI should be re-rendered on its own state change. External widgets never know about other widgets' states.
So it means, any state change happening inside RoutinesCreatePage widget, only that RoutinesCreatePage knows and reacts. Unless, you inform other widgets to know something has changed.
Alright, so talking about navigation, it works like a stack structure. HomePage trigger a push to RoutinesCreatePage, then to return, you need to pop, not another push.
Here a quick fix for your code, you can try.
HomePage
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
handleNewRoutine(); <--- this is to handle navigation and retrieve returning data from pop
},
),
Future handleNewRoutine() async {
var newRoutine = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => RoutinesPageCreate());
)
if (newRoutine == null) {
// nothing returns from RoutinesPageCreate widget
// so do nothing then
} else {
// add to routine list
// and trigger list re-rendering
setState(() {
this.routines.add(newRoutine);
});
}
}
RoutinesCreatePage: when clicking submit button, just populate all data from input fields, make object model and pop to return data to where this widget was pushed.
onPressed: () {
var newRoutine = .... // populate from UI to create new Routine model object.
Navigator.pop(context, newRoutine);
}
Also, take time to read the navigation guide from official Flutter documentation. It is very detailed on this part. https://flutter.io/docs/cookbook/navigation/returning-data
Some additional comments to your code:
in RoutinesCreatePage you don't need to know application level state, I mean _routines variable is unnecessary. You only need one object to store new routine to pop back to HomePage.
in Routines, this method Widget _buildRoutinesList(context) having unused ListView creation.
if (routines.length > 0 || routines.length <= 7) {
ListView.builder(
itemBuilder: _buildRoutinesItem,
itemCount: routines.length,
);
}

Login form in flutter

I´m create a simple login in flutter. This login connect to a API. For do this I retrieve inputs value and send to a method, but return
type '_InternalLinkedHashMap' is not a subtype of type 'Map'
This is my model
class Login {
final String usuario, password;
Login({this.usuario, this.password});
factory Login.fromJson(Map<String, dynamic> json) {
return Login(
usuario: json['usuario'] as String,
password: json['password'] as String,
);
}
}
And this is my main class
import 'package:flutter/material.dart';
import 'package:igota/screens/home/home_page.dart';
import 'package:igota/model/login.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class LoginPage extends StatefulWidget {
static String tag = 'login-page';
#override
_LoginPageState createState() => new _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
final userValue = new TextEditingController();
final passwordValue = new TextEditingController();
#override
Widget build(BuildContext context) {
final logo = Hero(
tag: 'hero',
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 48.0,
child: Image.asset('assets/logo.png'),
),
);
final username = TextFormField(
controller: userValue,
validator: (value) {
if (value.isEmpty) {
return 'Introduce un nombre';
}
},
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: InputDecoration(
hintText: 'Usuario',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
),
);
final password = TextFormField(
controller: passwordValue,
validator: (value) {
if (value.isEmpty) {
return 'Introduce una contraseña';
}
},
autofocus: false,
obscureText: true,
decoration: InputDecoration(
hintText: 'Contraseña',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
),
);
Widget _buildLoginButton(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
onPressed: () {
fetchPost(userValue, passwordValue);
//final snackBar = SnackBar(
// content: Text(
// 'Usuario/Contraseña incorrecto',
// textAlign: TextAlign.center,
//));
//Scaffold.of(context).showSnackBar(snackBar);
if (_formKey.currentState.validate()) {
Navigator.of(context).pushNamed(HomePage.tag);
}
},
color: Colors.blue[300],
child: Text('Entrar', style: TextStyle(color: Colors.white)),
),
),
);
}
final forgotLabel = FlatButton(
child: Text(
'¿Contraseña olvidada?',
style: TextStyle(color: Colors.black54),
),
onPressed: () {},
);
return Scaffold(
backgroundColor: Colors.white,
body: Builder(
builder: (context) => Center(
child: Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
logo,
SizedBox(height: 48.0),
username,
SizedBox(height: 8.0),
password,
SizedBox(height: 24.0),
_buildLoginButton(context),
forgotLabel
],
),
)),
),
);
}
}
//Test
Future<Login> fetchPost(user, pass) async {
Map headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(
'URL',
body: {"usuario": user.text, "password": pass.text},
headers: headers);
final responseJson = json.decode(response.body);
return Login.fromJson(responseJson);
}
So i need to return response and check if correct or incorrect to show snackbar. I don't know exactly what i'm doing right or wrong :$
You need to define the type of headers like this:
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
EDIT: You need to use json.encode on the body of a request with 'Content-type': 'application/json' as well.
EDIT2:
Future<Login> fetchPost(user, pass) async {
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(
'URL',
body: json.encode({"usuario": user.text, "password": pass.text}),
headers: headers);
final responseJson = json.decode(response.body);
return Login.fromJson(responseJson);
}

Textfield validation in Flutter

I am working on Flutter TextField widget. I want to show an error message below the TextField widget if the user does not fill that TextField. I only have to use TextField Widget not TextFormField in this case.
A Minimal Example of what you Want:
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() {
return new MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
final _text = TextEditingController();
bool _validate = false;
#override
void dispose() {
_text.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextField Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Error Showed if Field is Empty on Submit button Pressed'),
TextField(
controller: _text,
decoration: InputDecoration(
labelText: 'Enter the Value',
errorText: _validate ? 'Value Can\'t Be Empty' : null,
),
),
RaisedButton(
onPressed: () {
setState(() {
_text.text.isEmpty ? _validate = true : _validate = false;
});
},
child: Text('Submit'),
textColor: Colors.white,
color: Colors.blueAccent,
)
],
),
),
);
}
}
Flutter handles error text itself, so we don't require to use variable _validate. It will check at runtime whether you satisfied the condition or not.
final confirmPassword = TextFormField(
controller: widget.confirmPasswordController,
obscureText: true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_open, color: Colors.grey),
hintText: 'Confirm Password',
errorText: validatePassword(widget.confirmPasswordController.text),
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
);
String validatePassword(String value) {
if (!(value.length > 5) && value.isNotEmpty) {
return "Password should contain more than 5 characters";
}
return null;
}
Note: User must add at least one character to get this error message.
I would consider using a TextFormField with a validator.
Example:
class MyHomePageState extends State<MyHomePage> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextFormField validator'),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
hintText: 'Enter text',
),
textAlign: TextAlign.center,
validator: (text) {
if (text == null || text.isEmpty) {
return 'Text is empty';
}
return null;
},
),
RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// TODO submit
}
},
child: Text('Submit'),
)
],
),
),
);
}
}
If you use TextFormField then you could easily implement 'Error
below your text fields'.
You can do this without using _validate or any other flags.
In this example, I have used validator method of TextFormField
widget. This makes the work a lot more easier and readable at the
same time.
I also used FormState to make the work more easier
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final _form = GlobalKey<FormState>(); //for storing form state.
//saving form after validation
void _saveForm() {
final isValid = _form.currentState.validate();
if (!isValid) {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Form(
key: _form, //assigning key to form
child: ListView(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Full Name'),
validator: (text) {
if (!(text.length > 5) && text.isNotEmpty) {
return "Enter valid name of more then 5 characters!";
}
return null;
},
),
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: (text) {
if (!(text.contains('#')) && text.isNotEmpty) {
return "Enter a valid email address!";
}
return null;
},
),
RaisedButton(
child: Text('Submit'),
onPressed: () => _saveForm(),
)
],
),
),
),
);
}
}
I hope this helps!
For TextFiled and TextFormFiled Validation you can use this Example I hope this will helpful for you people.
TextField(
enableInteractiveSelection: true,
autocorrect: false,
enableSuggestions: false,
toolbarOptions: ToolbarOptions(
copy: false,
paste: false,
cut: false,
selectAll: false,
),
controller: _currentPasswordController,
obscureText: passwordVisible,
decoration: InputDecoration(
errorText: Validators.password(
_currentPasswordController.text),
filled: true,
fillColor: Colors.white,
contentPadding:
const EdgeInsets.fromLTRB(20, 24, 12, 16),
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(8.0))),
// filled: true,
labelText: 'Password',
hintText: 'Enter your password',
suffixIcon: GestureDetector(
onTap: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
child: Container(
margin: const EdgeInsets.all(13),
child: Icon(
passwordVisible
? FontAwesomeIcons.eyeSlash
: Icons.remove_red_eye_sharp,
color: ColorUtils.primaryGrey,
size: 25)),
),
),
),
Validation Message Example Code
static password(String? txt) {
if (txt == null || txt.isEmpty) {
return "Invalid password!";
}
if (txt.length < 8) {
return "Password must has 8 characters";
}
if (!txt.contains(RegExp(r'[A-Z]'))) {
return "Password must has uppercase";
}
if (!txt.contains(RegExp(r'[0-9]'))) {
return "Password must has digits";
}
if (!txt.contains(RegExp(r'[a-z]'))) {
return "Password must has lowercase";
}
if (!txt.contains(RegExp(r'[#?!#$%^&*-]'))) {
return "Password must has special characters";
} else
return;
}

Check if an application is on its first run with Flutter

Basically, I want to have a screen/view that will open when the user opens up the app for the first time. This will be a login screen type of thing.
Use Shared Preferences Package. You can read it with FutureBuilder, and you can check if there is a bool named welcome for example. This is the implementation I have in my code:
return new FutureBuilder<SharedPreferences>(
future: SharedPreferences.getInstance(),
builder:
(BuildContext context, AsyncSnapshot<SharedPreferences> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new LoadingScreen();
default:
if (!snapshot.hasError) {
#ToDo("Return a welcome screen")
return snapshot.data.getBool("welcome") != null
? new MainView()
: new LoadingScreen();
} else {
return new ErrorScreen(error: snapshot.error);
}
}
},
);
Above code work fine but for beginners I make it little bit simple
main.dart
import 'package:flutter/material.dart';
import 'package:healthtic/IntroScreen.dart';
import 'package:healthtic/user_preferences.dart';
import 'login.dart';
import 'profile.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
// This widget is the root of your application.
bool isLoggedIn = false;
MyAppState() {
MySharedPreferences.instance
.getBooleanValue("isfirstRun")
.then((value) => setState(() {
isLoggedIn = value;
}));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
//if true return intro screen for first time Else go to login Screen
home: isLoggedIn ? Login() : IntroScreen());
}
}
then share preferences
MySharedPreferences
import 'package:shared_preferences/shared_preferences.dart';
class MySharedPreferences {
MySharedPreferences._privateConstructor();
static final MySharedPreferences instance =
MySharedPreferences._privateConstructor();
setBooleanValue(String key, bool value) async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
myPrefs.setBool(key, value);
}
Future<bool> getBooleanValue(String key) async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
return myPrefs.getBool(key) ?? false;
}
}
then create two dart files IntroScreen and Login
Intro Screen will apear just once when user run application for first time usless the app is removed or caches are cleard
IntroScreen
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:healthtic/SliderModel.dart';
import 'package:healthtic/login.dart';
import 'package:healthtic/user_preferences.dart';
import 'package:shared_preferences/shared_preferences.dart';
class IntroScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Healthtic",
home: IntorHome(),
debugShowCheckedModeBanner: false,
);
}
}
class IntorHome extends StatefulWidget {
#override
_IntorHomeState createState() => _IntorHomeState();
}
class _IntorHomeState extends State<IntorHome> {
List<SliderModel> slides=new List<SliderModel>();
int currentIndex=0;
PageController pageController=new PageController(initialPage: 0);
#override
void initState() {
// TODO: implement initState
super.initState();
slides=getSlides();
}
Widget pageIndexIndicator(bool isCurrentPage) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 2.0),
height: isCurrentPage ? 10.0 : 6.0,
width: isCurrentPage ? 10.0 :6.0,
decoration: BoxDecoration(
color: isCurrentPage ? Colors.grey : Colors.grey[300],
borderRadius: BorderRadius.circular(12)
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
controller: pageController,
onPageChanged: (val){
setState(() {
currentIndex=val;
});
},
itemCount: slides.length,
itemBuilder: (context,index){
return SliderTile(
ImageAssetPath: slides[index].getImageAssetPath(),
title: slides[index].getTile(),
desc: slides[index].getDesc(),
);
}),
bottomSheet: currentIndex != slides.length-1 ? Container(
height: Platform.isIOS ? 70:60,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
onTap: (){
pageController.animateToPage(slides.length-1, duration: Duration(
microseconds: 400,
), curve: Curves.linear);
},
child: Text("Skip")
),
Row(
children: <Widget>[
for(int i=0;i<slides.length;i++) currentIndex == i ?pageIndexIndicator(true): pageIndexIndicator(false)
],
),
GestureDetector(
onTap: (){
pageController.animateToPage(currentIndex+1, duration: Duration(
microseconds: 400
), curve: Curves.linear);
},
child: Text("Next")
),
],
),
) : Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
height: Platform.isIOS ? 70:60,
color: Colors.blue,
child:
RaisedButton(
child: Text("Get Started Now",style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w300
),
),
onPressed: (){
MySharedPreferences.instance
.setBooleanValue("isfirstRun", true);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => Login()),
);
},
),
),
);
}
}
class SliderTile extends StatelessWidget {
String ImageAssetPath, title, desc;
SliderTile({this.ImageAssetPath, this.title, this.desc});
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(ImageAssetPath),
SizedBox(height: 20,),
Text(title),
SizedBox(height: 12,),
Text(desc),
],
)
,
);
}
}
final step Login
import 'package:flutter/material.dart';
import 'package:healthtic/user_preferences.dart';
import 'profile.dart';
class Login extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return LoginState();
}
}
class LoginState extends State<Login> {
TextEditingController controllerEmail = new TextEditingController();
TextEditingController controllerUserName = new TextEditingController();
TextEditingController controllerPassword = new TextEditingController();
#override
Widget build(BuildContext context) {
final formKey = GlobalKey<FormState>();
// TODO: implement build
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.all(25),
child: Form(
key: formKey,
autovalidate: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Email Id:", style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
Expanded(
child: TextFormField(
controller: controllerEmail,
decoration: InputDecoration(
hintText: "Please enter email",
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value.trim().isEmpty) {
return "Email Id is Required";
}
},
),
)
],
),
SizedBox(height: 60),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("UserName:", style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
Expanded(
child: TextFormField(
decoration: InputDecoration(
hintText: "Please enter username",
),
validator: (value) {
if (value.trim().isEmpty) {
return "UserName is Required";
}
},
controller: controllerUserName),
)
],
),
SizedBox(height: 60),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Password:", style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
Expanded(
child: TextFormField(
decoration: InputDecoration(
hintText: "Please enter password",
),
obscureText: true,
validator: (value) {
if (value.trim().isEmpty) {
return "Password is Required";
}
},
controller: controllerPassword),
)
],
),
SizedBox(height: 100),
SizedBox(
width: 150,
height: 50,
child: RaisedButton(
color: Colors.grey,
child: Text("Submit",
style: TextStyle(color: Colors.white, fontSize: 18)),
onPressed: () {
if(formKey.currentState.validate()) {
var getEmail = controllerEmail.text;
var getUserName = controllerUserName.text;
var getPassword = controllerPassword.text;
MySharedPreferences.instance
.setBooleanValue("loggedin", true);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => Profile()),
);
}
},
),
)
],
),
),
),
)),
);
}
}
Can be possible with this pakage : https://pub.dev/packages/is_first_run
Usage :
bool firstRun = await IsFirstRun.isFirstRun();

Resources