Not able to import variable from a different class in Flutter - flutter

I am going off of a login screen tempalte,a nd am trying to get a widget class for a button to just show the username input as an alert on the screen. The usernameinput widget is defined but when I import it, it does not work.
class _InputEmailState extends State<InputEmail> {
final myController = new TextEditingController();
This is the part where I define the input, and this is where I import the class in the button widget:
import 'package:login_minimalist/widget/inputEmail.dart';
When I try and reference the myController.text value, I get the error
The getter 'myController' isn't defined for the class '_ButtonLoginState'.
import 'package:flutter/material.dart';
import 'package:login_minimalist/widget/inputEmail.dart';
class ButtonLogin extends StatefulWidget {
#override
ButtonLoginState createState() => ButtonLoginState();
}
class ButtonLoginState extends State<ButtonLogin> {
#override
Here is the button widget code:
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40, right: 50, left: 200),
child: Container(
alignment: Alignment.bottomRight,
height: 50,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.blue[300],
blurRadius: 10.0, // has the effect of softening the shadow
spreadRadius: 1.0, // has the effect of extending the shadow
offset: Offset(
5.0, // horizontal, move right 10
5.0, // vertical, move down 10
),
),
],
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FlatButton(
onPressed: () {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Retrieve the text the user has entered by using the
// TextEditingController.
content: Text(InputEmailState.getUsername),
);
},
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Sign in',
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
Icon(
Icons.arrow_forward,
color: Colors.lightBlueAccent,
),
],
),
),
),
);
And here is the Input code:
class InputEmailState extends State<InputEmail> {
final myController = new TextEditingController();
getUsername() {
return(myController.text);
}
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 50, left: 50, right: 50),
child: Container(
height: 60,
width: MediaQuery.of(context).size.width,
child: TextField(
controller: myController,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.lightBlueAccent,
labelText: 'Student ID',
labelStyle: TextStyle(
color: Colors.white70,
fontSize: 20,
),
),
),
),
);
}

If I understand your question correctly, you're trying to access _ButtonLoginState from another class/file. However, in Dart, classes, members, variables, etc. that begin with an underline ("_") are considered private. You can't access them from a different file (except in some special situations with libraries).
To solve this, you can change the name of the class to ButtonLoginState and it should work.
EDIT: In response to more info:
You don't seem to have fully understood the concepts of State in a StatefulWidget. I would strongly recommend taking a good look through the Flutter guide on the subject.
There are many different ways of managing state and what I am going to explain is almost definitely not the best option most of the time (this was the introductory approach some time ago, but I can't even find the example anymore), however, it does work. For a more general option, I recommend Provider.
In your case, the problem starts with this: Text(InputEmailState.getUsername). You're not calling InputEmailState.getUsername, you're simply passing a reference to it. You need to include parentheses to actually call it - InputEmailState.getUsername().
However, this isn't the whole issue. You're trying to access this function using the name of the class, which means you're trying to use it as a static method. However, its an instance method (ie: you need a specific instance of the class to access it. This is the state I was talking about.
To simply get access to the state object of a specific widget, you can use a Key (generally a GlobalKey). You can define this in a parent widget, for example, and pass it as the key parameter of your InputEmail widget and keep a reference in the parent class. Then, to get the username, you can call <key>.currentState.getUsername() which will return the instance value. The exact implementation varies, and I don't have your code to know how it should be implemented.
As I say, this isn't really the recommended approach anymore. I strongly recommend getting to grips with the state concept, then it should be obvious what the best approach is.

Related

I want to use remaining available space on my page in Flutter

In the screen, I have a Column, it has a cusotm made widget of specific height. Then, I have Expanded, in which I have a TabBar which has three tabs.
In one of those tabs, I want to show a list. First, I have a padding, which contains column. The column has some text, which should remain at top and the list should be shown in the space which is remaining. I am using Expanded for that, but it is not working.
I can't use ListView directly, and also can't use expanded. It is only working when I am giving it a container of fix size. Now, in different screens, it will look different. So, I want to take all of the remaining space and build the list there. Code for reference -
Here is the doubts screen, which is one of the tabs of main screen -
import 'package:flutter/material.dart';
import 'package:my_board_plus/size_config.dart';
import 'package:my_board_plus/styles.dart';
import '../../api_handling/api_fetch/fetch_doubt_questions.dart';
import '../../data_models/doubt_question_model.dart';
class NewDoubtsScreen extends StatefulWidget {
const NewDoubtsScreen({Key? key}) : super(key: key);
#override
State<NewDoubtsScreen> createState() => _NewDoubtsScreenState();
}
class _NewDoubtsScreenState extends State<NewDoubtsScreen> {
late Future<List<DoubtQuestionModel>> doubtQuestionsList;
#override
void initState() {
doubtQuestionsList = fetchDoubtQuestion();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor2,
floatingActionButton: Container(
width: getProportionateScreenWidth(130),
height: getProportionateScreenHeight(50),
decoration: BoxDecoration(
color: brandPurple,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Center(
child: Text(
'? My Doubts',
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
),
),
body: Padding(
padding: EdgeInsets.only(top: 8.0, left: 5),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Trending Doubts',
style: TextStyle(
color: Colors.white,
),
),
Text(
'View all',
style: TextStyle(
color: brandYellow,
decoration: TextDecoration.underline
),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
height: getProportionateScreenHeight(530),
width: double.infinity,
color: Colors.red,
),
),
],
),
),
);
}
}
The red area that you are seeing is the one. I want it to occupy whole area available in the phone screen, so I can show list in it which should be scrollable. In this case, it is occupying all, but in different screens, it might not. So, please give me some suggestions.
You can try to give the height of the container like
height: double.infinity
Or you can give the height of it with substracting the other height from the screen size like
height: MediaQuery.of(context).size.height - getProportionateScreenHeight(50) //the heigth size that you give the other widget that top of it
try to wrap your Padding widget with the Expanded widget,
Expanded widget in column will take the rest of the space of the screen.
also no need to give height to Container widget, so you can remove getProportionateScreenHeight(530) this one

