Flutter Xlider - setState - value remains 0 and snaps back - flutter

This might be just a simple problem but I can't figure it out somehow right now. I am using the Flutter xslider https://pub.dev/packages/flutter_xlider vertically with values from 0 to 90. When I am dragging the slider it repaints a line depending on the value of the slider and simultaneously it changes the numeric value in a TextField. This once worked but after adding the TextField and updating all the components (Flutter. Dart. Packages. AndroidStudio) I have the following problem:
because of the line setState(() { }); in onDragging the value of the slider always remains 0 and snaps back after the dragging is finished. if I leave out the setState method the slider works however my line gets of course not painted. what am I doing wrong? does it has something to do with the surrounding components while building? or is there another reason?
UPDATE: i found the solution. I now update the values property by myself such as values: [sliderValue] whereas sliderValue is a double initialized once and then updated within the onDragging. its strange that this is never suggested inside the documentation
body: Column(
children: [
new Padding(
padding: new EdgeInsets.all(15.0),
),
angleTextQuestion,
new Padding(
child: Column(children:[ TextField( controller: txtAngleC,
keyboardType: TextInputType.number,
onChanged: (text) {
double angle = double.parse(text);
if(angle >= 0 && angle <= 90) {
_angleAirplane = double.parse(text);
setState(() {});
}
},),
Text("(type in a value between 0 - 90)")
]),
padding: new EdgeInsets.all(15.0),
),
Expanded( child: new Row( children: [
new Padding(
padding: new EdgeInsets.all(20.0),
),
Expanded( child: new SizedBox(
height: 250,
width: 50,
child: FlutterSlider(
values: [0],
rangeSlider: false,
max: 90,
min: 0,
rtl: true,
step: FlutterSliderStep(step: 1.0),
axis: Axis.vertical,
tooltip: FlutterSliderTooltip(
textStyle: TextStyle(fontSize: 14, color: Colors.lightBlue),
),
onDragging: (handlerIndex, lowerValue, upperValue) {
_angleAirplane = lowerValue;
txtAngleC.text = _angleAirplane.toString();
setState(() { });
},
))),
Text(_angleAirplane.toString()),
new Padding(
padding: new EdgeInsets.all(10.0),
),
Expanded( child: new CustomPaint(
foregroundPainter: new AnglePainter(
angle: _angleAirplane
)))
]
)),
]
),
```

change values:[0] to this
values: [_angleAirplane]
this happens because there is a setState inside your onDragging method and redraw the slider with 0 value.

Related

Is there any way to List the list views size in flutter?

I am new to flutter and trying to develop a version updation software. I need to show the version in a radio list. Is there any way to contain the list view in a container? Currently the list view is overflowing through the other widgets. I need to contain the list view between the text and the button. Is there any way to do it.The code the screenshot is given below
[![screenshot][1]][1]
Widget _createVersionRadioTiles(List<String> versions) {
var radioTiles = <Widget>[];
for (var version in versionList) {
var tile = Padding(
padding: const EdgeInsets.all(8.0),
child: RadioListTile<String>(
selected: selectedVersion == version,
tileColor: colorDarkGray,
// selectedTileColor: Colors.white,
value: version,
groupValue: selectedVersion,
onChanged: (String? value) {
setState(() {
selectedVersion = value!.toString();
});
},
title: Text(version),
),
);
radioTiles.add(tile);
}
return Column(
children: [
SizedBox(
height: 500,
width: 500,
child: ListView(children: radioTiles),
)
],
);
} ```
[1]: https://i.stack.imgur.com/qQxns.jpg
Wrap the RadioListTile with a Card widget. It will contain the ListTile in the container.
You can replace SizedBox with Container, and use clipBehavior: Clip.hardEdge to remove the overflows. see code below:
return Column(
children: [
Container(
height: 500,
width: 500,
clipBehavior: Clip.hardEdge,
child: ListView(children: radioTiles),
)
],
);

Fill the available space in Listtile

