How can I use setState in Flutter - flutter

Why when I use switch case or if condition inside setState I need to click twice to get the result in Text-Widget
here is the switch case with setState
var name = 'Mustafa';
int? index;
onTap: (index) {
setState(() {
switch (index) {
case 0:
{
name = "Mustafa";
}
break;
case 1:
{
name = "Kamel";
}
break;
case 2:
{
name = "Mohammed"
"";
}
break;
case 3:
{
name = "Hussain";
}
}
});
},
and here is the Text widget
body: TabBarView(
children: [
Center(
child: Text(
name,
)),
Center(
child: Text(
name,
)),
Center(
child: Text(
name,
)),
Center(child: Text(name)),
],
)));
}
}
I tried everything I can think of but I couldn't solve it

Firstly, try to initialize the index variable with a value like zero instead of null value. Then, Add a default block to the switch statement and see if it works.

Related

How to change variable value in flutter with bloc?

Want to ask is How to change variable value with stream flutter?
You think my question is so fundamental and I can search in everywhere on internet. But in this scenario with stream, I can't change the variable value with method. How I need to do? please guide me. I will show with example.
Here, this is bloc class code with rxDart.
class ChangePinBloc {
final ChangePinRepository _changePinRepository = ChangePinRepository();
final _isValidateConfirmNewPinController = PublishSubject();
String oldPin = '';
Stream get isValidateConfirmNewPinStream =>
_isValidateConfirmNewPinController.stream;
void checkValidateConfirmNewPin(
{required String newPinCode, required String oldPinCode}) {
if (newPinCode == oldPinCode) {
oldPin = oldPinCode;
changePin(newCode: newPinCode);
isValidateConfirmPin = true;
_isValidateConfirmNewPinController.sink.add(isValidateConfirmPin);
} else {
isValidateConfirmPin = false;
_isValidateConfirmNewPinController.sink.add(isValidateConfirmPin);
}
}
void changePin({required String newCode}) async {
changePinRequestBody['deviceId'] = oldPin;
}
dispose() {
}
}
Above code, want to change the value of oldPin value by calling checkValidateConfirmNewPin method from UI. And want to use that oldPin value in changePin method. but oldPin value in changePin always get empty string.
This is the calling method checkValidateConfirmNewPin from UI for better understanding.
PinCodeField(
pinLength: 6,
onComplete: (value) {
pinCodeFieldValue = value;
changePinBloc.checkValidateConfirmNewPin(
newPinCode: value,
oldPinCode: widget.currentPinCodeFieldValue!);
},
onChange: () {},
),
Why I always get empty String although assign a value to variable?
Lastly, this is complete code that calling state checkValidateConfirmNewPin from UI.
void main() {
final changePinBloc = ChangePinBloc();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: StreamBuilder(
stream: changePinBloc.isValidateConfirmNewPinStream,
builder: (context, AsyncSnapshot pinValidateSnapshot) {
return Stack(
children: [
Positioned.fill(
child: Column(
children: [
const PinChangeSettingTitle(
title: CONFIRM_NEW_PIN_TITLE,
subTitle: CONFIRM_NEW_PIN_SUBTITLE,
),
const SizedBox(
height: margin50,
),
Padding(
padding: const EdgeInsets.only(
left: margin50, right: margin50),
child: PinCodeField(
pinLength: 6,
onComplete: (value) {
changePinBloc.checkValidateConfirmNewPin(
newPinCode: value,
oldPinCode: widget.newCodePinValue!,
);
},
onChange: () {},
),
)
],
),
),
pinValidateSnapshot.hasData
? pinValidateDataState(pinValidateSnapshot, changePinBloc)
: const Positioned.fill(
child: SizedBox(),
),
],
);
},
),
),
);
}
}
To update the variable you should emit a new state using emit() method.
Just make sure your bloc is correct as it should inherit from Bloc object. Read flutter_bloc documentation to know how to use it.
A simple example:
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
ExampleBloc() : super(ExampleInitial()) {
on<ExampleEvent>((event, emit) {
//Do some logic here
emit(ExampleLoaded());
});
}
}

How to remake a request when the page reload Flutter / GetX

