How to add validation on the form in flutter - flutter

I did manage to get no error for the code but cannot validate the form and show the error message. I have 3 component dart code which is the password, input field, and button. There is also one body dart in the library. ..................
I did manage to get no error for the code but cannot validate the form and show the error message. I have 3 component dart code which is the password, input field, and button. There is also one body dart in the library. ..................
import 'package:flutter_auth/Screens/Login/components/background.dart';
import 'package:flutter_auth/Screens/Login/components/uploadpage.dart';
import 'package:flutter_auth/components/rounded_button.dart';
import 'package:flutter_auth/components/rounded_input_field.dart';
import 'package:flutter_auth/components/rounded_password_field.dart';
class Body extends StatelessWidget {
const Body({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Background(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"LOGIN",
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: size.height * 0.03),
RoundedInputField(
hintText: "Username",
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value.length == 0)
return "Please enter email";
else if (!value.contains("#"))
return "Please enter valid email";
else
return null;
},
onChanged: (value) {},
),
PasswordField(
onSaved: (value) {},
),
RoundedButton(
text: "LOGIN",
press: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
),
],
),
),
);
}
}
class SecondScreen extends StatelessWidget {
goBackToPreviousScreen(BuildContext context) {
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.upload_file,
color: Colors.white,
),
onPressed: () {
{
Navigator.push(
context,
MaterialPageRoute(builder: (context) => uploadpage()),
);
} // do something
},
)
],
),
body: Stack(fit: StackFit.expand, children: <Widget>[
Positioned(
bottom: 0,
width: MediaQuery.of(context).size.width,
child: Center(
child: RaisedButton(
color: Colors.purple[400],
textColor: Colors.white,
onPressed: () {
goBackToPreviousScreen(context);
},
child: Text('Logout')),
),
)
]));
}

Here's a basic example of how Forms work:
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GlobalKey<FormState> formKey = new GlobalKey();
String formFieldValue;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Form(
key: formKey,
child: Column(
children: [
TextFormField(
validator: (input) {
if (input.isEmpty) {
return 'Please type something';
}
return null;
},
onSaved: (input) => formFieldValue = input,
),
RaisedButton(
onPressed: submitForm,
child: Text(
'Submit'
),
)
],
),
)
);
}
submitForm() {
final formState = formKey.currentState;
if (formState.validate()) {
formState.save();
// then do something
}
}
}

Here the validator does not get called automatically. You have to call it manually onPressed of a button or something.
Here you need to wrap your column in Form widget and give it a key. Onpressed you need to validate it by calling key.currentState.validate()
final _formreg = GlobalKey<FormState>();
Form(key:_formreg, child:Column(children:
[RoundedInputField() ]
));
RaisedButton(onPressed:()=> {
a=_formreg.currentState.validate();
} )
a is a boolean value

Related

Flutter - TextFormField validator is not working in TabBarView

I need some required values to submit.
I'm using TabBarView to view different sections.
Here's my code.
add_products_screen.dart
class _AddProductScreenState extends State<AddProductScreen> {
#override
Widget build(BuildContext context) {
final formkey = GlobalKey<FormState>();
return Form(
key: formkey,
child: DefaultTabController(
length: 2,
initialIndex: 0,
child: Scaffold(
appBar: AppBar(
title: const Text('Add Products'),
bottom: const TabBar(
isScrollable: true,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
width: 4,
color: Colors.deepOrange,
),
),
tabs: [
Tab(child: Text('General')),
Tab(child: Text('Attributes')),
],
),
),
drawer: const CustomDrawer(),
body: const TabBarView(
children: [
GeneralTab(),
AttributeTab(),
],
),
persistentFooterButtons: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
if (formkey.currentState!.validate()) {}
},
child: const Text('Save Product'),
),
),
],
),
),
],
),
),
);
}
}
form_field_input.dart
class FormFieldInput extends StatelessWidget {
final String? label;
final void Function(String)? onChanged;
const FormFieldInput({
Key? key,
this.label,
this.onChanged,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
label: Text(label!),
),
validator: (value) {
if (value!.isEmpty) {
return '$label is required';
}
return null;
},
onChanged: onChanged,
);
}
}
general_tab.dart
class _GeneralTabState extends State<GeneralTab>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Consumer<ProductProvider>(
builder: (context, provider, child) {
return ListView(
padding: const EdgeInsets.all(15.0),
children: [
FormFieldInput(
label: 'Product Name',
onChanged: (value) {
provider.getFormData(productName: value);
},
),
FormFieldInput(
label: 'Description',
onChanged: (value) {
provider.getFormData(description: value);
},
),
],
);
},
);
}
}
attributes_tab.dart
class _AttributeTabState extends State<AttributeTab>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Consumer<ProductProvider>(
builder: (context, provider, _) {
return ListView(
padding: const EdgeInsets.all(15.0),
children: [
FormFieldInput(
label: 'Brand',
onChanged: (value) {
provider.getFormData(brand: value);
},
),
FormFieldInput(
label: 'Remarks',
onChanged: (value) {
provider.getFormData(remarks: value);
},
),
],
);
},
);
}
}
My error is when I pressed save product button validator is showing only the 1st tab textformfields.
2nd tab textformfields validators are only showing when I go to that tab.
Otherwise, it won't show.
Here are some screenshots.
Before I go to 2nd tab and press save product button
After I go to 2nd tab and press save product button
How do I solve this error?
A form with a key will validate all of its children.
In your first case General tab alone created so those two Formfileds are the children of the Form.
But in your second case as you have opened the attributes tab, both the General and Attributes tab is loaded and now all 4 Form Fields are children of the Form.
So,
Wrap the general_tab.dart and attributes_tab.dart with individual Form widget with seperate form key.
Then validate them alone with their keys.

