TextField unfocuses when the soft keyboard appears - android

For some reason, when I press the TextField, it focuses for a split second and then unfocuses immediately as the soft keyboard comes up. I can still type and submit, but the labelText doesn't disappear like it's supposed to and most importantly, FocusManager.instance.primaryFocus?.unfocus() doesn't let the keyboard disappear.
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(5),
child: Row(
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
child: Container(
margin: const EdgeInsets.only(left: 10, bottom: 10, top: 10),
child: TextField(
onTap: () => myFocusNode.requestFocus(),
decoration: const InputDecoration(
labelText: ' Enter task',
border: InputBorder.none,
floatingLabelBehavior: FloatingLabelBehavior.never,
),
controller: textController,
focusNode: myFocusNode,
onSubmitted: (_) {
submit();
myFocusNode.requestFocus();
textController.clear();
},
),
),
),
),
CircleAvatar(
child: TextButton(
onPressed: () {
submit();
myFocusNode.requestFocus();
textController.clear();
},
child: const FittedBox(
child: Text(
'Add',
style: TextStyle(
color: Colors.white,
),
),
),
),
),
],
),
);
}
I think this may be because the app is rebuilt when the soft keyboard shows up, but I'm not sure. What can I do to fix this?

I think you need to remove this:
onTap: () => myFocusNode.requestFocus(),
If you need, you can control the focus action when filed si submitted with the textInputAction property:
// Go to next field
textInputAction: TextInputAction.next
// Go to previous field
textInputAction: TextInputAction.previous
// Don't move focus
textInputAction: TextInputAction.none
// Many other possible values, check the doc ...
UPDATE
When i try your build code on a MediaPad tablet, it work like a charm, here is my implementation:
import 'package:flutter/material.dart';
class DoorMeasure extends StatefulWidget {
const DoorMeasure({Key? key}) : super(key: key);
#override
State<DoorMeasure> createState() => _DoorMeasureState();
}
class _DoorMeasureState extends State<DoorMeasure> {
var myFocusNode;
var textController = TextEditingController()..text = '';
#override
void initState() {
myFocusNode = new FocusNode();
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
child: Container(
margin: const EdgeInsets.only(left: 10, bottom: 10, top: 10),
child: TextField(
onTap: () => myFocusNode.requestFocus(),
decoration: const InputDecoration(
labelText: ' Enter task',
border: InputBorder.none,
floatingLabelBehavior: FloatingLabelBehavior.never,
),
controller: textController,
focusNode: myFocusNode,
onSubmitted: (_) {
myFocusNode.requestFocus();
textController.clear();
},
),
),
),
),
CircleAvatar(
child: TextButton(
onPressed: () {
myFocusNode.requestFocus();
textController.clear();
},
child: const FittedBox(
child: Text(
'Add',
style: TextStyle(
color: Colors.white,
),
),
),
),
),
],
),
);
}
}

Related

Updating State of StatefulWidget from other StatefulWidget in Flutter?