I'm making a flutter App for a project in my School and I have a problem. I have a page where there are widgets representing categories of articles and when a category is clicked a page with articles from that category is displayed. The problem is that once a category is called, the articles in that category remain the same despite the category change.
When the page is called, a controller is created that will execute the query that retrieves the items in the category.
How can I get this controller to remind me every time the page is loaded?
Category page code :
class ProduceCategoryScreen extends StatefulWidget {
static String routeName = "/produceByCategorie";
#override
State<ProduceCategoryScreen> createState() => _ProduceCategoryScreenState();
}
class _ProduceCategoryScreenState extends State<ProduceCategoryScreen> {
static int gridColumn = 1;
static ArticleByCategoryController articleController =
Get.put(ArticleByCategoryController());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Produitss"),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: Text(
'Green Tomato',
style: TextStyle(
color: Colors.black,
fontSize: 32,
fontWeight: FontWeight.w900,
),
),
),
IconButton(
onPressed: () {
switch (gridColumn) {
case 1:
setState(() {
gridColumn = 2;
});
break;
case 2:
setState(() {
gridColumn = 1;
});
break;
default:
}
},
icon: Icon(Icons.grid_view),
)
],
),
),
Expanded(
child: Obx(
() {
if (articleController.isLoading.value)
return Center(child: CircularProgressIndicator());
else
return AlignedGridView.count(
crossAxisCount: gridColumn,
itemCount: articleController.articleList.length,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
itemBuilder: (context, index) {
return ProductTile(articleController.articleList[index]);
},
);
},
),
),
],
),
);
}
}
The category widget
Category screen
If you want more information you can send me a message on my discord : PascheK7#6324.
I think you are passing same list of data everytime when you execute below line.
"return ProductTile(articleController.articleList[index]);"
articleController.articleList : does this list contain only one category data ?
or it is contain category wise data.
e.g. : articleController.articleList[0] = category 1 list
articleController.articleList[1] = category 2 list
articleController.articleList[2] = category 3 list --> this way, it shouldn't make problem.
If articleList contain only one category data, then issue can happen that you get same data everytime. bcoz you are passing only index, but not category wise data.

How can I implement these functions into a togglebutton ? Can't get this to work

Sorry, I'm relatively new to Flutter and having some trouble. I want to initiate functions when I press my first, second, or third button. These buttons initiate map markers, polylines, etc. They work fine when assigned onPress with an icon button... I can't figure out how to get them to work with a ToggleButton... Do they have to be added to a list somehow? If so, how?
Here are my three buttons:
Consumer<ProviderMaps>(builder: (context, menu, widget) {
return Container(
padding: EdgeInsets.zero,
decoration: BoxDecoration(
border: Border.all(color: Colors.transparent, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
child: ToggleButtons(
selectedColor: Colors.red,
color: Colors.white,
children: <Widget>[
Icon(MdiIcons.vectorPolygon),
Icon(MdiIcons.ruler),
Icon(MdiIcons.pencil),
],
onPressed: (int index) {
setState(() {
for (int buttonIndex = 0;
buttonIndex < _selection.length;
buttonIndex++) {
if (buttonIndex == index) {
_selection[buttonIndex] = !_selection[buttonIndex];
} else {
_selection[buttonIndex] = false;
}
}
});
},
isSelected: _selection,
),
);
}),
My list:
List<bool> _selection = List.generate(3, (_) => false);
I'd like to call functions like these when I switch buttons:
menu.changestatutpolyg();
menu.changestatupolyli();
_initFreeDraw();
This is how I normally call these functions (works fine):
Consumer<ProviderMaps>(builder: (context, menu, widget) {
return IconButton(
icon: Icon(MdiIcons.ruler),
color: Color(0xffFFFFFF),
onPressed: () {
menu.changestatupolyli();
});
}),
Anybody know how to do this? Please advise.
Edit:
Attempt#:
onPressed: (int index) {
setState(
() {
switch (index) {
case 1:
{
menu.changestatutpolyg();
}
break;
case 2:
{
menu.changestatupolyli();
}
break;
case 3:
{
_initFreeDraw();
}
break;
}
},
);
},
isSelected: _selection,
),
);
}),
Well, the ToggleButtons's onPressed does provide you the index of the button pressed. So, the index would be 0 if the first button is pressed, one if the second, etc.
So in your onPressed, you can check the index, and based on that call the adequate function (regular if statements work fine as well):
onPressed: (int index) {
switch(index) {
case 0: {
callFunction1();
}
break;
case 1: {
callFunction2();
}
break;
//etc.
}
},
Or better, you can place the functions in an array (make sure to place them in the right order), and do something like:
var functions = <Function>[function1, function2, function3];
//...
//then, in the onPressed:
onPressed: (int index) {
//call the function of that index
functions[index]();
}

Question about Flutter State and retrieving variables from State vs StatefulWidget

