I have a separate class which lays out radio buttons on the homepage in a separate class.
Radio Class:
Below is from TYPE enum which contains two values regular and nonregular:
TYPE _dtype = TYPE.regular;
Container(
child: Row(
children: <Widget>[
Container(
width: 180.0,
child: RadioListTile<TYPE>(
title: Text(
'Regular' ,
style: TextStyle(
fontSize: 20.0,
),
),
value: TYPE.regular,
groupValue: _dtype,
onChanged: (TYPE value) { setState(() { _dtype = value; }); },
),
),
Container(
width: 186.0,
child:RadioListTile<TYPE>(
title: const Text('Non Regular',
style: TextStyle(
fontSize: 20.0,
),
),
value: TYPE.nonregular,
groupValue: _dtype,
onChanged: (TYPE value) { setState(() { _dtype = value; }); },
),
),
],
),
)
i have couple of questions:
Is it possible to autosize the radio w.r.t. text in it? As without supplying width property, radio doesnot appear on screen.
Once one of the radio is selected, I want to pass the value..._dtype to homepage class to do some work. How can I do that?
Replace your Container with Expanded and remove the width attribute. This will automatically set each child of the row to occupy an equal amount of space.
To explain why this is necessary, RadioListTile will expand to take as much space as is available, and Container will be as big as its child if no size is specified. Since Row has infinite width and there's no width constraint between it and your RadioListTile, it's trying to expand to take up an infinite amount of width, so it won't render. Expanded automatically calculates and applies a width here, so it'll fix the issue and work well no matter the screen size.
Pass a callback function to your new widget. Flutter generally prefers sending data down the tree than up, so sending a callback function down to your button is a bit easier than sending the data up for your parent widget to handle, and it'll wind up having the same effect.
class YourRadioGrid extends StatelessWidget {
void Function(TYPE) doSomething = (t) {};
YourRadioGrid({this.doSomething});
...
onPressed: (TYPE value) {
doSomething(value);
}
}
// Then wherever in your homepage's build method this gets added:
YourRadioGrid(doSomething: (value) { /*handle the new value*/ })
Related
I'm trying to make a pair of dropdown buttons that take their items from the same list, and selecting a value in one disables it in the other one, and vice versa (much like Translators software, or conversions, as this example is the second one).
I tried the onChanged methods but they only worked when checking the list AFTER changing its value (i.e, list 2's value was still showing up on list 1 until I changed list 1's value, and vice versa).
Here's the list
const List<String> tipolong = <String>['mi', 'km', 'fur', 'hm', 'ch', 'dam', 'rd', 'm', 'yd', 'ft', 'dm', 'in', 'cm', 'mm'];
And here are the two Dropdown buttons with their respective derivatives of said list
tipolong2 = List.from(tipolong);
tipolong3 = List.from(tipolong);
class _DropdownButtonState extends State<DropdownButton> {
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValueLength1,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
// This is called when the user selects an item.
setState(() {
dropdownValueLength1 = newValue!;
tipolong3 = List.from(tipolong);
tipolong3.remove(dropdownValueLength1);
});
},
items: tipolong2.map((dropdownValueLength1) {
return DropdownMenuItem<String>(
value: dropdownValueLength1,
child: Text(dropdownValueLength1),
);
}).toList(), );
}
}
class _DropdownButtonState2 extends State<DropdownButton2> {
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValueLength2,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
// This is called when the user selects an item.
setState(() {
dropdownValueLength2 = newValue!;
tipolong2 = List.from(tipolong);
tipolong2.remove(dropdownValueLength2);
});
},
items: tipolong3.map((dropdownValueLength2) {
return DropdownMenuItem<String>(
value: dropdownValueLength2,
child: Text(dropdownValueLength2),
);
}).toList(), );
}
}
How can I make it so that when I choose an item in the first list, it is removed or disabled from the second one, and vice versa? So far I've tried the onTap and onChanged methods but they both work only on my second time checking the list (the one that should disappear does so, but after I popped up the list for a second time instead of the first one).
Sorry if my explanation is not clear.
You can check that is user selected from 1st dropdown then disable 2nd dropdown and vice-a-versa
You can use IgnorePointer to achieve your functionality
1st onChanged > Item Selected > store it in var A
Check var A is not empty > Disable 2nd DropDown and same for reverse
I have been experimenting with my flutter drop down button.
Context of what I am doing.
I have an app that will create a job and give it to an available staff member. I have stored all my staff members in a list for the menu button. I will put the code below to show the creation of the job ticket drop down button. selectedTech is at the top of the program so that's not the issue
String selectedTech = "";
Container(
// margin: EdgeInsets.only(right: 20),
width: MediaQuery.of(context).size.width / 2.5,
child: DropdownButton(
hint: Text(
selectedTech,
style: TextStyle(color: Colors.blue),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(color: Colors.blue),
items: listStaffUsers.map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
selectedTech = val.toString();
},
);
},
),
),
The above code works perfect.
However when I want to update the job ticket to change the available staff member I want to set the initial value of the drop down menu to the staff member assigned to the job, because it isn't always guaranteed that they change the staff member allocated to the job. When I set the selected value to my initial value I am locked with that value and cannot change it.
Here is the code I am using to update the staff member.
String selectedTech = "";
int the build method I add
selectedTech = widget.staff;
Container(
// margin: EdgeInsets.only(right: 20),
width: MediaQuery.of(context).size.width / 2.5,
child: DropdownButton(
hint: Text(
selectedTech,
style: TextStyle(color: Colors.blue),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(color: Colors.blue),
items: listStaffUsers.map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
selectedTech = val.toString();
},
);
},
),
),
Any Guidance or examples will be greatly appreciated.
As I understand under the Widget build method you set
selectedTech = widget.staff and then return the widget like this:
Widget build(BuildContext context) {
selectedTech = widget.staff;
return Container( ...
This will systematically lock your selectedTech to widget.staff whenever the build method is called (when you call setState). I mean whenever you change the value of the dropdown, the value will not be set the actual value on the dropdown menu. Because you call setState, setState builds the widget from scratch and selectedTech = widget.staff is called in these steps.
Instead of in build method you should initialize it first, then continue to build method.
class _StaffHomeState extends State<StaffHome> {
String? selectedTech;
// Write a function to initialize the value of selectedTech
void initializeSelectedTech () {
selectedTech = widget.staff;
}
// Call this function in initState to initialize the value
#override
void initState() {
initializeSelectedTech();
super.initState();
}
// Then Widget build method
Widget build(BuildContext context) {
return Container( .....
By this way, you initialize first the value before build method and whenever state changes, the data will be persisted.
I hope it is helpful.
It's worth noting that I am quite new to flutter so a lot of work done is based on tutorials or videos...
Apologies if the question seems obvious.
//Build Length Box.
Widget buildLength() => buildHeader(
header: 'House Length: ',
child: Row(
children: [
// I want to be able to get user input(has to be an integer value for calculations further in the program)
// from this child: Text(), At the moment I am only able to get the input from the slider...
//I want to be able to use the slider or the text next to it.
//The first child widget should preferably work with the same onChangedLength method the slider uses...
Expanded(
flex: 1,
child: Text(
length.toString(),
style: TextStyle(color: Colors.white),
),
),
Expanded(
flex: 9,
child: Slider( //TODO: Decide on the correct parameters for the slider, min/max/etc...
label: length.toString(),
value: (length ?? 0).toDouble(),
min: 0,
max: 100,
divisions: 100,
onChanged: (length) => onChangedLength(length.toInt()),
),
),
],
),
);
The onChanged Method was mentioned here as well.
onChangedLength: (length) => setState(() => this.editLength = length),
The tutorial I am busy with uses the onChanged, however if this wont work I am open to other methods I can use.
You are setting the changed value of the length into editLength.
yet you using length.toString() to display,
if your declared variable is int editLength
editLength.toString() // Text Widget
label: length.toString(), // Slider Widget
value: (editLength ?? 0).toDouble() // Slider Widget
or
if your declared variable is int length
onChanged: (newLength) => onChangedLength(newLength.toInt()), // Slider widget
onChangedLength(newLength) => setState(() => this.length = newLength); // onChangedLength function
I have a beginner question. It's really simple to break out a Widget to it's own class. Like having a Column with buttons in a stateless widget that accepts some functions and some strings in the constructor. Then I can include and use this from any screen and widget in my app.
But how is this achieved with dialogs? If I design a dialog I would love to have that in its own file so I can just import it, and then pass functions and texts into it.
Right now I'm trying to break out a Picker dialog form the flutter_picker package. In one of my screens I have this:
void _showTimeDialog() {
Picker(
adapter: NumberPickerAdapter(data: <NumberPickerColumn>[
NumberPickerColumn(begin: 0, end: 60, initValue: _minutes),
NumberPickerColumn(begin: 0, end: 60, initValue: _seconds),
]),
delimiter: <PickerDelimiter>[
PickerDelimiter(
child: Container(
width: 30.0,
alignment: Alignment.center,
child: Text(':'),
),
)
],
builderHeader: (context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('MINUTES'),
Container(
width: 30,
),
Text('SECONDS'),
],
),
);
},
hideHeader: false,
confirmText: 'OK',
diameterRatio: 1.3,
magnification: 1.3,
height: 100,
squeeze: 1,
title: Center(child: const Text('DURATION')),
selectedTextStyle: TextStyle(color: Theme.of(context).primaryColor),
onConfirm: (Picker picker, List<int> value) {
onConfirmDurationPicker(picker, value);
},
).showDialog(context);
}
void onConfirmDurationPicker(Picker picker, List<int> value) {
setState(() {
_minutes = picker.getSelectedValues()[0];
_seconds = picker.getSelectedValues()[1];
});
}
What I would like is to have this in it's own file. And then I want to pass the onConfirmDurationPicker function (that will change in different screens) and some other values to set this picker up. But I don't want to have to duplicate all this code in every single screen that need this kind of picker dialog.
What's the kosher way of breaking stuff like this out in its own classes/files?
Let me know if anything is unclear in my question.
You are on the right path! Indeed it is best practice to split your app into meaningful parts to avoid boilerplate code. In your case just create a new File and build a Stateless Widget there. This Stateless Widget should return your Picker and can take the arguments via it's constructor. You can then call your class with the .showDialog(context) wherever you want!
TLDR, I'm building a Stock Screener and when I access a table from one widget it renders proper state, but when called from within the class the state defaults to initial values (I think).
Overall design concept is user configures their stock screener and hits "Screen" button at bottom of page.
Here's a View Example. This is the Market Cap Selector Widget that uses Consumer to retrieve state from the class ScreenerController extends ChangeNotifier
class CapSelectorWidget extends StatelessWidget {
Widget capButton(capSelection) {
return Consumer<ScreenerController>(builder: (context, capData, child) {
return GestureDetector(
child: Container(
padding: EdgeInsets.all(12.0),
margin: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: capData.getColor(capSelection),
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
capSelection,
textAlign: TextAlign.center,
),
),
onTap: () {
capData.toggleCapSelection(capSelection);
},
);
});
}
Both methods getColor and toggleCapSelection ultimately reference and/or update the below _capColorList. Here is the code for the _capColorList and toggleCapSelection
class ScreenerController extends ChangeNotifier {
//////////////////////
// Market Cap Widget
//////////////////////
Map _capColorList = {
'Micro Cap': {
'isSelected': true,
'selectedColor': Colors.teal[300],
'unSelectedColor': Colors.white38,
},
'Small Cap': {
'isSelected': true,
'selectedColor': Colors.teal[400],
'unSelectedColor': Colors.white38,
},
'Mid Cap': {
'isSelected': true,
'selectedColor': Colors.teal[500],
'unSelectedColor': Colors.white38,
},
'Large Cap': {
'isSelected': true,
'selectedColor': Colors.teal[600],
'unSelectedColor': Colors.white38,
},
};
void toggleCapSelection(String capType) {
// Flip bool selected value
_capColorList[capType]['isSelected'] =
!_capColorList[capType]['isSelected'];
notifyListeners();
print(capType + ' ' + _capColorList[capType]['isSelected'].toString());
};
etc...
So far so good. When user hit's a Cap Bucket, it'll toggle state and the color will update.
Now when user hits "Screen" (ie Submit) at bottom of page, this code is implemented.
child: FlatButton(
child: Text('Screen', textAlign: TextAlign.center),
onPressed: () {
ScreenerController().screenStocks();
//Todo: Figure out why Provider.of doesn't work on Screen Button
//Provider.of<ScreenerController>(context, listen: false)
// .screenStocks();
},
),
I implement the following code to create a list of the Market Cap Buckets into a List.
My hypothesis is that because Provider.of isn't working for the FlatButton (it's in the same ChangeNotifierProvider tree), that I'm inadvertently creating a new instance.
Map screenerQueryBuilder() {
//If Cap Button is selected, add it to selectedCaps
List<String> selectedCaps = [];
for (String cap in _capColorList.keys) {
print(cap + _capColorList[cap]['isSelected'].toString());
if (_capColorList[cap]['isSelected']) {
selectedCaps.add(cap);
}
}
print(selectedCaps);
The problem is, even if let's say the Buttons "Mid Cap" and "Micro Cap" are not selected when the user hits Screen, when I iterate over _capColorList.keys the values are always true, and therefore they are always added to the selectedCaps List.
Might anyone know why this is?
Thank you in advance!