i am trying to update the state of the animated switcher in the middle area. i am trying to do this using a setstate in the lower area. but it does not work.
the first thing i did is to create a variable with a boolean data type in the home class.
then i passed the variable to both the middle area and the lower area
the idea was that if the same variable is passed to the class whose state i am trying to update, and the class with the set state, it would work. but it seems i am wrong. i would appreciate some assistance.
the boolean variable i am trying to make work is the _rep variable
This is the Home widget
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _animationController;
late AnimationController _controller;
late Animation<Offset> _animation;
late Animation<Offset> _anime;
bool _rep = true;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2)
);
_animation = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,3.0),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
_anime = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,-0.55),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
super.initState();
}
#override
void dispose() {
_animationController.dispose();
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(top: 3.h),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TopIcon(icons: Icons.arrow_back, color:Colors.grey.shade300 ,),
SizedBox(
height: 13.h,
width: 13.w,
child: Image.asset('assets/images/download.png')
),
TopIcon(icons: Icons.shopping_bag_outlined, color: Colors.grey.shade300,),
],
),
SizedBox(
height: 3.h,
),
Text('Frappuccino',
style: TextStyle(
fontSize: 27.sp,
fontWeight: FontWeight.bold
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('White Chocolate',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey.shade400
),
),
),
MiddleArea(
controller: _animationController,
animation: _animation,
rep: _rep,
),
LowerArea(controller: _animationController, anime: _anime, rep = _rep),
],
),
),
),
);
}
}
This is the middle area
class MiddleArea extends StatefulWidget {
MiddleArea({Key? key, required this.controller, required this.animation, required this.rep}) : super(key: key);
AnimationController controller;
Animation<Offset> animation;
final bool rep;
#override
State<MiddleArea> createState() => _MiddleAreaState();
}
class _MiddleAreaState extends State<MiddleArea> {
bool _flag = true;
bool _favourite = true;
#override
Widget build(BuildContext context) {
print(widget.rep);
return SizedBox(
height: 52.h,
child: Stack(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 135.0),
child: Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade200,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade100,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade50,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
],
),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
SizeAndFave(text: 'Preference'),
SizeAndFave(text: 'Fave!')
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: (){
setState(() {
_flag = !_flag;
});
},
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: widget.rep == true?Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
)
),
GestureDetector(
onTap: (){
setState(() {
_favourite = !_favourite;
});
},
child: _favourite? TopIcon(icons: Icons.favorite_border, color: Colors.brown.shade300)
:TopIcon(
icons: Icons.favorite, color: Colors.brown.shade300)
)
],
)
],
),
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition( opacity: animation,
child: child);
},
child: _flag == true ? Center(
key: const Key('1'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/starcup.png'),
),
),
):Center(
key: const Key('2'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/greeen.png'),
),
),
),
),
Positioned(
child:
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(top: 30.h),
child: TopIcon(
icons: Icons.car_crash_outlined, color: Colors.brown.shade300),
),
)),
const Positioned(
child:
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.only(top: 330.0, left: 14),
child: Text('\$ 5.99',
style: TextStyle(
fontSize: 27,
fontWeight: FontWeight.bold
),
),
),
))
],
),
);
}
}
and lastly, the lower area
class LowerArea extends StatefulWidget {
final AnimationController controller;
final Animation<Offset> anime;
bool rep;
LowerArea({Key? key, required this.controller, required this.anime, required this.rep}) : super(key: key);
#override
State<LowerArea> createState() => _LowerAreaState();
}
class _LowerAreaState extends State<LowerArea> {
bool _bigger = true;
bool _fade = true;
void move(){
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.all(1.h),
child: const Text('Tall Frappuccino',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.only(right: 5.h),
child: const Text('Swipe Down',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.all(2.h),
child: const Text('Pickup',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
)
],
),
),
SlideTransition(
position: widget.anime,
child: AnimatedContainer(
// height: 11.h,
width: _bigger ? 35.h: 80.h,
duration: const Duration(milliseconds: 500),
child: Stack(
fit: StackFit.loose,
children: [
Center(child: Image.asset('assets/images/baggie.png')),
Center(
child: Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: GestureDetector(
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
widget.rep = !widget.rep;
print('this is fade $_fade ');
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: _fade? Container(
key: Key('1'),
height: 8.h,
width: 7.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(0.3.h),
child: Icon(
Icons.lock_outline,
color: Colors.white54,
size: 2.5.h,
),
),
Icon(
Icons.arrow_drop_down,
color: Colors.white12,
size: 3.h,
),
],
),
):null,
),
),
),
)
],
),
),
)
],
);
}
use provider pacakges https://pub.dev/packages/provider
Create a class that inherits the functions of ChangeNotifyer to create a flag to control and create a setter.
provider class
class StateController extends ChangeNotifier{
bool _req = false;
bool get req => _req; //getter
setReqValue(){
_req = !_req;
notifyListener();
}
}
Declare the provider class in the main function. You can change the location of the declaration according to Wiget tree, but first declare it in main
Change main.dart
void main(){
runApp(
Multiprovider(
providers: [
ChangeNotifierProvider(create: (_) => StateController()),
],
child: HomePage(),
)
);
}
The UI is updated by notifyListener().
change UI
child: context.watch<StateController>().req == true ? Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
Change State
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
context.read<StateController>().setReqValue();
print('this is fade $_fade ');
});
},
I am too lazy to try understand your code. But if you want to update state of the page after you pop from Navigation to it.
In page you want to update
Navigation.push(context, /* Page that will change something */)
// Future will trigger then you return to this page.
.then((_) => setState(() {}))

