Flutter Slider Not Moving or Updating - flutter

I'm learning Flutter (and coding in general) and I can't seem to find the issue with my latest project. When I run in simulator the slider forms just fine, click the thumb and the label shows, but the thumb won't move on the track at all, and thus never calls the onChanged event.
import 'resources.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class ItemDetail extends StatefulWidget {
final Item item;
ItemDetail({Key key, #required this.item}) : super(key: key);
#override
_ItemDetailState createState() => new _ItemDetailState(item: item);
}
class _ItemDetailState extends State<ItemDetail> {
Item item;
_ItemDetailState({Key key, #required this.item});
#override
Widget build(BuildContext context) {
double margin = ((item.listPrice - item.stdUnitCost)/item.listPrice)*100;
return new Scaffold(
appBar: AppBar(
title: new Text('Item Detail'),
),
body: new Column(
children: <Widget>[
new Padding(padding: EdgeInsets.symmetric(vertical: 20.0)),
new Text(item.itemCode),
new Text(item.itemDescription),
new Text(item.itemExtendedDescription),
new Divider(height: 40.0,),
new Text('List Price: \$${item.listPrice}'),
new Text('Cost: \$${item.stdUnitCost}'),
item.itemType=='N'
? new Text('Non-Stock (${item.itemType})')
: new Text('Stock Item (${item.itemType})'),
new Text('Available: ${item.stockAvailable}'),
new Padding(padding: EdgeInsets.symmetric(vertical: 10.0)),
new Slider(
value: margin,
divisions: 20,
min: 0.0,
max: 100.0,
label: margin.round().toString(),
onChanged: (double value) {
setState(() {
margin = value;
});
},
)
],
),
);
}
}

Problem: In the above example, margin is not a state variable. It is a local variable inside a build method.
Fix: Move this as an instance variable.
Reason: Widgets will get rebuild only when there is a change in its state.
Code:
class _ItemDetailState extends State<ItemDetail> {
Item item;
var margin;
_ItemDetailState({Key key, #required this.item}) {
this.margin = ((item.listPrice - item.stdUnitCost)/item.listPrice)*100;
}
#override
Widget build(BuildContext context) {
//same as now
}
}

For others coming here, I had a slightly different reason that the slider wasn't updating. I had the value set to a constant. Make sure that value is a variable.
This:
Slider(
value: _myValue,
...
onChanged: (newValue) => {
setState(() => _myValue = newValue)
},
),
Not this:
Slider(
value: 0, // <-- problem
...
onChanged: (newValue) => {
setState(() => _myValue = newValue)
},
),

I got this issue when I put the variable inside the build function đŸ˜©
#override
Widget build(BuildContext context) {
int brightness = 85;
return Scaffold(
...
Moved the variable outside the function and got it solved 😎
int brightness = 85;
#override
Widget build(BuildContext context) {
return Scaffold(
...
because each time when state is changed, this build method is called and sets the variable back to the assigned value 😝

This might help! (I USED STATEFULBUILDER TO UPDATE THE VALUE)
double _value = 20;
StatefulBuilder(
builder: (context, state) => Center(
child: CupertinoSlider(
value: _value,
min: 0.0,
max: 100.0,
onChanged: (val) {
state(() {
_value = val;
});
},
),
),
)

I think the variable margin does not have scope from where the UI is building. When you debug, you can see changes in variable but it is not rendering. I tried it in following way and able to update the UI value.
class _MyHomePageState extends State<MyHomePage> {
double margin=0;
Widget getSlider(BuildContext context)
{
return Slider(
value:margin.toDouble(),
onChanged: (newRating){
setState(() {
margin = newRating;
});
},
min: 0,
max: 100,
divisions: 5,
);
} // getslider method closed
// #0verride Widget build method
// under children widgets , simply call the getSlider method to place the slider
} //class

I was having an issue when giving the Slider(value: tasksDetails.taskImportance.toDouble(), etc)
I used Slider((_currentImportance ?? tasksDetails.taskImportance).toDouble(), etc)
and could then move the slider. I have no idea why this now works. Can only guess something to do with Denish's reason above 'Reason: Widgets will get rebuild only when there is a change in its state'. The selected slider value was in both cases was save correctly.

My first problem was answered by Dinesh. Then the slider wasn't smooth. I was only able to tap to get it to move. I am on iOS, so I changed the Slider widget to Slider.adaptive.
This changes the slider to a CupertinoSlider.
Copy and paste class for your testing convenience:
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
double _currentSliderValue = 20;
#override
Widget build(BuildContext context) {
return Slider.adaptive(
value: _currentSliderValue,
max: 100,
divisions: 5,
label: _currentSliderValue.round().toString(),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
);
}
}

Related

Flutter: CheckboxListTile inside ListView jumps to top

So I know there are some similar questions about this issue but none of them worked for me. I have a ListView with different CheckboxListTiles and when I scroll down and choose an item, the ListView automatically jumps to the top. Is there a way to prevent this from happening? Thank you very much!
I've added a screenshot, so you can better understand.
This is my code:
class CheckboxWidget extends StatefulWidget {
const CheckboxWidget({
Key key,
this.item,
this.type,
this.state,
}) : super(key: key);
final Map<String, bool> item;
final String type;
final Map<String, dynamic> state;
#override
State<CheckboxWidget> createState() => _CheckboxWidgetState();
}
class _CheckboxWidgetState extends State<CheckboxWidget> {
#override
void initState() {
super.initState();
if (widget.state[widget.type].isEmpty) {
widget.item.updateAll((key, value) => value = false);
}
}
bool isChecked = false;
#override
Widget build(BuildContext context) {
final FilterProvider filterProvider = Provider.of<FilterProvider>(context);
return Expanded(
child: ListView(
key: UniqueKey(),
children: widget.item.keys.map(
(key) {
return CheckboxListTile(
contentPadding: EdgeInsets.only(left: 2, right: 2),
title: Text(
key,
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w400),
),
value: widget.item[key],
activeColor: Color(0xffF6BE03),
checkColor: Color(0xff232323),
shape: CircleBorder(),
//contentPadding: EdgeInsets.all(0),
onChanged: (value) {
value
? filterProvider.multifiltervalue = [widget.type, key]
: filterProvider.multifiltervalue = [
widget.type,
key,
false
];
setState(
() {
widget.item[key] = value;
},
);
},
);
},
).toList(),
),
);
}
}
Probably because this line key: UniqueKey(), when you call setState the build function builds its widgets again, and the ListView will have a new UniqueKey so it will rebuild the list cause it thinks its a different widget
remove this line key: UniqueKey(), and it should work fine
CheckboxListTile is a stateless Widget... setState is redrawing the whole list.
You could wrap the CheckboxListTile into a Statefull Widget or into a StatefulBuilder ... if you call setState inside the StatefulBuilder only this part should be redrawed..
another way could be to save the scroll position... but i think redrawing only the part on screen you haved changed is smarter :-)

How can I select Widgets by dragging over them but also clicking them individually in flutter?

I want to create an Interface in which it is possible to drag your finger over several Areas. This changes the state of the areas to a selected state (See images).
What is the best way to approach this?
Start Position:
Start Dragging:
Select First Area:
Selected All Areas:
The code needs some updates for current Flutter/Dart versions but this worked for me.
Updated code:
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: Grid(),
);
}
}
class Grid extends StatefulWidget {
#override
GridState createState() {
return new GridState();
}
}
class GridState extends State<Grid> {
final Set<int> selectedIndexes = Set<int>();
final key = GlobalKey();
final Set<_Foo> _trackTaped = Set<_Foo>();
_detectTapedItem(PointerEvent event) {
final RenderBox box = key.currentContext!.findAncestorRenderObjectOfType<RenderBox>()!;
final result = BoxHitTestResult();
Offset local = box.globalToLocal(event.position);
if (box.hitTest(result, position: local)) {
for (final hit in result.path) {
/// temporary variable so that the [is] allows access of [index]
final target = hit.target;
if (target is _Foo && !_trackTaped.contains(target)) {
_trackTaped.add(target);
_selectIndex(target.index);
}
}
}
}
_selectIndex(int index) {
setState(() {
selectedIndexes.add(index);
});
}
#override
Widget build(BuildContext context) {
return Listener(
onPointerDown: _detectTapedItem,
onPointerMove: _detectTapedItem,
onPointerUp: _clearSelection,
child: GridView.builder(
key: key,
itemCount: 6,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemBuilder: (context, index) {
return Foo(
index: index,
child: Container(
color: selectedIndexes.contains(index) ? Colors.red : Colors.blue,
),
);
},
),
);
}
void _clearSelection(PointerUpEvent event) {
_trackTaped.clear();
setState(() {
selectedIndexes.clear();
});
}
}
class Foo extends SingleChildRenderObjectWidget {
final int index;
Foo({required Widget child, required this.index, Key? key}) : super(child: child, key: key);
#override
_Foo createRenderObject(BuildContext context) {
return _Foo(index);
}
#override
void updateRenderObject(BuildContext context, _Foo renderObject) {
renderObject..index = index;
}
}
class _Foo extends RenderProxyBox {
int index;
_Foo(this.index);
}
I use Rect class.
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class StackOverflow extends StatefulWidget {
const StackOverflow({Key? key}) : super(key: key);
#override
_StackOverflowState createState() => _StackOverflowState();
}
class _StackOverflowState extends State<StackOverflow> {
late List<bool> isSelected;
late List<GlobalKey> myGlobalKey;
late List<Offset> offsetWidgets;
late List<Size> sizeWidgets;
late List<Rect> listRect;
#override
void initState() {
super.initState();
isSelected = List.generate(3, (index) => false);
myGlobalKey = List.generate(3, (index) => GlobalKey());
offsetWidgets = <Offset>[];
sizeWidgets = <Size>[];
listRect = <Rect>[];
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
for (final key in myGlobalKey) {
sizeWidgets
.add((key.currentContext!.findRenderObject() as RenderBox).size);
offsetWidgets.add((key.currentContext!.findRenderObject() as RenderBox)
.localToGlobal(Offset.zero));
}
for (int i = 0; i < 3; i++) {
final dx = offsetWidgets[i].dx + sizeWidgets[i].width;
final dy = offsetWidgets[i].dy + sizeWidgets[i].height;
listRect.add(Rect.fromPoints(offsetWidgets[i], Offset(dx, dy)));
}
});
}
#override
Widget build(BuildContext context) {
return Listener(
onPointerMove: (PointerMoveEvent pointerMoveEvent) {
if (listRect[0].contains(pointerMoveEvent.position)) {
if (!isSelected[0]) {
setState(() {
isSelected[0] = true;
});
}
} else if (listRect[1].contains(pointerMoveEvent.position)) {
if (!isSelected[1]) {
setState(() {
isSelected[1] = true;
});
}
} else if (listRect[2].contains(pointerMoveEvent.position)) {
if (!isSelected[2]) {
setState(() {
isSelected[2] = true;
});
}
}
},
child: Container(
color: Colors.amber,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RawMaterialButton(
key: myGlobalKey[0],
fillColor: isSelected[0] ? Colors.blueGrey : Colors.transparent,
shape:
const CircleBorder(side: BorderSide(color: Colors.blueGrey)),
onPressed: () {
setState(() {
isSelected[0] = false;
});
},
),
RawMaterialButton(
key: myGlobalKey[1],
fillColor: isSelected[1] ? Colors.blueGrey : Colors.transparent,
shape:
const CircleBorder(side: BorderSide(color: Colors.blueGrey)),
onPressed: () {
setState(() {
isSelected[1] = false;
});
},
),
RawMaterialButton(
key: myGlobalKey[2],
fillColor: isSelected[2] ? Colors.blueGrey : Colors.transparent,
shape:
const CircleBorder(side: BorderSide(color: Colors.blueGrey)),
onPressed: () {
setState(() {
isSelected[2] = false;
});
},
),
],
),
),
);
}
}
This package: drag_select_grid_view offers another related approach. From the code, you can see among other fun things:
Use of a GestureDetector to encapsulate the selection area (a GridView here)
In the GridView.itemBuilder, a custom ProxyWidget (his Selectable) wraps the normal widget builder for your selectable items. This is used to expose mount/unmount points in order to hang onto the corresponding custom ProxyElement.
When a tap/motion is detected, he uses the current context to get a RenderObject covering the selection area to do manual hit testing with the local position Offset, by checking if any of the cached Elements contain the point, using each item's bounding box in the selection area's coordinate system. (see _findIndexOfSelectable and Selectable.containsOffset) (This is like #mario's answer, and presumably could be costlier than #a.shak's if there are many possible Elements to choose from on
screen.)
Results are passed back to the user through a ValueNotifier, which also lets the user control clearing or setting a custom the selection. (see the controller code)
For contrast, I'll try to describe #a.shak's answer in words:
In his GridState class wrap a Listener around the subtree representing your selection area. (although a GestureDetector could work, too)
in onPointerDown|Move, start detecting; onPointerUp you can clear/etc.
Detection requires getting the subtree’s RenderBox (a RenderObject) so you can do hitTesting with the pointer’s local position to find other intersecting ROs. Given the selection area's RB, convert the pointer to its local coordinates and do the RenderBox.hitTest, then walk along the BoxHitTestResult.path of intersecting objects to check whether any HitTestEntry is of a type we know can be selected. (i.e. the _Foo extends RenderProxyBox class - see below)
If it's a match, success! Track its info for UI updates and later use elsewhere.
Use a GlobalKey with the GridView to get the RenderBox corresponding to the selection area’s extents during hit testing. (Probably don’t need this as you can use the State’s own context
)
In The GridView.itemBuilder, wrap your selectable objects in a custom SingleChildRenderObjectWidget, used to get the item's RenderBox for hit testing and storing info.
Store info here like your item's index and push it down into a custom RenderBox that our SCROW creates.
Uses a RenderProxyBox since we don’t actually care about controlling the rendering; just delegate it all to the child. This custom class also lets us find our selectable object(s) of interest more easily during the hit test (see _detectTapedItem).
So in both cases, you need to implement some extra custom classes (ProxyWidget+ProxyElement vs SingleChildRenderObjectWidget+RenderProxyBox) in order to get the right RenderBoxes for hit testing with the selected point on the screen, and to store misc info like an item's index to update the UI and use later.
For custom shapes, you can have your CustomPainter override its hitTest method to leverage Path.contains() to restrict touches to be within the path only. See this answer. Or just use a a package like touchable to give your shapes gesture callbacks.