I am new to the flutter and trying to fill the empty space in the listtile. I tried to use dense and visualDensity but with that, I am not getting the required result. Any support and suggestions will be appreciated.
here is my code and output:
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(
height: isLargeScreen ? 300 : 200,
child: ListView.builder(
physics: const ScrollPhysics(),
shrinkWrap: true,
itemCount: tags.length,
itemBuilder: (context, index) {
return CheckboxListTile(
value: tempSelectedTags.contains(tags[index].id),
onChanged: (e) {
setState(() {
if (tempSelectedTags.contains(tags[index].id)) {
tempSelectedTags.remove(tags[index].id);
} else {
tempSelectedTags.add(tags[index].id);
}
});
},
title: Text(
tags[index].name,
style: !tempSelectedTags.contains(tags[index].id)
? theme.textTheme.labelMedium?.copyWith(
color: ThemeConfig.colorTertiary)
: theme.textTheme.titleSmall?.copyWith(
color: ThemeConfig.colorTertiary),
),
);
}),
),
const Spacer(),
Padding(
padding: const EdgeInsets.only(bottom:sdPaddingMedium),
child: SdPrimaryButton(
title: appLocalizations.btnApply,
onPressed: () {
viewModel.setTags(tempSelectedTags);
Navigator.pop(context);
},
),
),
],
)
Output can be seen here
There are two important things that determine the vertical layout in your column.
The whole box has a fixed size
SizedBox(
height: isLargeScreen ? 300 : 200,
There is a flexible space between the checkbox options and the bottom-right button
const Spacer(),
So if you want to remove the space, you can either
reduce the overall box size or
replace the const Spacer with a constant spacing like
SizedBox(height: 50) and also remove the SizedBox, so that the whole box will be content-sized

How can I set initial position for slidable_button

I'd like to use slidable_button found here to set a bar happy hour on or off in my app. It doesn't have an initial position so I need to use two buttons, one to set happy hour on and another to turn it off. Ideally I'd like to have it initialize in either the left position or the right position depending on the current state. Unfortunately the package is very new and the author is not contactable.
Is there a way I could modify this code to set an initial button position left or right?
Here is the code I have so far. I have two buttons in separate rows in a column but have only shown the code for one as they are similar.
Center(
child: SlidableButton(
width: MediaQuery.of(context).size.width / 3,
buttonWidth: 60.0,
color: Colors.green,
buttonColor: Colors.blue,
dismissible: true,
label: Center(child: Text(_user.turnOnString!)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(''),
Text('Start'),
],
),
),
onChanged: (position) {
if (position == SlidableButtonPosition.right) {_startHappyHour(context, _user);}
},
),
),
switch button would be more suitable for your situation, like lite_rolling_switch, you can set initial value(on/off):
LiteRollingSwitch(
//initial value
value: true,
textOn: 'disponible',
textOff: 'ocupado',
colorOn: Colors.greenAccent[700],
colorOff: Colors.redAccent[700],
iconOn: Icons.done,
iconOff: Icons.remove_circle_outline,
textSize: 16.0,
onChanged: (bool state) {
//Use it to manage the different states
print('Current State of SWITCH IS: $state');
},
),

Select number of items in list using gesture in flutter

I am new to flutter and have to implement a functionality to select correct words from list of alphabets.First of all here is the image which i have to make.
The alphabets and the correct words will come from the server side.What is was thinking is that i will place all the Alphabets in grid view/list view and when user will select words by using gesture on the alphabets . I know that what i am thinking may be wrong.If thats the case then please tell me the correct way to achieve what is given in the image ? Thanks in advance.
After doing some more research I think I can point you in the right direction and I wrote a small demonstration app that should help you so I found a good article explaining why I was having issues nesting widgets that both receive touch input. It is slightly complex but the gist of it is. If multiple touch inputs are detected they are handled in what is called the GestureArena and the child widget always wins. You can define your own GestureFactory and use RawGestureDetector to work around this issue. However, this might be more than you need for your application and in my opinion is the more complicated route. The route I went still involves just GestureDetector obviously you would need to expand on this as it only draws one oval at this time but that should be easy.
Offset position = Offset(0, 0);
bool isTapped = false;
double width = 50;
double height = 50;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: GestureDetector(
child: GridView(
physics: NeverScrollableScrollPhysics(), //Very Important if
// you don't have this line you will have conflicting touch inputs and with
// gridview being the child will win
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
childAspectRatio: 2,
),
children: <Widget>[
...letters
.map(
(letter) => Text(
letter,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.amber,
fontSize: 18,
),
),
)
.toList(),
],
),
onTapDown: (TapDownDetails details) {
//User Taps Screen
print('Global Position: ${details.globalPosition}');
setState(() {
position = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
isTapped = true;
});
print(position);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
print('${details.delta.dy}');
setState(() {
height += details.delta.dy;
});
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
print('${details.delta.dx}');
setState(() {
width += details.delta.dx;
});
},
onTapCancel: () {
//User has released finger from screen
//Validate Word??
},
),
),
),
Positioned(
top: position.dy,
left: position.dx,
child: Container(
height: height,
width: width,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(
color: isTapped ? Colors.blue : Colors.transparent,
width: 3.0),
),
),
),
),
],
),
);
}
Flutter Deep Dive: Gestures found this very helpful key to figuring this all out.