Here's the context:
In my app, users can create a question, and all questions will be displayed on a certain page. This is done with a ListView.builder whose itemBuilder property returns a QuestionTile.
The problem:
If I create a new question, the text of the new question is (usually) displayed as the text of the previous question.
Here's a picture of me adding three questions in order, "testqn123", "testqn456", "testqn789", but all are displayed as "testqn123".
Hot restarting the app will display the correct texts for each question, but hot reloading wont work.
In my _QuestionTileState class, if I change the line responsible for displaying the text of the question on the page, from
child: Text(text)
to
child: Text(widget.text)
the issue will be resolved for good. I'm not super familiar with how hot restart/reload and state works in flutter, but can someone explain all of this?
Here is the code for QuestionTile and its corresponding State class, and the line changed is the very last line with words in it:
class QuestionTile extends StatefulWidget {
final String text;
final String roomName;
final String roomID;
final String questionID; //
QuestionTile({this.questionID, this.text, this.roomName, this.roomID});
#override
_QuestionTileState createState() => _QuestionTileState(text);
}
class _QuestionTileState extends State<QuestionTile> {
final String text;
int netVotes = 0;
bool expand = false;
bool alreadyUpvoted = false;
bool alreadyDownvoted = false;
_QuestionTileState(this.text);
void toggleExpansion() {
setState(() => expand = !expand);
}
#override
Widget build(BuildContext context) {
RoomDbService dbService = RoomDbService(widget.roomName, widget.roomID);
final user = Provider.of<User>(context);
print(widget.text + " with questionID of " + widget.questionID);
return expand
? ExpandedQuestionTile(text, netVotes, toggleExpansion)
: Card(
elevation: 10,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 7, 15, 7),
child: GestureDetector(
onTap: () => {
Navigator.pushNamed(context, "/ChatRoomPage", arguments: {
"question": widget.text,
"questionID": widget.questionID,
"roomName": widget.roomName,
"roomID": widget.roomID,
})
},
child: new Row(
// crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
// the stack overflow functionality
children: <Widget>[
InkWell(
child: alreadyUpvoted
? Icon(Icons.arrow_drop_up,
color: Colors.blue[500])
: Icon(Icons.arrow_drop_up),
onTap: () {
dynamic result = dbService.upvoteQuestion(
user.uid, widget.questionID);
setState(() {
alreadyUpvoted = !alreadyUpvoted;
if (alreadyDownvoted) {
alreadyDownvoted = false;
}
});
},
),
StreamBuilder<DocumentSnapshot>(
stream: dbService.getQuestionVotes(widget.questionID),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
// print("Current Votes: " + "${snapshot.data.data["votes"]}");
// print("questionID: " + widget.questionID);
return Text("${snapshot.data.data["votes"]}");
}
},
),
InkWell(
child: alreadyDownvoted
? Icon(Icons.arrow_drop_down,
color: Colors.red[500])
: Icon(Icons.arrow_drop_down),
onTap: () {
dbService.downvoteQuestion(
user.uid, widget.questionID);
setState(() {
alreadyDownvoted = !alreadyDownvoted;
if (alreadyUpvoted) {
alreadyUpvoted = false;
}
});
},
),
],
),
Container(
//color: Colors.red[100],
width: 290,
child: Align(
alignment: Alignment.centerLeft,
child: Text(text)), // problem solved if changed to Text(widget.text)
),
}
}
You can wrap your UI with a Stream Builder, this will allow the UI to update every time any value changes from Firestore.
Since you are using an item builder you can wrap the widget that is placed with the item builder.
That Should update the UI

Flutter: Prevent executed feturebuilder when setState is occurred

I am trying to load DropDownMenu inside Future builder.In my widget i have a Column. Inside Column I have a few widget :
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(),
Divider(),
Container(),
...widget._detailsModel.data.appletActions.map((item) {
.....
...item.appletInputs.map((inputs) {
FutureBuilder(
future: MyToolsProvider()
.getDropDownConfiges(inputs.dataUrl),
builder:
(ctx,AsyncSnapshot<DropDownModel.DropDownConfigToolsModle>snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData &&
snapshot.connectionState ==
ConnectionState.done) {
_dropDown = snapshot.data.data[0];
return DropdownButton<DropDownModel.DataModle>(
hint: Text("Select Item"),
value: _dropDown,
onChanged: (data) {
setState(() {
_dropDown = data;
});
},
items: snapshot.data.data.map((item) {
return DropdownMenuItem<
DropDownModel.DataModle>(
value: item,
child: Row(
children: <Widget>[
Icon(Icons.title),
SizedBox(
width: 10,
),
Text(
item.title,
style: TextStyle(
color: Colors.black),
),
],
),
);
}).toList(),
);
} else {
return Center(
child: Text('failed to load'),
);
}
}),
}
}
]
As you can see i have FutureBuilder inside a loop to show DropdownButton.everything is ok and code works as a charm but my problem is :
onChanged: (data) {
setState(() {
_dropDown = data;
})
every time setState called, future: MyToolsProvider().getDropDownConfiges(inputs.dataUrl), is executed and
_dropDown = snapshot.data.data[0]; again initialized and it get back in a first time .
It is not possible declared MyToolsProvider().getDropDownConfiges(inputs.dataUrl), in initState() method because inputs.dataUrl it is not accessible there.
How can i fixed that?
Updating parent state from within a builder is anti-pattern here. To reduce future errors and conflicts I recommend to wrap the parts that use and update _dropDown variable as a statefull widget.
Afterward the builder is just responsible of selecting correct widget based on future results and separated widget will only update itself based on interactions. Then hopefully many current and potential errors will disappear.
Do one thing, change this
_dropDown = snapshot.data.data[0];
to
_dropDown ??= snapshot.data.data[0];
What this will do is, it will check if _dropDown is null then assign it with value otherwise it won't.