How to change container when radio button clicked

I have this layout :
I want when click on Select Time to change this blue container to another container
this is the code of radio buttons :
class TimeRadioButtonsClass extends StatefulWidget {
#override
_TimeRadioButtonsClassState createState() => _TimeRadioButtonsClassState();
List<String> labels;
String picked;
Function function;
TimeRadioButtonsClass({this.picked , this.labels , this.function});
}
class _TimeRadioButtonsClassState extends State<TimeRadioButtonsClass> {
#override
Widget build(BuildContext context) {
return RadioButtonGroup(
orientation: GroupedButtonsOrientation.VERTICAL,
margin: const EdgeInsets.only(left: 12.0),
onSelected: (String selected) => setState((){
widget.picked = selected;
}),
labels: widget.labels,
picked: widget.picked,
activeColor: Color(0xffFFD243),
onChange: (String label, int index) {
print("label: $label index: $index");
widget.function(label,index);
},
);
}
}
and here I call this class in CheckoutClass :
TimeRadioButtonsClass(picked: picked , labels: when),
NOTE : I used setState in CheckoutClass by access it as parameter in TimeRadioButtonsClass it changes container for just one time.
I don't know the reason why setState doesn't work !
Edit : I pass function as parameter to TimeRadioButtonsClass (I edit TimeRadioButtonsClass code above) and this is the code for calling:
TimeRadioButtonsClass(picked: picked , labels: when , function: (String label , int index){
setState(() {
if(index == 1){
indexRadio = 1;
print("indexRadio 1 : "+indexRadio.toString());
}
else{
indexRadio = 0;
print("indexRadio 2 : "+indexRadio.toString());
}
});
},),
indexRadio == 1 ? Container(
height: 50.0,
color: Colors.blue,
):
Container(
height: 50.0,
color: Colors.red,
),
If your container is outside the class, then you can make a callback on the selected button of the radiobutton.
widget.onTap(selected);
Add this line of code for the onTap method, to make a callback to the class from where it is called.
class TimeRadioButtonsClass extends StatefulWidget {
#override
_TimeRadioButtonsClassState createState() => _TimeRadioButtonsClassState();
List<String> labels;
String picked;
Function onTap;
TimeRadioButtonsClass({this.picked, this.labels, this.onTap});
}
class _TimeRadioButtonsClassState extends State<TimeRadioButtonsClass> {
#override
Widget build(BuildContext context) {
return RadioButtonGroup(
// orientation: GroupedButtonsOrientation.VERTICAL,
margin: const EdgeInsets.only(left: 12.0),
onSelected: (String selected) {
setState(() {
widget.picked = selected;
});
widget.onTap(selected); //This will make a callback to your class and send the selected value in the onTap method of that class.
},
labels: widget.labels,
picked: widget.picked,
activeColor: Color(0xffFFD243),
onChange: (String label, int index) =>
print("label: $label index: $index"),
);
}
}
Now to get the onTap value from the class, Call your widget as below
NOTE: Here value of 1 and 2 is your radio button picked string 1 is the ASAP and 2 is the Select timer
TimeRadioButtonsClass(
picked: "picked",
onTap: (value) {
if (value == 1) {
//do change for one
containerColor = Colors.blue;
setState(() {
});
} else if (value == 2) {
//do change for two
containerColor = Colors.red;
setState(() {
});
}
},
)
Define a Color for container as Default so to change the color on the onTap method of the class
Color containerColor = Colors.red;
Now Show The container As Follows
Container(
height: 100,
width: 100,
color: containerColor,
);
EDITED 2:- (This code changes the color for container and Radio button also changes now everytime)
String picked = "ASAP"; // Add this line at the top
Replace the Code of Calling TimeRadioButtonsClass Class with this
TimeRadioButtonsClass(
picked: picked,
labels: [
"ASAP",
"Select Timer",
],
onTap: (
int index,
) {
indexRadio = index;
if (index == 0) {
picked = "ASAP";
} else {
picked = "Select Timer";
}
setState(() {});
},
),
indexRadio == 0
? Container(
height: 50.0,
color: Colors.blue,
)
: Container(
height: 50.0,
color: Colors.red,
),
And Also Replace the TimeRadioButtonClass Itself with this code
import 'package:flutter/material.dart';
import 'package:grouped_buttons/grouped_buttons.dart';
class TimeRadioButtonsClass extends StatefulWidget {
#override
_TimeRadioButtonsClassState createState() => _TimeRadioButtonsClassState();
List<String> labels;
String picked;
Function(int) onTap;
TimeRadioButtonsClass({this.picked, this.labels, this.onTap});
}
class _TimeRadioButtonsClassState extends State<TimeRadioButtonsClass> {
#override
Widget build(BuildContext context) {
return RadioButtonGroup(
orientation: GroupedButtonsOrientation.VERTICAL,
margin: const EdgeInsets.only(left: 12.0),
labels: widget.labels,
picked: widget.picked,
activeColor: Colors.red,
onChange: (String label, int index) {
print(index);
widget.onTap(index);
});
}
}
What I have done is that Removed the onSelected (maybe it may come use later), and Made the onTap function to pass the int value
setState() only works within a particular State class.
Whenever you change the internal state of a State object, make the change in a function that you pass to setState
Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
setState() docs
Here, setState() is working, but your container isn't in its scope.
When the date is selected, _TimeRadioButtonsClassState.build will be called which doesn't affect your container since your container isn't in that widget (or class, if you prefer)
If possible, try shifting the container inside the widget where you'll be calling setState or try using a different state management approach
A more complex approach would be using GlobalKey and passing it as a parameter to your StatefulWidget and calling setState or using context.findAncestorStateOfType
Try this :
class _TimeRadioButtonsClassState extends State<TimeRadioButtonsClass> {
String _picked;
initState() {
_picked = widget.picked;
super.initState();
}
#override
Widget build(BuildContext context) {
return RadioButtonGroup(
orientation: GroupedButtonsOrientation.VERTICAL,
margin: const EdgeInsets.only(left: 12.0),
onSelected: (String selected) => setState((){
_picked = selected;
}),
labels: widget.labels,
picked: _picked,
activeColor: Color(0xffFFD243),
onChange: (String label, int index) => print("label: $label index: $index"),
);
}
}