Provider returning null when rebuilding Flutter app

I'm pretty new to flutter and I'm trying to make a login system using providers. It seems to be working when I test the login. But when I rebuild the app the provider returns a null value. Any help would be appreciated.
The screen to check for employee data. If it exist it should redirect to the home page. And if it doesn't, it should redirect to the login authenticate page
Landing Page
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Landing extends StatefulWidget {
#override
_LandingState createState() => _LandingState();
}
class _LandingState extends State<Landing> {
//AuthService auth = new AuthService();
#override
Widget build(BuildContext context) {
Future<Employee> getuserdata() => Employee_preferences().getEmployee();
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AuthService(),
),
ChangeNotifierProvider(
create: (_) => Employee_Provider(),
)
],
child: MaterialApp(
title: 'ClockServe',
theme: ThemeData(primarySwatch: Colors.blue),
home: FutureBuilder(
future: getuserdata(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgressIndicator();
default:
if (snapshot.hasError) {
return Text('Error:${snapshot.error}');
} else if (snapshot.data.empId == null) {
return AuthenticatePage();
} else {
return HomePage(emp: snapshot.data);
}
}
}),
routes: {
'/navigatorPage': (context) => NavigatorPage(),
'/homePage': (context) => HomePage(),
'/authenticate': (context) => AuthenticatePage(),
'/attendancePage': (context) => AttendanceScanner()
},
),
);
}
}
The homepage. The page will hold employee information. Landing page is correctly redirecting to this page but for some reason the provider is returning null
HomePage
class HomePage extends StatefulWidget {
final Employee emp;
const HomePage({Key key, this.emp}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//to do: add back end
//use futurebuilder to return user object
//using futureprovider to get snapshot data of user object from database
#override
Widget build(BuildContext context) {
Employee emp = Provider.of<Employee_Provider>(context).emp;
print(emp.empEmail);
return Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () async {
Employee_preferences().removeEmployee();
Navigator.pushReplacementNamed(context, '/authenticate');
},
label: Text(
'Log Out',
style: TextStyle(color: Colors.white),
),
icon: Icon(
Icons.logout,
color: Colors.white,
),
)
],
title: Text('ClockServe'),
centerTitle: true,
),
//button to pop qr scanner camera
//after scanning a qr code it should parse the json array
//into a method, the method will take that as parameter.
//method should send http request check in the auth dart
floatingActionButton: FloatingActionButton.extended(
label: Text('Check In'),
icon: Icon(Icons.camera_alt),
onPressed: () => navigateToScanPage(context),
),
// floatingActionButton: FloatingActionButton(
// onPressed: () {},
// child: Icon(Icons.alarm_on),
// ),
body: SingleChildScrollView(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(emp.empFirstName ?? 'emp first name'),
],
),
),
),
);
}
}
Future navigateToScanPage(context) async {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AttendanceScanner()));
}
Code for login page just in case if it's relevant.
Login Page
class LoginPage extends StatefulWidget {
final Function toggleView;
LoginPage({this.toggleView});
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
String email = '';
String password = '';
String error = '';
bool loading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
widget.toggleView();
},
label: Text('Register'),
icon: Icon(Icons.person_add),
)
],
title: Text('Login'),
),
body: Container(
padding: EdgeInsets.all(30),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
WelcomeHeader(),
SizedBox(
height: 10,
),
TextFormField(
validator: (value) => value.isEmpty ? 'Enter email' : null,
onChanged: (val) {
setState(() => email = val);
},
decoration: decorationBox.copyWith(hintText: 'Email'),
),
SizedBox(
height: 20,
),
TextFormField(
validator: (value) => value.isEmpty ? 'Enter password' : null,
onChanged: (val) {
setState(() => password = val);
},
obscureText: true,
decoration: decorationBox.copyWith(hintText: 'Password'),
),
SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () async {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
AuthService auth = new AuthService();
final Future<Map<String, dynamic>> successMsg =
auth.empLogin(email, password);
successMsg.then((response) {
if (response['status']) {
Employee emp = response['employee'];
print(emp);
Provider.of<Employee_Provider>(context, listen: false)
.setEmp(emp);
Navigator.pushReplacementNamed(context, '/homePage');
}
});
}
},
child: Text('Log In'),
),
SizedBox(
height: 20.0,
),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 20.0),
)
],
),
),
),
),
);
}
}
class WelcomeHeader extends StatelessWidget {
const WelcomeHeader({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text(
'Welcome To ClockServe',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28.0,
),
),
Divider(
height: 20,
thickness: 2,
),
Text(
'Enter your credentials to login',
style: TextStyle(fontStyle: FontStyle.italic),
),
],
),
);
}
}

