Flutter and GetX - Widget doesn't redraw when I update my RxObject - flutter

Here the existant :
My IssueController
Rx<GitlabIssue> selectedIssue = new GitlabIssue().obs;
RxList<GitlabIssue> issues = <GitlabIssue>[].obs;
void changeIssueState(GitlabIssueState state) async {
var stateValue = _getGitLabIssueStateValue(state);
bool isInternet = await Utils().isInternet();
if (isInternet) {
var resp = await _issueProvider.changeGitlabIssueState(
projectId, selectedIssue.value.iid, stateValue);
if (resp.statusCode == 200) {
Get.snackbar(
'Issue state changed',
'Issue is $stateValue',
);
var issueIndex = issues.indexOf(selectedIssue.value);
selectedIssue.value.state =
selectedIssue.value.state == 'opened' ? 'closed' : 'opened';
issues[issueIndex] = selectedIssue.value;
gitLabIssueBox.put(projectId, issues);
} else
Get.snackbar(
'Issue state not changed', 'Issue state was not correctly changed');
} else
print('No connectivity');
}
My view
Widget _getButtonStatus() {
return Obx(() {
Color stateColor = _issueController.selectedIssue.value.state == 'closed'
? Colors.green
: Colors.red;
return OutlinedButton(
onPressed: () {
_issueController.selectedIssue.value.state == 'closed'
? _issueController.changeIssueState(GitlabIssueState.reopen)
: _issueController.changeIssueState(GitlabIssueState.close);
},
style: OutlinedButton.styleFrom(
//primary: Colors.white,
//backgroundColor: Colors.teal,
side: BorderSide(color: stateColor, width: 1),
),
child: Obx(
() => Text(
_issueController.selectedIssue.value.state == 'closed'
? 'Reopen issue'
: 'Close issue',
style: TextStyle(color: stateColor),
),
),
);
});
}
I update the value of my selectedIssue.obs and I put the widget in an Obx() => function.
I can't figure out why my widget doesn't redraw.
Weirdly, my onPressed function behave correctly; The button stay the same but if I click again on it the status of my issue change correctly.
Please help me !! :D

Related

Flutter Circular Progress Indicator On Submit Button

I have a Login screen and it has two text fields and a submit button, in this submit button, what im doing right now is that when its pressed it shows a loading icon and at some point it will navigate to the other screen, but if the usernames email and phone number is wrong i show a snackbar with and error message, the problem is that even when that happends the loading icon keeps loading and it never stops, what im want to do is that if everything is fine and the user can navigate to the other screen than show the loading icon and then the it will navigate to the other screen, and if the email or password are wrong in this case if response.statusCode is not equal to 200 i show the snackbar with the error message and i want loading icon to be false. same with if the text fields are empty, for now ive made a validator on my TextFormFields, so when the text fields are empty or response.statusCode != 200 loading = false
bool loading = true;
TextFormField loginEmailTextField() {
return TextFormField(
enableInteractiveSelection: false,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your phone number';
}
return null;
},
TextFormField loginPasswordTextField() {
return TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
},
ElevatedButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
Future<Response> futureResponse = fetchWorkingLocationData();
futureResponse
.then((response) => {
if (response.statusCode == 200)
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MenuPage()),
)
}
else
{
{
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.blue,
content: Text(
"Incorrect phone number or password",
style: TextStyle(fontSize: 18),
),
duration: Duration(seconds: 4),
),
),
}
},
})
.catchError((error, stackTrace) => print('shush'));
}
if (loading) return;
setState(() {
loading = true;
});
},
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: loading
? Loading()
: Text(
'Submit',
I'm not sure if your isLoading is already outside (it seems so),but isLoading boolean should be outside of your main widget build function, because whenever you call setState your boolean will be set to true again, also I would set it to false instead because you don't want it showing immediately on the login page
In your button function you should also call setState and set your isLoading value to whatever it should be when that if check is done
.then((response) => {
if (response.statusCode == 200)
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MenuPage()),
)
}
else
{
{
setState(){
isLoading = false;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.blue,
content: Text(
"Incorrect phone number or password",
style: TextStyle(fontSize: 18),
),
duration: Duration(seconds: 4),
),
),
}
},
})
Basically what you want to do is set the state of that boolean whenever it should be set to either true or false
This has to be done for every case that you have in your button (both for the icon and the loading indicator)