How to add a mark as fabourtie icon in my flutter app?

I am developing a Quotes app as a beginner in flutter.I have multiple page in my app.Right now i want to create an icon button which will perform as a bookmark(Mark as fabourite) for the user.So i added the flutter fabourite button in my app.Initially it stays white and when i touch it ,it becomes red,which i wants.But when i move to another page and get back to the previous one(Where the fabourite icon was added) the button become white...I want it to stay red unles the user touch it again.I just want it to be used as an marked as fabourite icon...What can i do now?
class p1 extends StatefulWidget {
#override
_p1State createState() => _p1State();
}
class _p1State extends State<p1> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body:Stack(
children:<Widget>[
Image(
image:AssetImage("Image/Chowsun1.jpg"),
fit:BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
Align(alignment: Alignment.center,
child: Text(' Hello world '
,style: TextStyle(fontSize: 35.0,
color: Colors.white,
fontFamily: "Explora",
fontWeight: FontWeight.w900 ) )
),
Align(
alignment: Alignment.bottomLeft,
child: const Text(' 1 ',
style: TextStyle(
fontSize: 25.0,
fontFamily: "MonteCarlo",
color: Colors.white,
fontWeight: FontWeight.w900),
),
),
Align(
alignment: Alignment.bottomCenter,
child: FavoriteButton(
isFavorite: true,
iconSize: 40,
iconDisabledColor: Colors.red,
iconColor: Colors.white,
// iconDisabledColor: Colors.white,
valueChanged: (_isFavorite) {},
)
)])
),
);
}
}
The issue is that you are not storing the state of the _isFavorite anywhere, that's what we use StatefulWidget for, is to keep track of state (and changes to it)
At the start of your class, declare a bool to track this value in:
class _p1State extends State<p1> {
bool _isFavorite = false;
then later in your build method:
FavoriteButton(
isFavorite: _isFavorite,
Finally, once you tap the button, you'll need to inform your class that the value has changed:
valueChanged: (isFav) {setState(() { _isFavorite = isFav; })},
Note that I changed the argument in your valueChanged, because we are now tracking the value in _isFavorite (the leading underscore indicates it's a privately scoped variable, on the class. And isFav is scoped only to the valueChanged method, so it doesn't actually need the leading underscore.

Why does my widget rebuild when I use keyboard

I have this issue of rebuilding widget when The keyboards shows up. I tried to use the sizer package but never could figure out how to get it to work
when I go back from this screen everything in the previous screen will rebuild, Please note: If I don't click on the typeaheadwidget such that the keyboard doesn't show up the state is preserved in the previous screen but as soon as the keyboard pops up the widgets get rebuilt
Could you please check ?
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
TextEditingController pickUpTextEditingController = TextEditingController();
TextEditingController dropOffTextEditingController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
#mustCallSuper
Widget build(BuildContext context) {
String placeAddress =
Provider.of<AppData>(context).pickUpLocation.placeName ?? "";
pickUpTextEditingController.text = placeAddress;
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
Container(
height: 250.0,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 6.0,
spreadRadius: 0.5,
offset: Offset(0.7, 0.7),
)
],
),
child: Padding(
padding: EdgeInsets.only(
left: 25.0, top: 30.0, right: 25.0, bottom: 20.0),
child: Column(
children: [
SizedBox(height: 5.0),
Stack(
children: [
GestureDetector(
onTap: () {
Navigator.pop(
//send back data
context,
dropOffTextEditingController.text);
},
child: Icon(Icons.arrow_back)),
Center(
child: Text(
"Set Drop Off",
style: TextStyle(
fontSize: 18.0, fontFamily: "Brand-Bold"),
),
)
],
),
SizedBox(height: 16.0),
Row(
children: [
Image.asset("images/images/pickicon.png",
height: 16.0, width: 16.0),
SizedBox(width: 18.0),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0),
),
child: Padding(
padding: EdgeInsets.all(3.0),
child: TextField(
controller: pickUpTextEditingController,
decoration: InputDecoration(
hintText: "PickUp Location",
fillColor: Colors.grey[400],
filled: true,
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.only(
left: 11.0, top: 8.0, bottom: 8.0),
),
),
),
))
],
),
SizedBox(height: 10.0),
Row(
children: [
Image.asset("images/images/desticon.png",
height: 16.0, width: 16.0),
SizedBox(width: 18.0),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0),
),
child: Padding(
padding: EdgeInsets.all(3.0),
child: TypeAheadField(
itemBuilder: null,
onSuggestionSelected: null,
suggestionsCallback: null,
),
),
),
),
],
),
],
),
),
),
],
),
);
}
}
You should not try to control when the build method is called. Flutter will call build when it decides it needs to (e.g. keyboard appears, device rotated, parent rebuilds, etc).
Instead, you should make sure that your build method is a "pure" function. In Flutter specifically, this means that you should not perform any action with "side-effects" (basically anything which modifies the state of the app).
For example:
Widget build(BuildContext context) {
final x = 2 + 3; // fine, nothing else is modified
final state = context.watch<MyModel>(); // also fine, only reading data
controller.text = "hello"; // BAD, modifies the state of the app
return ...;
}
Instead, you should move your logic with side effects into other lifecycle methods (e.g. initState(), didChangeDepencencies(), etc).
For example, if you want to set your text field to a particular string when it first appears, you can use initState:
class _SearchScreenState extends State<SearchScreen> {
#override
void initState() {
super.initState();
final data = context.read<AppData>();
controller.text = data.pickUpLocation.placeName ?? "";
}
Widget build(BuildContext context) {
// ...
}
}
Now build() can be called whenever it has to be, without resetting the state of your text field.
Note that, even if there was some way to prevent your widget from being rebuilt, this is also likely not what you want, since the UI would not update to accommodate the keyboard.
the only reason why your widgets got rebuilds after keyboard pop up.
is that one or more of your widgets size depends on MediaQuery.
you can try to ge your screen size from LayoutBuilder as an alternative for MediaQuery.
Give the textfield an initial value like this:
initvalue:Provider.of<AppData>(context).pickUpLocation.placeName ?? ""
and use onchange method in text field instead of text editing controller like this:
onchange(value){
Provider.of<AppData>(context).pickUpLocation.placeName=value;}
I am also facing the same issue, My blocBuidler is getting rebuilt every time when click on textfield or keyboard is appear.
In my case, I was calling the event in parent BlocBuilder so whenever I pressed on textfields the parent BlocBuilder is called the event, so it builds state of child BlocBuilder
Make sure you are also doing the same thing. If you are doing the same thing please check the state whether it is already built or not.
(BlocProvider.of<YourBlocName>(context).state is YouBlocState) ? Print('do nothing'): BlocProvider.of<YourBlocName>(context).add(youBlocEvent);
When you tap the TextField widget, it makes the keyboard show up. And when the keyboard shows up, your screen size changes. This causes the rebuild