Flutter how to get user input using text form in show dialog?

I'm trying to get the user input to change the title using a text form in show dialog but it seems the state is rebuilding whenever the keyboard shows/closes, my code is working before, but when I did flutter upgrade to v1.17 it's not working anymore. I've been stuck here for a couple of days now and I don't know what's wrong with my code or what error might be causing it, I can only see "getSelectedText on inactive InputConnection" and "mSecurityInputMethodService is null" in the debug console, please help.
Here's a sample of my code:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
final TextEditingController titleController = new TextEditingController();
final GlobalKey<FormState> _keyDialogForm = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
titleController.text = 'Hello';
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Column(
children: <Widget>[
Text(titleController.text),
SizedBox(
height: 50,
),
FlatButton(
color: Colors.redAccent,
onPressed: () {
showTitleDialog();
},
child: Text(
'Show Dialog',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
))
],
),
));
}
Future showTitleDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Form(
key: _keyDialogForm,
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.ac_unit),
),
maxLength: 8,
textAlign: TextAlign.center,
onSaved: (val) {
titleController.text = val;
},
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter Title Name';
}
return null;
},
)
],
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
if (_keyDialogForm.currentState.validate()) {
_keyDialogForm.currentState.save();
Navigator.pop(context);
}
},
child: Text('Save'),
color: Colors.blue,
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel')),
],
);
});
}
}
You can copy paste run full code below
You can call setState in onSaved
code snippet
onSaved: (val) {
titleController.text = val;
setState(() {});
},
working demo
full code
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
final TextEditingController titleController = new TextEditingController();
final GlobalKey<FormState> _keyDialogForm = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
titleController.text = 'Hello';
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Column(
children: <Widget>[
Text(titleController.text),
SizedBox(
height: 50,
),
FlatButton(
color: Colors.redAccent,
onPressed: () {
showTitleDialog();
},
child: Text(
'Show Dialog',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
))
],
),
));
}
Future showTitleDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Form(
key: _keyDialogForm,
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.ac_unit),
),
maxLength: 8,
textAlign: TextAlign.center,
onSaved: (val) {
titleController.text = val;
setState(() {});
},
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter Title Name';
}
return null;
},
)
],
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
if (_keyDialogForm.currentState.validate()) {
_keyDialogForm.currentState.save();
Navigator.pop(context);
}
},
child: Text('Save'),
color: Colors.blue,
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel')),
],
);
});
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Test(),
);
}
}

Showing selected image in alert dialog in flutter