How to create a dynamic color for radio button on Flutter?

How can I set the color of radio button changed if the value also changed ?
here is the RadioButton
CustomRadioButton(
horizontal: false,
elevation: 0,
absoluteZeroSpacing: false,
width: 150,
enableButtonWrap: true,
//* warna radio button
selectedColor: warna(),
unSelectedColor: Color(0XFF343f56),
// selectedBorderColor: Colors.pink,
// unSelectedBorderColor: Colors.cyan,
buttonTextStyle: ButtonTextStyle(selectedColor: Colors.white),
enableShape: true,
buttonLables: _buttonLables,
buttonValues: _buttonValue,
radioButtonValue: (value) {
_radio = value;
print(_radio);
},
)
and this is the warna function
String _radio; //* value radio button
List<String> _buttonLables = ['Pengeluaran', 'Pemasukan'];
List<String> _buttonValue = ['PENGELUARAN', 'PEMASUKAN'];
warna() {
if (_radio == null) {
return Colors.blue;
} else if (_radio == 'PENGELUARAN') {
return Colors.red;
} else if (_radio == 'PEMASUKAN') {
return Colors.green;
}
}
I'm using a stateful widget but the color only changed when I hot reload the code
I should setState when the value changed
radioButtonValue: (value) {
setState(() {
_radio = value;
});
// _radio = value;
print(_radio);
},

Why AutoCompleteTextField is not showing any suggestion in Flutter?