Flutter + Provider: How do I pass the value of the slider back to the provider?

Flutter 1.9.1
Provider 3.1.0
I am currently building my first app and I have decided to jump in on state management now as to not get to bogged down later on in my build.
I have set up a width_restriction_provider;
class WidthRestrictionProvider extends ChangeNotifier {
int _widthRestriction = 140;
int get widthRestriction => _widthRestriction;
set widthRestriction(int val) {
_widthRestriction = val;
notifyListeners();
}
changeWidthSlider(int newValue) {
_widthRestriction = newValue;
notifyListeners();
}
}
and this is my slider widget;
class WidthSlider extends StatelessWidget {
#override
Widget build(BuildContext context) {
final WidthRestrictionProvider widthRestrictionProvider =
Provider.of<WidthRestrictionProvider>(context);
return Container(
child: Slider(
min: 140.0,
max: 420.0,
onChanged: () => widthRestrictionProvider.changeWidthSlider(newValue);
),
);
}
}
I have tried many different ways but I keep running into problems along the way.
How do I successfully pass the new slider value back to the provider?
I am currently learning Flutter and Dart so I apologize if this question comes across as a little basic.
I have solved this issue by using statefulWidget for the range slider. Hopefully it will work for you
class RangeSliderPricing extends StatefulWidget {
const RangeSliderPricing({Key? key}) : super(key: key);
#override
_RangeSliderPricingState createState() => _RangeSliderPricingState();
}
class _RangeSliderPricingState extends State<RangeSliderPricing> {
RangeValues _currentRangeValues = RangeValues(20, 40);
#override
Widget build(BuildContext context) {
return RangeSlider(
values: _currentRangeValues,
min: 0,
max: 100,
divisions: 5,
labels: RangeLabels(
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.round().toString(),
),
onChanged: (RangeValues values) {
print('on change working $values');
setState(() {
_currentRangeValues = values;
});
context.read<ProductController>().fliterByPriceRange(
priceStart: values.start.round().toDouble(),
priceEnd: values.end.round().toDouble());
},
);
}
}
You can use StatefullBuilder:
StatefulBuilder(
builder: (context, setState) {
return Slider(
value: myProvider.sliderValue,
onChanged: ((double value) {
setState(() => myProvider.sliderValue = value);
}),
min: 0,
max: 100,
);
},
),