How can i show the selected image in my alert dialog ?
In my app, i added an alert dialog which has the camera button. When user clicks the camera button, another alert dialog asks to select file from gallery. After the user selects image file from gallery, i want to show the image in the alert dialog with the camera button, but the image shows only after reopening the alert dialog.
I have posted my code below. I am new to flutter. Please can someone help me? Thanks in advance.
class Test extends StatefulWidget {
#override
_State createState() => new _State();
}
Future<File> imageFile;
class _State extends State<Test> {
Future<void> _openDailog() async {
return showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Click Photo'),
Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24.0),
color: Colors.blue),
child: IconButton(
color: Colors.white,
icon: Icon(Icons.camera_alt),
onPressed: () {
_cameraOptions();
print("test");
},
),
)
],
),
content: SingleChildScrollView(
child: Container(
width: 300.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
showImage(),
InkWell(
child: Container(
margin: EdgeInsets.only(top: 8.0),
child: RaisedButton(
color: Colors.blue,
child: new Text(
"Send",
style: TextStyle(color: Colors.white),
),
onPressed: () {
Navigator.of(context).pop();
print("test");
},
),
)),
],
),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(Icons.insert_drive_file),
onPressed: () {
_openDailog();
},
)
],
);
}
Future<void> _cameraOptions() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
FlatButton(
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
Navigator.of(context).pop();
},
color: Colors.transparent,
child: new Text(
'Select From Gallery',
textAlign: TextAlign.start,
style: new TextStyle(
decoration: TextDecoration.underline,
),
),
),
],
),
),
);
});
}
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: MediaQuery.of(context).size.width,
height: 100,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
'No Image Selected',
textAlign: TextAlign.center,
);
}
},
);
}
}
That is because you would need to setState() however you can't do that in an alert dialogue as it doesn't have its own state, the workaround for that would be to have the dialogue be its own stateful widget. Please check out this article as it shows how to do that. If you faced problems let me know!
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MaterialApp(
home: new MyHomePage(),
));
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("StackoverFlow"),
),
body: Container(),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await _dialogCall(context);
},
),
);
}
Future<void> _dialogCall(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return MyDialog();
});
}
}
class MyDialog extends StatefulWidget {
#override
_MyDialogState createState() => new _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
String imagePath;
Image image;
#override
Widget build(BuildContext context) {
return AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
Container(child: image!= null? image:null),
GestureDetector(
child: Row(
children: <Widget>[
Icon(Icons.camera),
SizedBox(width: 5),
Text('Take a picture '),
],
),
onTap: () async {
await getImageFromCamera();
setState(() {
});
}),
Padding(
padding: EdgeInsets.all(8.0),
),
],
),
),
);
}
Future getImageFromCamera() async {
var x = await ImagePicker.pickImage(source: ImageSource.camera);
imagePath = x.path;
image = Image(image: FileImage(x));
}
}
Try this solution with GestureDetector() .it works
onTap:()async{
var image = await ImagePicker.pickImage(
source: ImageSource.gallery).whenComplete((){
setState(() {
});
}
);
setState(() {
_image = image;
});
},

How to get value from textfield and display in textfromfield (another screen)

I'm new to flutter, I trying to pass a value from textfield and when i click a button submit, display it in textformfield in another screen, my problem, I don't know the right way to get value
Some Code :
String txt = "";
TextEditingController controllerTxt = new TextEditingController();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('Create'),
actions: <Widget>[
FlatButton(
child: Text('Submit'),
textColor: Colors.white,
onPressed: () {
setState(() {
//txt = (controllerTxt.text);
Navigator.pushNamed(context, '/ResultPage');
});
},
),
],
),
body: new Container(
child: new Column(
children: <Widget>[
new TextField(
controller: controllerTxt,
maxLines: 5,
decoration: new InputDecoration(
),
),
],
),
),
);
}
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('Result'),
),
body: new Container(
padding: EdgeInsets.all(10.0),
child: new Column(
children: <Widget>[
new TextFormField(
decoration: InputDecoration(
labelText: 'Name :',
),
),
new Text("${controllerTxt.text}"),
],
),
),
);
}
}
I have done the same thing by passing data through the constructor
Navigator.push(context,
MaterialPageRoute(builder: (context) => ResultPage(controllerTxt.text)));
class ResultPage extends StatefulWidget {
final String result;
ResultPage(this.result);