I am new to Flutter and currently working on a project where I need to show user a list of matched members so that a user can easily select one of them. For that I use AutoCompleteTextField. It is working fine as long as provided by already fetched list of members to it's suggestion property. But I wonder, why it's not working when I put it under BlocBuilder. Event hits on textChanged method and the state also returns a list but the suggestions are invisible.
Widget autoCompleteSearchBar() {
return BlocBuilder<OrderInfoBloc, MyOrderInfoStates>(
builder: (context, state) {
return AutoCompleteTextField<Member>(
clearOnSubmit: false,
style: TextStyle(
color: Colors.black,
fontSize: 16,
),
decoration: InputDecoration(
hintText: 'Search Member Here..',
border: InputBorder.none,
suffixIcon: IconButton(
icon: Icon(Icons.cancel),
iconSize: 20,
color: Colors.yellow[700],
onPressed: () {
_autoCompleteController.text = "";
},
),
contentPadding: EdgeInsets.fromLTRB(10, 30, 10, 20),
hintStyle: TextStyle(color: Colors.grey),
),
keyboardType: TextInputType.text,
controller: _autoCompleteController,
textChanged: (value) {
context.read<OrderInfoBloc>().add(SearchTextChanged(text: value));
},
itemSubmitted: (item) async {
_autoCompleteController.text = state.radioGroupValue == 'By Code'
? item.memberNo
: item.memberName;
context.read<OrderInfoBloc>().add(SelectedMember(member: item));
},
key: _key,
suggestions: state.membersList,
itemBuilder: (context, item) {
print(item);
// return state.radioGroupValue == 'By Code'
// ? autoCompleteSearchBarRow(
// item: item.memberNo, icon: Icon(Icons.person))
// : autoCompleteSearchBarRow(
// item: item.memberName, icon: Icon(Icons.person));
return autoCompleteSearchBarRow(
item: item.memberNo, icon: Icon(Icons.person));
},
itemFilter: (item, query) {
print(query);
// bool _itemFilter;
// if (_autoCompleteController.text.isNotEmpty) {
// _itemFilter = state.radioGroupValue == 'By Code'
// ? item.memberNo
// .toLowerCase()
// .startsWith(query.toLowerCase())
// : item.memberName
// .toLowerCase()
// .startsWith(query.toLowerCase());
// } else {
// _autoCompleteController.text = '';
// _itemFilter = false;
// }
// return _itemFilter;
return item.memberNo.toLowerCase().startsWith(query.toLowerCase());
},
itemSorter: (a, b) {
// return state.radioGroupValue == 'By Code'
// ? a.memberNo.compareTo(b.memberNo.toLowerCase())
// : a.memberName.compareTo(b.memberName.toLowerCase());
print(b);
return a.memberNo.compareTo(b.memberNo.toLowerCase());
},
);
}
);
}
Widget autoCompleteSearchBarRow(
{#required String item, #required Icon icon}) {
return ListTile(
leading: icon,
title: Text(item),
);
}
Use the flutter_typeahead package which works well with flutter bloc
Now, come to the bloc side you don't need to wrap your autocomplete widget with blocbuilder cause if you do so, the bloc will always repaint the widget whenever an event fires. so in your case when you are typing in the text box, event fires and bloc rebuild the widget and because of that suggestion don't show up and even if you see suggestion they will be gone once the corresponding bloc state occurs and rebuild the widget
the recommended solution would be seen below
Don't add any state to get suggestions just return the result or records from event as below. (below function added to Cubit file)
Future<List<Item>> getProductItemsBySearchString(String item) async {
return await itemRepository.getItemsByName(item);
}
as you can see above I am returning item records directly from the getProductItemsBySearchString() event method (no bloc state)
Then use It like below
class ItemScreen extends StatelessWidget {
// then you can call bloc event in function as below
Future<List<Item>> getItemSuggestionsList(
BuildContext context, String text) async {
final bloc = context.read<ItemCubit>();
List<Item> data = await bloc.getProductItemsBySearchString(text);
if (data != null) {
return data;
} else {
return null;
}
}
#override
Widget build(BuildContext context) {
return TypeAheadField(
getImmediateSuggestions: true,
textFieldConfiguration: TextFieldConfiguration(
controller: _itemEditingController,
autofocus: false),
suggestionsCallback: (pattern) {
// call the function to get suggestions based on text entered
return getItemSuggestionsList(context, pattern);
},
itemBuilder: (context, suggestion) {
// show suggection list
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(suggestion.name),
trailing: Text(
'Item Code: ${suggestion.code}',
),
),
);
},
onSuggestionSelected: (suggestion) {
// get selected suggesion
},
);
}
}

Flutter Progress_state_button - how to change button state