Manage multiple form validation in PageView flutter

I have multiple forms inside a PageView, Forms are in different files like registration_form.dart contains the Sign-Up form and so on. In my App, each page contains a different Form. I want that when the user clicks on "Continue", the form will be validated and in an error situation, the user will be warned. I call all the pages in one class called Body as shown below. The "Continue" button is inside of it in the Opacity container. If there is a better approach to follow as a solution I am open to recommendations.
#override
Widget build(BuildContext context) {
return SafeArea(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: MediaQuery.of(context).size.height * 0.65,
child: Flex(
direction: Axis.horizontal,
children: [
Flexible(
child: PageView(
controller: _controller,
//physics: new NeverScrollableScrollPhysics(),
children: [
RegisterForm(),
WelcomeForm(),
//CompanyForm(),
//CompanyNextForm(),
//CompanyLogoForm(),
//FinancingDataForm(),
//UtilityForm(),
//MatrixInformationForm(),
//MatrixInformationNextForm(),
//MatrixInformationLastForm(),
//PriceBuildingForm(),
//InstallKitForm(),
//InstallKitDetailedForm(),
//CustomPricingForm(),
//CustomPricingNextForm(),
//FillRow1Form(),
//FillItem1Row1Form(),
//FillItem2Row1Form(),
//FillItem3Row1Form(),
//FillRow2Form(),
//FillItem1Row2Form(),
//FillItem2Row2Form(),
//FillItem3Row2Form(),
//FillRow3Form(),
//FillItem1Row3Form(),
//FillItem2Row3Form(),
//FillItem3Row3Form(),
//InvoicingForm(),
//FinancingForm(),
//FinancingNextForm(),
//FinancingLastForm(),
//FinalizeForm(),
//DoneForm(),
//BookingForm(),
],
),
),
],
),
),
SizedBox(
height: ResponsiveLayout.isSmallScreen(context)
? 10
: ResponsiveLayout.isMediumScreen(context)
? 10
: 10,
),
Opacity(
opacity: 1, //currentIndex == 20 ? 0 : 1,
child: Container(
height: 50,
decoration: BoxDecoration(
color: Color.fromRGBO(16, 88, 198, 1),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: GestureDetector(
onTap: () {
_controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn);
},
child: Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
color: Color.fromRGBO(16, 88, 198, 1),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 100.0,
child: Center(
child: RichText(
text: TextSpan(children: [
WidgetSpan(
child: Text(
'Continue ',
style: TextStyle(
color: Colors.white,
fontSize: ResponsiveLayout
.isSmallScreen(context)
? 12
: ResponsiveLayout.isMediumScreen(
context)
? 12
: 15,
),
)),
WidgetSpan(
child: Icon(
Icons.arrow_forward,
size: ResponsiveLayout.isSmallScreen(
context)
? 12
: ResponsiveLayout.isMediumScreen(
context)
? 12
: 15,
color: Colors.white,
),
),
]),
),
)),
],
),
),
)),
),
],
),
),
);
}
Okay. I was struggling with the same question recently and was looking for a good approach. Maybe this answer will be helpful for any other developer looking for the answer.
Approach
Currently, in my case, I used form keys for validations and function callbacks. This solution did the job for me because I needed to take input as raw text. Others like multiple choice options similar where there were predefined outputs.
To describe my solution more explicitly. Consider this example, we want to get basic details of the user like name, age, city etc. For user input like the name, we can use TextFormField. This will give access to the onChanged callback for validation. To access the response in the PageView widget containing the class. You can use the TextEditingController.
Now, we can then simply add the Form widget at the parent of the basic form widget build method.
Finally for multiple choice questions. We can provide a callback function like onTap to the widget of PageView. This function will be called whenever the user interacts with the dropdown or similar widget.
Note: If we have multiple forms in the PageView widget. You will be needing separate form keys for individual forms.
Code Example
So, we have the main form_screen.dart containing the PageView widget and basic_profile.dart containing our form. Both the files should look something like this:
form_screen.dart
...
// define the variables and keys here
final _basicProfileKey = GlobalKey<FormState>();
final _userName = TextEditingController();
late String _userGender;
...
// callback function that we will be passing to the BasicProfile
// widget on the other page
void _userGender(String value) {
_userGender = value;
}
...
// the submission callback that will be called whenever the user
// clicks on the next or save button available in the class file
// (this file) containing the PageView widget
void _submissionCallback(){
if(_pageViewIndex == 0) {
final validationStatus = _basicProfileKey.currentState?.validate() ?? false;
if(validationStatus) {
// implement your logic here and then move to next page in the pageview
}
}
}
...
// Build method widget tree containing the PageView and BasicProfile
// widgets
child: PageView(
children: [
BasicProfile(
basicProfileKey: _basicProfileKey,
userName: _userName,
userGenderCallback: userGender
),
]
),
basic_profile.dart
...
// declare the variables for this widget which we will be initialised
// via constructor
final GlobalKey<FormState> basicProfileKey;
final TextEditingController userName;
final Function(String) userGenderCallback;
...
#override
void initState(){
// initialise the default values here if any and call the
// callback function received above
userGenderCallback(_defaultValue);
}
...
Widget build(BuildContext context){
...
child: Form(
key: basicProfileKey,
...
TextFieldForm(
onValidate: (){
// do the validation here
}
)
...
DropDown(
onChanged: (value) {
// logic for validation
userGenderCallback(value);
}
)
}
In my approach used setState as the state management solution but other state solutions can also be used for easier state sharing between the widgets.
Hope this helps!

Flutter ListView doesnt update when the underlying list is updated

I am building this card game app which has a list of Cards (containers with special effects) and the list is managed and updated by the Provider/Consumer mechanism.
It looks like this
class LowerSectionWithScrollingCardList extends StatefulWidget {
#override
_LowerSectionWithScrollingCardListState createState() =>
_LowerSectionWithScrollingCardListState();
}
class _LowerSectionWithScrollingCardListState
extends State<LowerSectionWithScrollingCardList> {
#override
Widget build(BuildContext context) {
return Consumer<GameState>(builder: (context, gameState, child) {
print('lower list ${gameState.gcurrentPlayers[0].ownList}');
return Expanded(
flex: 34,
child: Container(
color: Colors.white,
child: ListView(
children: gameState.gcurrentPlayers[0].ownList,
scrollDirection: Axis.horizontal,
),
),
);
});
}
}
gameState.gcurrentPlayers[0].ownList is the first player which is us, and ownlist is the actual list of widgets or cards which gets updated by clicking some other buttons in the app.
the list is updated by this method exactly
void ggiveCardToCurrentPlayer(int howMuch){
for(int i=0;i<howMuch;i++)
ggetPlayerWithCurrentTurn().ownList.add(gplayingCards.removeLast());
notifyListeners();
}
Now after the "notifylisteners" is called, I am 100% sure that the Consumer is updated with the new data, because the print statement in the build method prints the newly added cards.
Finally, the issue is that the listView itself doesn't update while the list it renders has those added cards.
I checked out a few posts regarding a similar issue and they suggest that one add keys to the Data items, in my case the data items are my cards, and I added keys to them. No change.
class RegularUnoCard extends StatelessWidget{
final Color _color;
final String _value;
final Key _key;
RegularUnoCard(this._color, this._value,this._key);
#override
Widget build(BuildContext context) {
return Container(
key: _key,
margin: EdgeInsets.symmetric(
vertical: _cardMarginVer, horizontal: _cardMarginHor),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_cardCornerRadii),
border: Border.all(color: _color, width: 4, style: BorderStyle.solid),
boxShadow: [
BoxShadow(
color: _color,
spreadRadius: (_value == plus2) ? 8 : 2,
blurRadius: 5)
],
color: Colors.white,
),
child: Container(
height: _cardHeight,
width: _cardWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60),
color: _color,
),
child: Center(
child: getLogo(),
),
),
);
}
I hope this the correct way of putting keys in the Cards.
I also read that one must call setState() but I dont have any place to call Setstate from within my listView.
I have tried replacing the ownList logic with Provider.of(context).playerlist[0].ownlist etc etc but that too doesnt work
I hope I have supplied enough data for this evaluation. Please comment if more information is required. Thanks a lot for your time and suggestions.
I read more about the problem, the source which was helpful was this
Basically my list was being updated but I was providing the reference of the array, and since flutter works on immutable data, it did not detect my array change. So all I had to do was to build a new list from the existing reference array.
children: List<Widget>.from(gameState.gcurrentPlayers[0].ownList),
The final ListView should look like
#override
Widget build(BuildContext context) {
return Consumer<GameState>(builder: (context, gameState, child) {
print('lower list ${gameState.gcurrentPlayers[0].ownList}');
return Expanded(
flex: 34,
child: Container(
color: Colors.white,
child:ListView(
children: List<Widget>.from(gameState.gcurrentPlayers[0].ownList),
scrollDirection: Axis.horizontal,
),
),
);
},key: UniqueKey(),);
}
}
Now my card game is updating with the new cards!!