Can't solve problem with FloatingActionButton

I want the dialog to open when a button is clicked, but an error occurs due to BLoC. Previously, there was such an error with the class itself, but I successfully solved it, and in this case, the complexity already arises. I've already tried a couple of options but couldn't solve it. I tried to make the event in onPressed as a separate widget, and the same error did not work either.
home_page
class HomePage extends StatelessWidget {
final todoRepository = TodoRepository();
#override
Widget build(BuildContext context) {
// final TodoBloc todoBloc = context.read<TodoBloc>();
return BlocProvider<TodoBloc>(
create: (context) => TodoBloc(todoRepository),
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter Todos'),
),
// floatingActionButton: FloatingActionButton(
// onPressed: () {
// // _addTodo(context);
// final newTodo = Todo(description: 'Todo 1');
// BlocProvider.of<TodoBloc>(context).add(CreateTodos(newTodo));
// },
// child: const Icon(Icons.add),
// ),
body: SingleChildScrollView(
child: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
// ActionButton(),
TodoList(),
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add, size: 32, color: Colors.black),
onPressed: () {
// showAddTodoSheet(context);
// showAddTodoSheet(BuildContext context) {
final TodoBloc todoBloc = context.read<TodoBloc>();
final _todoDescriptionFromController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return BlocBuilder<TodoBloc, TodoState>(
builder: (context, state) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
color: Colors.transparent,
child: Container(
height: 230,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0))),
child: Padding(
padding: const EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller:
_todoDescriptionFromController,
textInputAction:
TextInputAction.newline,
maxLines: 4,
style: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'I have to...',
labelText: 'New Todo',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500)),
validator: (value) {
if (value!.isEmpty) {
return 'Empty description!';
}
return value.contains('')
? 'Do not use the # char.'
: null;
},
),
),
Padding(
padding: const EdgeInsets.only(
left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: const Icon(
Icons.save,
size: 22,
color: Colors.white,
),
onPressed: () {
final newTodo = Todo(
description:
_todoDescriptionFromController
.value.text);
if (newTodo
.description.isNotEmpty) {
todoBloc
.add(CreateTodos(newTodo));
Navigator.pop(context);
}
},
),
),
)
],
),
],
),
),
),
),
);
});
});
},
),
));
}
You have to use MultiBlocProvider before MaterialApp.
just do like that
#override
Widget build(BuildContext context) => MultiBlocProvider(
providers: [
BlocProvider(create: (_) => TodoBloc()),
],
child: MaterialApp()
);

Why my list is not updating while using setState in Flutter