Maintaining state in Listview.Builder() of Stateful widget (a progress bar) problem

I am creating a listview using listview.builder. Each view is a card with a button on it and a progress bar (See screenshot)
Each card is a stateful widget. When you press the button, the progressbar starts a 5 second timer. When the timer is over, the object is removed from the LinkedHashMap which makes is dissapear from the screen too. This works, if only one button is pressed in the displayed list. If one button is pressed and after 2 seconds second or/and third button(s) are pressed, the animation of progress bar can be seen on all three, but as soon as a first one dissapears from the screen and the second and subsequest are re-drawn, they loose there state and they appear as if their buttons were never pressed.
What I am trying to do is, keep their states independent and keep their animations independent. When one card dissapears, the others gets shifted up/down accordingly but the progress animation to continue as it is as currently they seem to redraw themselve in unpressed/original state.
My code for my card is as follows:
class _DeliveryCardsViewState extends State<DeliveryCardsView> {
double percentage = 1.0;
bool countdownStarted = false;
String buttonText = "CLAIM ORDER";
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: Container(
padding: EdgeInsets.only(top: 10.0, bottom: 10.0, left: 10.0, right: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text('#'+widget.delivery.uID),
GestureDetector(
onTap: () async {
if (!countdownStarted) {
countdownStarted = true;
setState(() {
buttonText = 'UNDO';
});
int j = 100;
while (j > 0) {
await Future.delayed(Duration(milliseconds: 50)); // Duration in seconds (50/10) = 5 seconds
j -= 1;
setState(() {
if (countdownStarted){
percentage = (j / 100).toDouble();
} else {
percentage = 1.0;
}
});
if (!countdownStarted){
setState(() {
buttonText = 'CLAIM ORDER';
});
break;
}
}
//Try to claim it now
if (countdownStarted) tryClaimOnFirebase();
} else {
countdownStarted = false;
}
},
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
Text(buttonText,style: TextStyle(color: Colors.white),),
SizedBox(height: 5.0,),
LinearPercentIndicator(
width: 100.0,
lineHeight: 7.0,
percent: percentage,
backgroundColor: Colors.green,
progressColor: Colors.red,
),
],
),
decoration: BoxDecoration(
color: mainGrey,
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
)
),
],
),
],
),
),
);
}
And my Listview.builder code is as follows:
return Container(color: Colors.grey[350],
child: ListView.builder(
key: UniqueKey(),
itemCount: deliveriesAvailable.length,
controller: _controller,
scrollDirection: Axis.vertical,
reverse: false,
shrinkWrap: true,
itemBuilder: (context,position){
return GestureDetector(
child: DeliveryCardsView(delivery: deliveriesAvailable.values.elementAt(position),),
onTap: (){},
);
}),
);
Error recording:
Working now.
Alright so, I think I underdtand what was happening. The entire stateful widget was getting disposed and rebuilt and as a result the 'logic' 'inside' the stateful widget was also resetting. What I did was the following:
1) Added a couple of extra variables in the 'Object'
2) When the view gets built again, it checks those variables and then 'resumes' the state it was last disposed in, and that included the the value of the progress bar counter.
Obvously for this to happen, the entire logic was taken out from onTap() and added to a new async method.
Simplessss :)