I would like to get a percent making two functions.
My first function is a filtered list of my data.
int countP(String idC) {
final _ps = ps.where((element) => element.idC == idC);
return _ps.length;}
My second function comes from my first function:
int countPP(String idC) {
final _ps = ps.where((element) => element.idC == idC);
final _psPd = _ps.where((element) => element.pd != '');
return _psPd.length;}
In my view I would like to show the percent:
final percentPd =((pCtrl.countPP(idC) * 100) / pCtrl.countP(idC)).round();
I need to show the result in Text:
Text(percentPd)
My question is:
How can I show the result in Text Widget using Getx, because when I open my view the first time doesn't show the result, but if I refresh, yes?
I used Obx, GetX, and GetBuilder in my Text.
I put my controller using Get.find() but doesn't work.
I Used Get.put(Controller) and doesn't work
You should declare percentPd like this
final percentPd = 0.obs;
percentPd.value = ((pCtrl.countPP(idC) * 100) / pCtrl.countP(idC)).round();
and then assign it's value afterwards and use Obx on the widget where you want to show it.
In your widget use this code :
#override
Widget build(BuildContext context) {
return {
GetBuilder<yourController>(builder: (controller)
{
return Text(controller.percentPd)
}
}
Related
i am initializing a variable with value from session. but could not print it in the widget. but it is showing after hot load. here is my code :
class _dashboardState extends State<dashboard> {
var logindata;
#override
initState() {
super.initState();
_getSession() async {
logindata = jsonDecode(await FlutterSession().get("login_data"));
}
_getSession();
}
#override
Widget build(BuildContext context) {
print(logindata); // prints null
}
}
Instead of jsonDecode(await FlutterSession().get("login_data"))
if i add any random string or number like
logindata = "Session value";
it prints normally. otherwise on hot load
only i am getting the session value.
what will be the reason?
please do help :(. i am new to flutter.
After following ideas from the comments i have updated the code as follows:
class _dashboardState extends State<dashboard> {
var logindata;
#override
void initState() {
getSessionValue().then((logindata) {
setState(() {
logindata = logindata;
});
});
super.initState();
}
Future<void> getSessionValue() async {
logindata = jsonDecode(await FlutterSession().get("login_data"));
return logindata;
}
#override
Widget build(BuildContext context) {
print(logindata); // first prints null then correct array without hotload.
}
}
here i got first null, then the correct value. but in my case i need the value of an object in the array logindata, that is
logindata["shop_name"] . so in that case i am getting error The method '[]' was called on null. Receiver: null Tried calling: []("shop_name") . What do i do now ? i am really stuck here. :(
Let me explain this first,
lifecycle of State goes like this createState -> initState ->........-> build
so you're right about the order of execution
you're calling getSessionValue() from initState and expecting widget to build right after it, but since getSessionValue() returns a Future after awaiting,
the execution continues and builds the widget not waiting for the returned Future value from getSessionValue(), so it prints null initially, and then when the Future is available youre calling setState and it prints the actual value
there is no notable delay here but the execution flow causes it to behave like this
so what's the solution?... Here comes FutureBuilder to the rescue
it is possible to get different states of a Future using FutureBuilder and you can make changes in the UI accordingly
so in your case, inside build, you can add a FutureBuilder like this,
FutureBuilder(
future: getSessionValue(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return Text("none");
case ConnectionState.waiting: return Text("waiting");
case ConnectionState.active: return Text("active");
case ConnectionState.done:
print(logindata); // this will print your result
return Text("${logindata}");
}
})
keep in mind that the builder should always return a widget
as the async operation is running, you can show the progress to the user by
showing the appropriate UI for different states
for eg: when in ConnectionState.waiting, you can show/return a progress bar
Hope this helps, Thank you
That is a normal behaviour since you are having an async function to get the login data (so it will take some time to be there) while the widget will be building , then build method will get executed first which will make the print executed with no data and then when you hot reload it will be executed perfectly , so if you you want to print it right after you get the data you can make the function this way :
_getSession() async {
logindata = jsonDecode(await FlutterSession().get("login_data")).then((value) {print(value);}); }
I have a "WidgetBackGround" statefullwidget that return an animated background for my app,
I use it like this :
Scaffold( resizeToAvoidBottomInset: false, body: WidgetBackGround( child: Container(),),)
The problem is when I use navigator to change screen and reuse WidgetBackGround an other instance is created and the animation is not a the same state that previous screen.
I want to have the same animated background on all my app, is it possible to instance it one time and then just reuse it ?
WidgetBackGround.dart look like this:
final Widget child;
WidgetBackGround({this.child = const SizedBox.expand()});
#override
_WidgetBackGroundState createState() => _WidgetBackGroundState();
}
class _WidgetBackGroundState extends State<WidgetBackGround> {
double iter = 0.0;
#override
void initState() {
Future.delayed(Duration(seconds: 1)).then((value) async {
for (int i = 0; i < 2000000; i++) {
setState(() {
iter = iter + 0.000001;
});
await Future.delayed(Duration(milliseconds: 50));
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return CustomPaint(painter: SpaceBackGround(iter), child: widget.child);
}
}
this is not a solution, but maybe a valid workaround:
try making the iter a static variable,
this of course won't preserve the state of WidgetBackGround but will let the animation continue from its last value in the previous screen
A valid solution (not sure if it's the best out there):
is to use some dependency injection tool (for example get_it) and provide your WidgetBackGround object as a singleton for every scaffold in your app
I want to rebuild the buildSuggestions method that returns a widget that the defined below
and resultInfo getting the result from TMDB API search that a string as an input
how can i recall the resultInfo and rebuild it.
#override
Widget buildSuggestions(BuildContext context) {
if (query.length > 1) {
return ResultInfo(s: query);
}
return ResultInfo(s: "old");
//ResultInfo not updating after the query updates
}
this is the home screen code where searchdelegate is defined and here you can find the code for ResultInfo widget.
UPDATE: After looking at the project, you need to call the getMovies function from the buildSuggestions build method because initState was only getting called once despite the rebuilds. Add this and you're good.
#override
Widget buildSuggestions(BuildContext context) {
if (query != '') {
searchResult.getMovies(query); // do the search from here
return ResultInfo(searchString: query);
}
return ResultInfo(searchString: "old");
}
}
You can leave the search in initState also so you don't get a blank list when you navigate to the search page.
OLD ANSWER:
Any time the query changes in a widget that extends SearchDelegate, buildSuggestions is called. You can verify that by adding a print statement in the buildSuggestions build method.
I see a couple issues here:
class ResultInfo extends StatefulWidget {
final String s;
ResultInfo({Key key, #required this.s}) : super(key: key);
#override
_MovieInfoState createState() => _MovieInfoState(s);
}
class _MovieInfoState extends State<ResultInfo> {
// final String s; don't need this here
_MovieInfoState(this.s);
#override
void initState() {
super.initState();
searchResult.getMovies(widget.s); // access the actual parameter you passed in with widget.s
}
When you pass a value into a stateful widget you don't re-declare the same value again in the stateful part, you access what was passed in with widget.variable. So in your case you were passing in a null value into the getMovies function.
So assuming your stream functionality is all working, that simple change alone should fix your issue.
And in the interest of your code being readable by other people, I suggest a better variable name than s, like query for example because that's what it is. Anyone reading your code has no idea what s is and what it's for until they dig around a bit.
Second issue:
if (query.length > 1) { // won't return results until 2nd character is input
return ResultInfo(s: query);
}
return ResultInfo(s: "old");
}
Maybe its your intention to only return ResultInfo(s: query) until more than one character is input, but if its not, change your condition to this
if (query != '') {
return ResultInfo(s: query);
}
return ResultInfo(s: "old");
}
I'm trying to return a FutureBuilder from a SearchDelegate but result shown are not correct.
query seems to be correct and from network I can see all http calls done correctly, so the problem is related to the list data update itself.
In buildSuggestions (of SearchDelegate) a StatefulWidget called 'ResultList' is returned. This widget has for state:
previousQuery - last search term before rerendering
list - list of data returned from a Future
from - first element to show
pageSize - number of elements returned
I need those variables in order to implement infinite scroll so in ResultList build method first of all I check if widget.query has changed from last rendering
if (previousQuery != widget.query) {
setState(() {
from = 0;
list.clear();
previousQuery = widget.query;
});
}
I'm using a ScrollController, so in initState of ResultList when user reach the bottom of the screen i just update "from":
setState(() {
from += pageSize;
});
In FutureBuilder builder method, if snapshot has new data, I append it to list. I should update list in setState but I can't do this inside a builder.
builder: (context, snapshot) {
List<int> ids = [];
List<int> newids = [];
if (snapshot.hasData) {
ids = list.map((item) => item.id as int).toSet().toList();
newids = (snapshot.data.results as List)
.map((item) => item.id as int)
.toSet()
.toList()
.where((id) => !ids.contains(id))
.toList();
if (newids.length != 0) {
setState(() {//can't do this here
list = [
...list,
...(snapshot.data.results as List)
.where((element) => newids.contains(element.id as int))
];
});
}
}
Any hint? Thanks in advance.
when we Use Future Builder Inside of StateFull Widget we Should know On Every SetState
Future Builder call Future Function. and rebuild it self,
so your problem is going to solve if you Remove Future Builder ,
so please change your code to some thing like below...
#override
Widget build(BuildContext context) {
return newids.Empty && ids.Empty ? CircularProgressIndicator() : MyWidget();
}
and Call Your Future in Init State (and When You Want get new Items(next page))
I am trying to show the price of items in the cart but the total value should be shown in TextField. I am saving data to SQLite and retrieving then show to a widget, but when I try to access total_price to another widget it's not updating, but When I press hot reload again the data shows but not first time when I am opening the page
return FutureBuilder<List<CartModel>>(
future: fetchCartFromDatabase(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data.length > 0) {
cartCount = snapshot.data.length;
for(int i = 0;i<snapshot.data.length;i++){
var price = snapshot.data[i].product_price.split("₹");
total_price+=double.parse(price[1]);
}
} else if (snapshot.hasData && snapshot.data.length == 0) {
return new Text("No Data found");
}
else
{
return new Container(
alignment: AlignmentDirectional.center,
child: new CircularProgressIndicator(),
);
}
);
value initialized
int cartCount = 0;
double total_price=0.0;
The FutureBuilder updates only its children. To update the value of another widget you must use setState.
The best way would be putting FutureBuilder in an upper level or using some sort of state manager, like provider.
To use setState you need to initialize you fetch from an initState of a stetefullWidget (or to call it from a function). This way you will not need a FutureBuilder and must refactor your code:
class YourWidget extends StatefulWidget {
#override
_YourWidgetState createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
double total_price = 0;
#override
void initState() {
super.initState();
fetchCartFromDatabase().then((value){
setState((){
for(int i = 0;i<value.length;i++){
var price = value[i].product_price.split("₹");
total_price+=double.parse(price[1]);
}
});
});
}
}
The addPostFrameCallback is not a good solution, since it updates the value only in the next frame. When the app grows it leads to lags.
To continue using the FutureBuilder, move your widget tree that needs to be updated to be inside of the FutureBuilder.