I made a list of class OriginDestination, i.e. _allCities in my file. I then assigned all values in filteredCities and cities in initState.
Then I made a function runFilter which would take keyword from TextField and filter the results accordingly and save them to resultCities. Then I am using resultCities to display the information in ListView.builder. But the problem is, the list is not filtering according to the keyword i am searching.
Also, it would be appreciated if you can suggest a better way of using parameter cities, i.e. I don't think that passing the cities as parameter through state's constructor is a good practice.
Here is the code -
import 'package:flutter/material.dart';
import 'package:passenger_flutter_app/models/new_city.dart';
import 'package:passenger_flutter_app/models/origin_destination.dart';
import 'package:passenger_flutter_app/utils/colors.dart';
class SelectionScreen extends StatefulWidget {
List<OriginDestination>? cities;
SelectionScreen({this.cities});
#override
_SelectionScreenState createState() => _SelectionScreenState(cities);
}
class _SelectionScreenState extends State<SelectionScreen> {
final List<OriginDestination>? _allCities;
_SelectionScreenState(this._allCities);
bool originSelected=false;
List<OriginDestination>? resultCities = [];
List<OriginDestination>? filteredCities = [];
void getCitiesFromResponse() {
/*for(var city in _allCities!) {
cities!.add(city.origin!);
}*/
filteredCities=_allCities;
resultCities=_allCities;
}
#override
initState() {
// at the beginning, all users are shown
getCitiesFromResponse();
super.initState();
}
void _runFilter(String enteredKeyword) {
if (enteredKeyword.isEmpty) {
// if the search field is empty or only contains white-space, we'll display all users
filteredCities = _allCities;
} else {
filteredCities = _allCities!
.where((city) =>
city.origin!.name!.toLowerCase().contains(enteredKeyword.toLowerCase()))
.toList();
// we use the toLowerCase() method to make it case-insensitive
}
#override
void setState() {
resultCities=filteredCities;
}
}
#override
Widget build(BuildContext context) {
var originSelected;
return SafeArea(
child: Scaffold(
backgroundColor: const Color(0xffEEEDEF),
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width*0.8,
),
Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
color: Colors.orange,
onPressed: () {
Navigator.pop(context);
},
),
Column(
children: [
originSelected==true ? Container(
child: Text(''),
) :
Container(
width: MediaQuery.of(context).size.width * 0.85,
height: 50.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(5.0)),
border: Border.all(color: colorAccent)),
child: TextField(
decoration: InputDecoration(
hintText: "Enter Origin",
border: InputBorder.none,
contentPadding: const EdgeInsets.only(left: 10.0),
hintStyle: TextStyle(
fontSize: 15.0,
color: Colors.grey[500],
),
),
onChanged: (value) {
_runFilter(value);
},
),
),
],
),
],
),
],
),
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.width * 0.04, top: 3.0),
child: Text(
'Popular Searches:',
style: TextStyle(
color: popUpLightTextColor,
fontSize: MediaQuery.of(context).size.width * 0.035),
),
),
),
Expanded(
child: ListView.builder(
itemCount: resultCities!.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(
left: 18.0, top: index==0 ? 29.0 : 15.0, bottom: 15.0),
child: InkWell(
onTap: () {
print(resultCities?[index].origin!.name);
/*setState(() {
widget.city=filteredCities[index]['city'];
print("Changed to - ");
//print(widget.city);
Navigator.pop(context);
});*/
},
child: Text(
resultCities?[index].origin!.name??"No name",
style: const TextStyle(
color: darkText,
fontSize: 15.0,
fontWeight: FontWeight.normal,
),
),
),
);
},
),
),
],
),
),
);
}
}
seems like you defined the setState function instead of calling it, so instead of:
#override
void setState() {
resultCities=filteredCities;
}
write:
setState(() {
resultCities=filteredCities;
});
Why are you overriding the setState. You also pass the call back as argument in the setState.
You should be call setState on a trigger, like a gesture or button:
setState(() => resultCities = filteredCities);

Want to add login button only when user input credentials