How to implement a Slider within an AlertDialog in Flutter?

I am learning app development on Flutter and can't get my Slider to work within the AlertDialog. It won't change it's value.
I did search the problem and came across this post on StackOverFlow:
Flutter - Why slider doesn't update in AlertDialog?
I read it and have kind of understood it. The accepted answer says that:
The problem is, dialogs are not built inside build method. They are on a different widget tree. So when the dialog creator updates, the dialog won't.
However I am not able to understand how exactly does it have to be implemented as not enough background code is provided.
This is what my current implementation looks like:
double _fontSize = 1.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(qt.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.format_size),
onPressed: () {
getFontSize(context);
},
),
],
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 15.0),
itemCount: 3,
itemBuilder: (context, index) {
if (index == 0) {
return _getListTile(qt.scripture, qt.reading);
} else if (index == 1) {
return _getListTile('Reflection:', qt.reflection);
} else {
return _getListTile('Prayer:', qt.prayer);
}
})
);
}
void getFontSize(BuildContext context) {
showDialog(context: context,builder: (context){
return AlertDialog(
title: Text("Font Size"),
content: Slider(
value: _fontSize,
min: 0,
max: 100,
divisions: 5,
onChanged: (value){
setState(() {
_fontSize = value;
});
},
),
actions: <Widget>[
RaisedButton(
child: Text("Done"),
onPressed: (){},
)
],
);
});
}
Widget parseLargeText(String text) {...}
Widget _getListTile(String title, String subtitle) {...}
I understand that I will need to make use of async and await and Future. But I am not able to understand how exactly. I've spent more than an hour on this problem and can't any more. Please forgive me if this question is stupid and noobish. But trust me, I tried my best.
Here is a minimal runnable example. Key points:
The dialog is a stateful widget that stores the current value in its State. This is important because dialogs are technically separate "pages" on your app, inserted higher up in the hierarchy
Navigator.pop(...) to close the dialog and return the result
Usage of async/await
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _fontSize = 20.0;
void _showFontSizePickerDialog() async {
// <-- note the async keyword here
// this will contain the result from Navigator.pop(context, result)
final selectedFontSize = await showDialog<double>(
context: context,
builder: (context) => FontSizePickerDialog(initialFontSize: _fontSize),
);
// execution of this code continues when the dialog was closed (popped)
// note that the result can also be null, so check it
// (back button or pressed outside of the dialog)
if (selectedFontSize != null) {
setState(() {
_fontSize = selectedFontSize;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Font Size: ${_fontSize}'),
RaisedButton(
onPressed: _showFontSizePickerDialog,
child: Text('Select Font Size'),
)
],
),
),
);
}
}
// move the dialog into it's own stateful widget.
// It's completely independent from your page
// this is good practice
class FontSizePickerDialog extends StatefulWidget {
/// initial selection for the slider
final double initialFontSize;
const FontSizePickerDialog({Key key, this.initialFontSize}) : super(key: key);
#override
_FontSizePickerDialogState createState() => _FontSizePickerDialogState();
}
class _FontSizePickerDialogState extends State<FontSizePickerDialog> {
/// current selection of the slider
double _fontSize;
#override
void initState() {
super.initState();
_fontSize = widget.initialFontSize;
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Font Size'),
content: Container(
child: Slider(
value: _fontSize,
min: 10,
max: 100,
divisions: 9,
onChanged: (value) {
setState(() {
_fontSize = value;
});
},
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
// Use the second argument of Navigator.pop(...) to pass
// back a result to the page that opened the dialog
Navigator.pop(context, _fontSize);
},
child: Text('DONE'),
)
],
);
}
}
You just need to warp the AlertDialog() with a StatefulBuilder()