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;
}
Related
I'm try use controller of TextField but i receive error
"NoSuchMethodError: The method 'call' was called on null"
It will fine if i use onChange().
My code:
class _MyHomePageState extends State<MyHomePage> {
Icon _searchIcon = Icon(Icons.search, color: Colors.white);
int _searchIconState = 0;
Widget _appBarTitle;
TextEditingController _controller = TextEditingController();
_onChange() {
String text = _controller.text;
print(text);
}
#override
void initState() {
super.initState();
/* My TextField */
_appBarTitle = TextField(
controller: _controller,
onChanged: (text) {
print('onChanged: ' + text);
},
style: TextStyle(color: Colors.white, fontSize: 18),
decoration: InputDecoration(
border: InputBorder.none,
icon: _searchIcon,
hintText: 'Search...',
hintStyle:
TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 18)));
_controller.addListener(_onChange());
}
#override
void dispose() {
// Clean up the controller when the Widget is removed from the Widget tree
// This also removes the _printLatestValue listener
_controller.dispose();
super.dispose();
}
_nestedScrollViewController() {}
_tabBarController() {}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: NestedScrollView(
controller: _nestedScrollViewController(),
headerSliverBuilder: (BuildContext context, bool isScrolled) {
return <Widget>[
SliverAppBar(
title: _appBarTitle /* TextField put in here */,
pinned: true,
floating: true,
forceElevated: isScrolled,
bottom: TabBar(
tabs: <Widget>[
Tab(text: 'TO DAY'),
Tab(text: 'TOMORROW'),
Tab(text: '7Days')
],
controller: _tabBarController(),
),
)
];
},
body: Scaffold(
body: TabBarView(
children: <Widget>[
todayUI(),
tomorrowUI(),
weekUI(),
],
),
)
),
),
);
}
}
You added listener incorrect way
You have to remove () after onChange
_controller.addListener(_onChange());
to
_controller.addListener(_onChange);
I have a problem regarding if statement in dart, I want the user to tap the city to go to a new screen. this code work perfectly fine
class citySec extends StatelessWidget {
Widget getListView(BuildContext context) {
var listView = ListView(
children: <Widget>[
Text(
"choose ur city:",
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
),
ListTile(
leading: Icon(Icons.location_city),
title: Text("Toronto ", textDirection: TextDirection.rtl),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TorontoUniversitySection(),
),
);
},
),
],
);
return listView;
}
#override
Widget build(BuildContext context) {
return Scaffold(body: getListView(context));
}
}
Since I have a long list of cities and the previous code will make my code very long so I had to change my code. However, I faced some errors with if statements, here is what I did so far.
import 'package:flutter/material.dart';
import 'package:rate/screens/firstScreen.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Rate',
home: Scaffold(
appBar: AppBar(
title: Text("jgfnjfnj ", textDirection: TextDirection.rtl),
),
body: ListDisplay(),
),
));
}
class ListDisplay extends StatelessWidget {
List<String> litems = ["Toronto","NewYork","London","Riyadh","Dubai","Istanbul"];
#override
Widget build (BuildContext ctxt) {
return new Scaffold(
appBar: AppBar(title: Text("Please Choose your city: ", textDirection: TextDirection.ltr,),
),
body: new ListView.builder
(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int index) {
return new ListTile(
leading: Icon(Icons.location_city),
title: Text(litems[index], textDirection: TextDirection.rtl),
onTap: () {
// begin of all IF statements
if (litems.contains("Totonto")){
Navigator.push(
ctxt,
MaterialPageRoute(
builder: (ctxt) => TorontoUniversitySection()
),
);
}
if (litems.contains("London")){
Navigator.push(
ctxt,
MaterialPageRoute(
builder: (ctxt) => LondonUniversitySection()
),
);
}
// end of all If statements
},
);
}
)
);
}
}
for example, when I press Toronto it will take me to LondonUniversitySection()
That is because in your if statements, you check whether your list contains Toronto/London and not if currently pressed one is Toronto/London. Changing litems.contains("x") to litems[index] == "x" will do the trick. Here's edited fragment:
return new ListTile(
leading: Icon(Icons.location_city),
title: Text(litems[index], textDirection: TextDirection.rtl),
onTap: () {
if (litems[index] == "Toronto") {
Navigator.push(
ctxt,
MaterialPageRoute(builder: (ctxt) => TorontoUniversitySection()),
);
} else if (litems[index] == "London") {
Navigator.push(
ctxt,
MaterialPageRoute(builder: (ctxt) => LondonUniversitySection()));
}
},
);
Also, I recommend using a switch or else-if for that, not a bunch of ifs.
Try onTap: litems.contains("Totonto")?
Navigator.push( ctxt, MaterialPageRoute( builder: (ctxt) => TorontoUniversitySection() ), )
: null
class _RegisterBodyState extends State<RegisterBody> {
FocusNode myFocusNode = new FocusNode();
FocusNode myFocusNode2 = new FocusNode();
void initState() {
super.initState();
myFocusNode = FocusNode();
myFocusNode2 = FocusNode();
}
#override
void dispose() {
// Clean up the focus node when the Form is disposed.
myFocusNode.dispose();
myFocusNode2.dispose();
super.dispose();
}
Color color;
#override
Widget build(BuildContext context) {
return Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Register",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 70.0,
fontWeight: FontWeight.bold,
),
),
Form(
child: Column(
children: [
TextFormField(
onTap: () {
setState(() {
color = Colors.red;
});
},
autofocus: true,
focusNode: myFocusNode,
decoration: InputDecoration(
icon: Icon(
Icons.supervised_user_circle,
size: 40.0,
),
labelText: "User Name",
labelStyle: TextStyle(
color:
myFocusNode.hasFocus ?color : Colors.yellow),
),
),
TextFormField(
focusNode: myFocusNode2,
onTap: () {
setState(() {
color = Colors.black;
});
},
autofocus: false,
decoration: InputDecoration(
icon: Icon(
Icons.supervised_user_circle,
size: 40.0,
),
labelText: "User Name",
labelStyle: TextStyle(
color: myFocusNode2.hasFocus ? color : Colors.teal,
),
),
),
],
))
],
),
),
);
}
}
So basically there is this product edit page which behaves differently uppon called.
If the product is being created for first time, then it is shown in a tab view controller.
if the product is being updated, its body is returned in scaffold.
here are some screenshots
when I submit through create product, i encounter no error.
But when I submit through update product, though the logic works, i get a short red screen with
like this
error Another exception was thrown: No Material widget found.
Here is the code for the screen
`
import 'package:flutter/material.dart';
import 'package:academy_app/models/products.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:academy_app/scoped-model/Products.dart';
class ProductEdit extends StatefulWidget {
ProductEdit();
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return ProductEditState();
}
}
class ProductEditState extends State<ProductEdit> {
String title;
String description;
double price;
Product product;
final focusnode2 = FocusNode();
final focusnode3 = FocusNode();
Map<String, dynamic> formData = {
"name": null,
"desc": null,
"price": null,
"image": "asset/foood.jpg"
};
GlobalKey<FormState> formkey = GlobalKey<FormState>();
Widget buildTitle(productItem) {
return TextFormField(
initialValue: productItem != null ? productItem.title : "",
validator: (String value) {
if (value.isEmpty || value.length < 3) {
return 'title cannot be empty';
}
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(labelText: "Title"),
onFieldSubmitted: (String value) {
FocusScope.of(context).requestFocus(focusnode2);
},
onSaved: (String valuee) {
setState(() {
formData["name"] = valuee;
});
},
);
}
Widget buildDesc(productItem) {
return TextFormField(
initialValue: productItem != null ? productItem.description : "",
validator: (String value) {
if (value.isEmpty || value.length < 3) {
return 'Cant have that short description';
}
},
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(focusnode3);
},
focusNode: focusnode2,
maxLines: 3,
decoration: InputDecoration(labelText: "Description"),
onSaved: (String valuee) {
setState(() {
formData["desc"] = valuee;
});
},
);
}
Widget buildPrice(productItem) {
return TextFormField(
initialValue: productItem != null ? productItem.price.toString() : "",
textInputAction: TextInputAction.done,
focusNode: focusnode3,
decoration: InputDecoration(labelText: " How much"),
keyboardType: TextInputType.number,
onFieldSubmitted: (value) {
focusnode3.unfocus();
},
validator: (value) {
if (!RegExp(r'^(?:[1-9]\d*|0)?(?:\.\d+)?$').hasMatch(value)) {
return ' Enter numbers only';
}
},
onSaved: (String valuee) {
setState(() {
formData["price"] = double.parse(valuee);
});
},
);
}
void submitForm(Function addProduct, Function updateProduct, int index) {
if (!formkey.currentState.validate()) {
return;
}
formkey.currentState.save();
setState(() {
if (index == null) {
addProduct(Product(
price: formData["price"],
title: formData["name"],
description: formData["desc"],
image: "asset/foood.jpg"));
} else {
updateProduct(
Product(
price: formData["price"],
title: formData["name"],
description: formData["desc"],
image: "asset/foood.jpg"),
);
}
Navigator.pushReplacementNamed(context, '/');
});
}
Widget buildSubmitButton() {
return ScopedModelDescendant<ProductsModel>(
builder: (BuildContext context, Widget, ProductsModel) {
return RaisedButton(
child: Text("Save"),
onPressed: () => submitForm(ProductsModel.addProduct,
ProductsModel.updateProduct, ProductsModel.selected_index));
},
);
}
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
buildTitle(product),
buildDesc(product),
buildPrice(product),
SizedBox(
height: 10.0,
),
buildSubmitButton()
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<ProductsModel>(
builder: (context, Widget child, ProductsModel) {
product = ProductsModel.getproduct();
return ProductsModel.selected_index == null
? _buildPageContent(context, product)
: Scaffold(
appBar: AppBar(
title: Text("Update Item"),
),
body:_buildPageContent(context, product) ,
);
},
);
}
}
`
why am i getting that red screen error? i confused about passing the contexts. why arent the textfiled accessing the material parent through in scaffold?
As the error suggests that no material widget found, you need to wrap the Container of the _buildPateContent function into Material Widget. Here is the change that you can do:
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Material(
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
buildTitle(product),
buildDesc(product),
buildPrice(product),
SizedBox(
height: 10.0,
),
buildSubmitButton()
],
),
),
),
));
}
try wrapping you app in materialApp or wrap TextField in material Widget.
In my case I forgot to wrap my widget with Scaffold widget. A lot of widgets need to be wrapped with it to work properly. So change this
Widget build(BuildContext context) {
return YourScreenContent();
}
to this
Widget build(BuildContext context) {
return Scaffold(
body: YourScreenContent(),
);
}
I had same issue.
I got the error when I had code like this.
Widget _getLCSBar(int index) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 0.1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_getLikeButton(index),
_getCommentButton(index),
_getShareButton(index),
],
));
}
What I have done is wrapped it with Material
Widget _getLCSBar(int index) {
return Material(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 0.1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_getLikeButton(index),
_getCommentButton(index),
_getShareButton(index),
],
)));
}
Problem Solved!
Just wrap the Gesture Detector inside Material:
return Material(
child: GestureDetector(
...
),
);
Hai i am new in flutter/dart and hopefully you guys can help me on this. I am having this issue when i use obscureText: true and validator: in a TextFormField somehow i am unable to type anything in that field. Can someone tell me why is this?
class _LoginPageState extends State<LoginPage>{
final formKey = new GlobalKey<FormState>();
String _email;
String _password;
void validateAndSave(){
final form = formKey.currentState;
if (form.validate()){
print('Form is valid');
}else{
print('Form is invalid');
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Login'),
),
body: new Container(
padding: const EdgeInsets.all(20.0),
child: new Form(
key: formKey,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(labelText: 'Email'),
validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null,
onSaved: (value) => _email = value,
),
new TextFormField(
decoration: new InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) => value.isEmpty ? 'Password can\'t be empty' : null,
onSaved: (value) => _password = value,
),
new RaisedButton(
child: new Text('Login', style: new TextStyle(fontSize: 20.0)),
onPressed: validateAndSave,
)
],
),
)
)
);
}
}
There is nothing wrong with the above code.
Anyway as I was testing the above code so Added/replaced few things like a validator class FieldValidator and instead of column use ListView etc.
Check out the code :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: LoginPage(),
);
}
}
class FieldValidator {
static String validateEmail(String value) {
if (value.isEmpty) return 'Email can\'t be empty!';
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = RegExp(pattern);
if (!regex.hasMatch(value)) {
return 'Enter Valid Email!';
}
return null;
}
static String validatePassword(String value) {
if (value.isEmpty) return 'Password can\'t be empty!';
if (value.length < 7) {
return 'Password must be more than 6 charater';
}
return null;
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String _email;
String _password;
final _formKey = GlobalKey<FormState>();
void validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
print('Form is valid $_email $_password');
} else {
print('Form is invalid');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: Container(
padding: const EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: ListView(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: FieldValidator.validateEmail,
onSaved: (value) => _email = value.trim(),
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
validator: FieldValidator.validatePassword,
onSaved: (value) => _password = value.trim(),
),
RaisedButton(
child: Text('Login', style: TextStyle(fontSize: 20.0)),
onPressed: validateAndSave,
)
],
),
),
),
);
}
}
Hope it helps !
Assigning obscure text from a variable works for me.
obscureText: _obscurePasswordText
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();