I am trying to use the progress state button in flutter. From the pub.dev docs, the widget should be set up as follows
Widget buildTextWithIcon() {
return ProgressButton.icon(iconedButtons: {
ButtonState.idle: IconedButton(
text: "Send",
icon: Icon(Icons.send, color: Colors.white),
color: Colors.deepPurple.shade500),
ButtonState.loading:
IconedButton(text: "Loading", color: Colors.deepPurple.shade700),
ButtonState.fail: IconedButton(
text: "Failed",
icon: Icon(Icons.cancel, color: Colors.white),
color: Colors.red.shade300),
ButtonState.success: IconedButton(
text: "Success",
icon: Icon(
Icons.check_circle,
color: Colors.white,
),
color: Colors.green.shade400)
}, onPressed: onPressedIconWithText, state: stateTextWithIcon);
}
I have a function (already written and working fine) that I want to run when the button is clicked, changing the button state to ButtonState.loading then to ButtonState.success then back to ButtonState.idle. See below the function stated on the pub.dev site.
void onPressedIconWithText() {
switch (stateTextWithIcon) {
case ButtonState.idle:
stateTextWithIcon = ButtonState.loading;
Future.delayed(Duration(seconds: 1), () {
setState(() {
stateTextWithIcon = Random.secure().nextBool()
? ButtonState.success
: ButtonState.fail;
});
});
break;
case ButtonState.loading:
break;
case ButtonState.success:
stateTextWithIcon = ButtonState.idle;
break;
case ButtonState.fail:
stateTextWithIcon = ButtonState.idle;
break;
}
setState(() {
stateTextWithIcon = stateTextWithIcon;
});
}
}
However, I am new to coding, and have no idea at all on how to use "breaks" or to change the button state. Could anybody help with advising on how i would insert my funcion (let's say its just void runFunction() in to the above code, changing the state from idle --> loading (onPressed) --> success --. idle.
Any help would be greatly appreciated
You could use setState to update the values for stateTextWithIcon
ButtonState stateTextWithIcon = ButtonState.idle;
Widget buildTextWithIcon() {
return ProgressButton.icon(iconedButtons: {
ButtonState.idle: IconedButton(
text: "Send",
icon: Icon(Icons.send, color: Colors.white),
color: Colors.deepPurple.shade500),
ButtonState.loading:
IconedButton(text: "Loading", color: Colors.deepPurple.shade700),
ButtonState.fail: IconedButton(
text: "Failed",
icon: Icon(Icons.cancel, color: Colors.white),
color: Colors.red.shade300),
ButtonState.success: IconedButton(
text: "Success",
icon: Icon(
Icons.check_circle,
color: Colors.white,
),
color: Colors.green.shade400)
}, onPressed: (){
progressButton()
},
state: stateTextWithIcon,
);
this is the fucntion handled by my onPressed
Future progressButton() async {
setState(() {
//sets the state of stateTextWithIcon to loading once button is pressed
stateTextWithIcon = ButtonState.loading;
});
var url = 'https://google.com';
final response = await http.get(url);
if (response.statusCode == 200 || response.statusCode == 201) {
setState(() {
//sets the state of stateTextWithIcon to success if whatever request made was successful
stateTextWithIcon= ButtonState.success;
});
} else {
setState(() {
//sets the state of stateTextWithIcon to fail if the request was unsuccessful
stateTextWithIcon = ButtonState.fail;
});
}
}

flutter like_button on tap increment the value in the server

I used the like button flutter package from https://pub.dev/packages/like_button
to include like button to my list view from api. I have a table which is contain the count of likes and I show them in every item. now I want to increment the count of likes after tap on like button, but the type of function is bool and I can pass item id as parameter. so please help to solve this issue
LikeButton(
onTap: onLikeButtonTapped,
size: 20,
circleColor:
CircleColor(start: Colors.pink, end: Colors.pinkAccent),
bubblesColor: BubblesColor(
dotPrimaryColor: Colors.red,
dotSecondaryColor: Colors.redAccent,
),
likeBuilder: (bool isLiked) {
return Icon(
Icons.favorite,
color: isLiked ? Colors.pink : Colors.pink[300],
size: 20,
);
},
likeCount: news[index].count_like,
countBuilder: (int count, bool isLiked, String text) {
var color = isLiked ? Colors.grey[700] : Colors.grey[600];
Widget result;
if (count == 0) {
result = Text(
"love",
style: TextStyle(color: color),
);
} else
result = Text(
text,
style: TextStyle(color: color),
);
return result;
},
),
and here is the predefined function which is provided by the package developer
Future<bool> onLikeButtonTapped(bool isLiked) async{
<!-- send your request here -->
<!-- final bool success= await sendRequest(); -->
<!-- if failed, you can do nothing -->
<!-- return success? !isLiked:isLiked; -->
return !isLiked;
}
I am not sure if this could solve your problem, but I also had a similar situation to pass arguments through the function so I modified the function and somehow solved the issue. Hope it helps.
I used ontap like this:
onTap: (isLiked) {
return changedata(
isLiked,
);
},
and then the function,
Future<bool> changedata(status) async {
//your code
return Future.value(!status);
}
}