I want to make the login button visible only when user enter the credentials in Text Form Field (Email and Password), otherwise login button should be invisible.
My LoginPage is designed as below:-
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
late String _email, _password, error = '';
bool _obscureText = true;
bool _isKeyboardVisible = true;
bool _canShowButton = true;
final AuthenticationService _authenticationService = AuthenticationService();
_hideWidget(){
setState(() {
_canShowButton = !_canShowButton;
});
}
_toggle() {
setState(() {
_obscureText = !_obscureText;
});
}
_submit() async {
if(_formKey.currentState!.validate()){
_formKey.currentState?.save();
dynamic result = await _authenticationService.loginWithEmail(email: _email, password: _password);
if(result == null) {
setState(() => error = 'Es wurde kein Benutzerkonto gefunden. Bitte erstellen Sie zuerst das Konto');
} else {
Navigator.pushNamed(context, HomePage.id);
}
print(_email);
print(_password);
}
}
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
_isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom != 0;
if(!_isKeyboardVisible) {
return GestureDetector(
onTap: (){
FocusScopeNode currentFocus = FocusScope.of(context);
if(!currentFocus.hasPrimaryFocus){
currentFocus.unfocus();
}
},
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 20),
alignment: Alignment.center,
child: Text("Bitte Melde Dich an",
style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 28),),
),
Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 10.0),
child: Padding(
padding: EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0),
child: SizedBox(
child: TextFormField(
autofocus: false,
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(borderRadius: BorderRadius.all(
const Radius.circular(10.0),
)
),
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: color, width: 2.0),
borderRadius: BorderRadius.circular(10.0)
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: color, width: 2.0),
borderRadius: BorderRadius.circular(10.0)
)
),
validator: (input) => !EmailValidator.validate(input!, true)
? 'Please enter valid email'
: null,
onSaved: (input) => _email = input!,
),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0, vertical: 10.0
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 5, bottom: 5),
child: Stack(
alignment: const Alignment(0, 0),
children: <Widget>[
TextFormField(
autofocus: false,
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
const Radius.circular(10.0)
)
),
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: color, width: 2.0),
borderRadius: BorderRadius.circular(10.0)
),
suffixIcon: IconButton(
onPressed: (){
_toggle();
},
icon: Icon(_obscureText ? Icons.visibility : Icons.visibility_off)
)
),
validator: (input) => input!.length < 6
? 'Must be at least 6 characters': null,
onSaved: (input) => _password = input!,
obscureText: _obscureText,
)
],
),
),
),
Padding(
padding: const EdgeInsets.only(top: 10),
child: Container(
width: MediaQuery.of(context).size.width - 70,
height: MediaQuery.of(context).size.height * 0.09,
child: !_canShowButton ? SizedBox.shrink() : TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: color,
elevation: 5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0))
),
onPressed: () {
_hideWidget();
},
child: Text('Anmelden')),
),
),
],
),
)
],
),
),
),
);
} else {
return GestureDetector(
onTap: (){
FocusScopeNode currentFocus = FocusScope.of(context);
if(!currentFocus.hasPrimaryFocus){
currentFocus.unfocus();
}
},
child: Scaffold(
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 60),
alignment: Alignment.center,
child: Text(
'Login Credentials',
style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 28),
overflow: TextOverflow.clip,
)
),
Container(
margin: EdgeInsets.only(top: 20),
alignment: Alignment.center,
child: Text("Bitte Melde Dich an",
style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 28),),
),
Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 10.0),
child: Padding(
padding: EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0),
child: SizedBox(
/*height: _animation.value,*/
child: TextFormField(
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(borderRadius: BorderRadius.all(
const Radius.circular(10.0),
)
),
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: color, width: 2.0),
borderRadius: BorderRadius.circular(10.0)
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: color, width: 2.0),
borderRadius: BorderRadius.circular(10.0)
)
),
// focusNode: _focusNode,
validator: (input) => !EmailValidator.validate(input!, true) ? 'Please provide valid email':null,
onSaved: (input) => _email = input!,
),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0, vertical: 10.0
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 5, bottom: 5),
child: Stack(
alignment: const Alignment(0, 0),
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
const Radius.circular(10.0)
)
),
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: color, width: 2.0),
borderRadius: BorderRadius.circular(10.0)
),
suffixIcon: IconButton(
onPressed: (){
_toggle();
},
icon: Icon(_obscureText ? Icons.visibility : Icons.visibility_off)
)
),
validator: (input) => input!.length < 6
? 'Must be at least 6 characters': null,
onSaved: (input) => _password = input!,
obscureText: _obscureText,
)
],
),
),
),
],
),
)
],
),
),
),
);
In short I have added the bool variable and function as below. After that in onPressed of login Button I called _hideWidget().
bool _canShowButton = true;
_hideWidget(){
setState(() {
_canShowButton = !_canShowButton;
});
Padding(
padding: const EdgeInsets.only(top: 10),
child: Container(
width: MediaQuery.of(context).size.width - 70,
height: MediaQuery.of(context).size.height * 0.09,
child: !_canShowButton ? SizedBox.shrink() : TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: color,
elevation: 5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0))
),
onPressed: () {
_hideWidget();
},
child: Text('Anmelden')),
),
),
What I have achieved is: The Login Button is there on the page and when I click on TextFormField it gets disappear.
Want to achieve: Login Button should be invisible on the screen and when user enters the credentials after that it should appear.
Is there any way to achieve this??
This is how you can tell if there's data in the textfield or not, if there is it'll change isFilled to true.
bool isFilled = false;
TextFormField(
onChanged: (val) {
setState(() {
(val.isEmpty) ? isFilled = false : isFilled = true;
});
},
)
Now to show the button when there's data in the textfield you can use ternary operator.
(isFilled)?TextButton(onPressed: (){}, child: Container()) : Container()
Just switch TextButton with your button. Ternary operators work like so
(condition) ? (do if true) : (do if false)
in this case, you're showing the button if isFilled = true, and an empty container if not
Well the simplest way of implementing this is to add a TextEdittingController to each Textfield and then render the button only when the TextEdittingController.tex is not empty,when it's empty you can render an empty container otherwise render the button,use a simple tenary operator to do this

After using "pushReplacementNamed" in flutter I cannot push this Route again

I have problems using the Navigation in Flutter with the MVVM pattern.
I have two Views:
Login
Dashboard
I Navigate with Navigator.pushReplacementNamed(context, Dashboard) from Login to Dashboard.
When the user loggs out I naviagte from Dashboard to Login with Navigator.pushReplacementNamed(context, Login)
But the i get the following error:
A Login ViewModel was used after being disposed. Once you have called dispose() on a LoginViewModel, it can no longer be used.
I thought that a View\Widget is created when i call it again after it was disposed?
What should i do? It makes no sense to Navigate with
pushNamed
and leave the widget in the widget tree.
My LoginView:
import 'package:app/common/routing_contstants.dart';
import 'package:app/core/viewmodels/views/login_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_signin_button/button_view.dart';
import 'package:flutter_signin_button/flutter_signin_button.dart';
import 'package:app/common/colors.dart';
import 'base_view.dart';
class LoginView extends StatefulWidget {
#override
_LoginView createState() => _LoginView();
}
class _LoginView extends State<LoginView> {
final TextEditingController _mailCtr = TextEditingController();
final TextEditingController _passCtr = TextEditingController();
#override
Widget build(BuildContext context) {
return BaseView<LoginViewModel>(
builder: (context, model, child) => Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
// Add box decoration
decoration: BoxDecoration(
// Box decoration takes a gradient
gradient: LinearGradient(
// Where the linear gradient begins and ends
begin: Alignment.topRight,
end: Alignment.bottomCenter,
// Add one stop for each color. Stops should increase from 0 to 1
stops: [0.2, 0.4, 0.6, 1.0],
colors: [
Colors.deepPurple[800],
Colors.deepPurple[700],
Colors.deepPurple[500],
Theme.of(context).colorScheme.pink,
],
),
),
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: <Widget>[
SizedBox(
height: 120,
),
Image.asset(
"assets/images/icon.png",
color: Colors.white,
width: 200,
height: 100,
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _mailCtr,
decoration: InputDecoration(
hintText: "Email",
hintStyle: TextStyle(color: Colors.grey[500]),
icon: Icon(
Icons.email,
color: Colors.white,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
cursorColor: Colors.white,
style: TextStyle(color: Colors.white),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _passCtr,
decoration: InputDecoration(
hintText: "Password",
hintStyle: TextStyle(color: Colors.grey[500]),
icon: Icon(
Icons.lock,
color: Colors.white,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
cursorColor: Colors.white,
style: TextStyle(color: Colors.white),
obscureText: true,
),
),
GestureDetector(
onTap: () {},
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: new Text("Forget Password?",
style: Theme.of(context)
.textTheme
.body1
.copyWith(color: Colors.white)),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
width: double.infinity,
child: OutlineButton(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: new Text("Login",
style: Theme.of(context)
.textTheme
.title
.copyWith(color: Colors.white))),
onPressed: () async {
var loginSuccess = await model.signInWithEmailAndPassword(context, _mailCtr.text, _passCtr.text);
if (loginSuccess != null) {
Navigator.pushReplacementNamed(context, DashboardRoute);
}
},
// color: Colors.blue,
textColor: Colors.white,
borderSide: BorderSide(color: Colors.white),
)),
),
Padding(
padding: const EdgeInsets.all(0.8),
child: SignInButton(
Buttons.Google,
onPressed: () {},
),
),
Padding(
padding: const EdgeInsets.all(0.8),
child: SignInButton(
Buttons.Facebook,
onPressed: () {},
),
),
SizedBox(
height: 100,
),
],
),
),
)),
),
),
);
}
}
My DashboardView:
import 'package:app/common/routing_contstants.dart';
import 'package:app/core/viewmodels/views/dashboard_view_model.dart';
import 'package:flutter/material.dart';
import 'package:app/common/colors.dart';
import 'base_view.dart';
class DashboardView extends StatefulWidget {
#override
_DashboardView createState() => _DashboardView();
}
class _DashboardView extends State<DashboardView> {
#override
Widget build(BuildContext context) {
return BaseView<DashboardViewModel>(
builder: (context, model, child) => Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
// Add box decoration
decoration: BoxDecoration(
// Box decoration takes a gradient
gradient: LinearGradient(
// Where the linear gradient begins and ends
begin: Alignment.topRight,
end: Alignment.bottomCenter,
// Add one stop for each color. Stops should increase from 0 to 1
stops: [0.3, 0.7, 0.4, 0.3],
colors: [
Theme.of(context).colorScheme.blue,
Theme.of(context).colorScheme.darkBlue,
Theme.of(context).colorScheme.pink,
Colors.pink[600],
],
),
),
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: <Widget>[
SizedBox(height: 32.0),
SizedBox(
height: 50.0,
child: _signOutButton(context, model),
),
SizedBox(height: 30.0),
SizedBox(
height: 100,
),
],
),
),
),
),
),
),
);
}
Widget _signOutButton(context, DashboardViewModel model) {
return OutlineButton(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: new Text("Logout",
style: Theme.of(context)
.textTheme
.title
.copyWith(color: Colors.white))),
onPressed: () async {
await model.signOut(context).then(
(_) => {
Navigator.pushReplacementNamed(context, LoginRoute)
},
);
},
// color: Colors.blue,
textColor: Colors.white,
borderSide: BorderSide(color: Colors.white),
);
}
}
My BaseView:
import 'package:app/common/locator.dart';
import 'package:app/core/viewmodels/base_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BaseView<T extends BaseModel> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget child) builder;
final Function(T) onModelReady;
BaseView({this.builder, this.onModelReady});
#override
_BaseViewState<T> createState() => _BaseViewState<T>();
}
class _BaseViewState<T extends BaseModel> extends State<BaseView<T>> {
T model = getIt<T>();
#override
void initState() {
if (widget.onModelReady != null) {
widget.onModelReady(model);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (context) => model,
child: Consumer<T>(builder: widget.builder));
}
}
User Factory instead of RegisterLazySingleton.

Categories

Resources