I've made a custom checkbox widget in dart and used a global key to save the state.
class CheckBox extends StatefulWidget {
final String label;
final void Function(dynamic) onChanged;
const CheckBox({required this.label, required this.onChanged, Key? key})
: super(key: key);
#override
CheckBoxState createState() => CheckBoxState();
}
class CheckBoxState extends State<CheckBox> {
final key = GlobalKey();
late bool isChecked;
#override
void initState() {
isChecked=false;
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(widget.label, style: Theme.of(context).textTheme.headline4),
KeyedSubtree(
key: key,
child: Checkbox(
activeColor: LightSeaGreen,
checkColor: White,
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = !isChecked;
});
widget.onChanged(value);
},
),
),
],
),
);
}
}
I've populated a list of checkboxes using the above widget as below,
when I select the checkboxes in one category (i.e. certificate provider ) and check the checkboxes in the course language category, the selected checkboxes of the certificate provider remains unchecked (the state is not saved).What can I do to save the state when I go from from one category to another? Any kind of help would be highly appreciated.
You can't use Global key to preserve state of all your responces. you should use a class (factory) to save your responces and use a state management library to handle your responces maybe, provider will work well. Or, you can use a local data base like hive to store your responces and you can fetch these responces whenever you want that will be best pratice.
Thank you
Related
I would like to get the value of the dropdown from the other widget in the real estate app. Say I have two widgets. First one is the dropdown widget, and the second one is Add New Property widget (or a page).. I would like to access the value of the dropdown from the Add New Property.
I could achieve this with final Function onChanged; but Im wondering if there is another way to achieve with the Provider package or the ValueNotifier
the code below is my Dropdown button widget
class PropertyType extends StatefulWidget {
final Function onChanged;
const PropertyType({
super.key,
required this.onChanged,
});
#override
State<PropertyType> createState() => _PropertyTypeState();
}
class _PropertyTypeState extends State<PropertyType> {
final List<String> _propertyTypeList = propertyType;
String? _propertyType = 'No Info';
#override
Widget build(BuildContext context) {
return ANPFormContainer(
fieldTitle: 'Property Type',
subTitle: 'အိမ်ခြံမြေအမျိုးအစား',
child: FormBuilderDropdown<String>(
name: 'a2-propertyType',
initialValue: _propertyType,
items: _propertyTypeList
.map(
(itemValue) => DropdownMenuItem(
value: itemValue,
child: Text(itemValue),
),
)
.toList(),
onChanged: (val) {
setState(() {
_propertyType = val;
widget.onChanged(val);
});
},
),
);
}
}
And this is the "Add New Property" form page
class ANPTest extends StatefulWidget {
const ANPTest({super.key});
#override
State<ANPTest> createState() => _ANPTestState();
}
class _ANPTestState extends State<ANPTest> {
final TextEditingController _propertyid = TextEditingController();
String _propertyType = 'No Info';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ZayyanColorTheme.zayyanGrey,
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
PropertyID(propertyID: _propertyid),
PropertyType(onChanged: (String value) {
_propertyType = value;
}),
addVerticalSpacer(25),
ANPNextButton(onPressed: _onpressed),
],
),
),
);
}
_onpressed() {
final anp = MdlFirestoreData(
propertyid: _propertyid.text, propertyType: _propertyType)
.toFirestore();
FirebaseFirestore.instance.collection('Selling Posts').add(anp);
}
}
Thank you for helping me out.
Best
yes, you could use Getx or provider package by creating a controller(function) and the package helps you to have access to variables in
your controller to use them everywhere in your program,
you may need to learn about Getx
it can help you manage your navigation and state
I have custom widget (Container with some Texts and one Slider). I want this widget to reuse in various places of app. I know how to pass data via Constructor (with named arguments / parameters). This works fine. I have problem with creating currentValue variable that will show Slider's current position.
First (before widget is build and shown) I would like to initialize this variable (currentValue) with value set in different variable - initialValue.
Then, I want to store current position of slider in currentValue variable.
Finally I want to get value of currentValue (sliders value) in different part of application.
Should I use any kind of State Management libraries or local variable may be enough?
How to do this properly, with Flutter Best Practices in mind?
Thanks for suggestions. Of course sample code is welcome.
Below is my code (what I have so far achieved):
import 'package:flutter/material.dart';
class SettingsSliderWidget extends StatefulWidget {
String title;
double initialValue;
double minValue;
double maxValue;
int divisions;
SettingsSliderWidget(
{required this.title, required this.initialValue, required this.minValue, required this.maxValue, required this.divisions});
#override
State<SettingsSliderWidget> createState() => _SettingsSliderWidgetState();
}
class _SettingsSliderWidgetState extends State<SettingsSliderWidget> {
double currentValue = 15; // I don't like this solution! I would like to assign initialValue here
#override
Widget build(BuildContext context) {
// Here I can put 'local variable' - but this have not solved my problems.
return Container(
decoration: BoxDecoration(
border: Border.all(width: 0.25),
borderRadius: BorderRadius.circular(5),
color: Colors.white12),
padding: const EdgeInsets.fromLTRB(25, 10, 25, 10),
child: Column(
children: [
Row(children: [
Text(widget.title),
Spacer(),
Text(currentValue.toString())
]),
Slider(
min: widget.minValue,
max: widget.maxValue,
activeColor: Colors.lightBlue,
inactiveColor: Colors.lightGreen,
thumbColor: Colors.lightBlueAccent,
value: currentValue,
divisions: widget.divisions,
label: '${currentValue}',
onChanged: (value) {
setState(() {
currentValue = value;
});
},
),
],
),
);
}
}
You can use late double currentValue = widget.initialValue; to get value 1st time on state class. And to get changed value, use a callback method like final ValueChanged<double>? onChanged;
class SettingsSliderWidget extends StatefulWidget {
final String title;
final double initialValue;
final double minValue;
final double maxValue;
final int divisions;
/// to get value
final ValueChanged<double>? onChanged;
const SettingsSliderWidget({
Key? key,
required this.title,
required this.initialValue,
required this.minValue,
required this.maxValue,
required this.divisions,
this.onChanged,
}) : super(key: key);
#override
State<SettingsSliderWidget> createState() => _SettingsSliderWidgetState();
}
class _SettingsSliderWidgetState extends State<SettingsSliderWidget> {
late double currentValue = widget.initialValue;
....
onChanged: (value) {
if(widget.onChanged!=null)widget.onChanged!(value);
And use cases like
SettingsSliderWidget(
onChanged: (value) {
},
),
you can put currentValue in empty file beside main file like this:
double currentValue = 15;
see this :
i use this trick if i want use variable in many screen and files
then you can use this variable wherever you want in your app
You can use initState to set the the state based on widget.initialValue
class _SettingsSliderWidgetState extends State<SettingsSliderWidget> {
late double currentValue;
#override
void initState() {
currentValue = widget.initValue
super.initState();
}
...
I try to create custom dropdown for my app. And the dropdown can have three duplicate on the same screen. When each dropdown tapped, there is a variable called isDropdownOpened set to true. The case, when one of the dropdown opened then I wan't the others have to set it's isDropdownOpened variable to false again. So, how to change the isDropdownOpened value automatically when other instances of dropdown tapped?
should i use state management like provider, or bloc and cubit? Or even i can do it with setState.
here is the code.
class SearchDropdownButton extends StatefulWidget {
const SearchDropdownButton({
Key? key,
required this.text,
}) : super(key: key);
final String text;
#override
State<SearchDropdownButton> createState() => _SearchDropdownButtonState();
}
class _SearchDropdownButtonState extends State<SearchDropdownButton> {
late OverlayEntry _categoryBottomBar;
bool isDropdownOpened = false;
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
onPressed: () {
setState(() {
if (isDropdownOpened) {
_categoryBottomBar.remove();
} else {
Overlay.of(context)!.insert(_categoryBottomBar);
}
isDropdownOpened = !isDropdownOpened;
});
},
and the instances on a row.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
SizedBox(width: 20),
SearchDropdownButton(text: "Consume"),
SizedBox(width: 20),
SearchDropdownButton(text: "Gadget"),
SizedBox(width: 20),
SearchDropdownButton(text: "Fashion"),
SizedBox(width: 20),
],
),
the complete code : https://pastebin.com/QtfDfXzU
Your case is not specific to flutter (React is same). Basic way to do this is moving isDropdownOpened state to parent stateful widget. Corresponding react tutorial is here.
If you want to do this in implicit way then yes, you should use state management library for inter-component state sharing.
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 :-)
In my flutter app I need to pass on data from a widget to another widget. Both are encapsulated in a Column widget, the diagram below best describes it
I've tried using inherited widget, but that doesn't seem to help, here's the code for the inherited widget I created:
import 'package:flutter/material.dart';
class updateMonth extends StatefulWidget {
final Widget child;
const updateMonth({Key? key,required this.child}) : super(key: key);
#override
_updateMonthState createState() => _updateMonthState();
}
class _updateMonthState extends State<updateMonth> {
int month=DateTime.now().month;
void updMonth(int newMonth){
setState(() =>
month=newMonth
);
}
#override
Widget build(BuildContext context)=>monthFromCal(
child: widget.child,
month: month,
newMonth: this,
);
}
class monthFromCal extends InheritedWidget {
final int month;
final _updateMonthState newMonth;
const monthFromCal({
Key? key,
required Widget child,
required this.month,
required this.newMonth
}) : super(key: key, child: child);
static _updateMonthState of(BuildContext context)=>context
.dependOnInheritedWidgetOfExactType<monthFromCal>()!.newMonth; //NULL CHECK
#override
bool updateShouldNotify(monthFromCal old) {
return old.month!=month;
}
}
updateMonth() is a stateful widget that is supposed to receive and update the value of month, which would then be passed on to the inherited widget monthFromCal(). When I run this, I get "null check operator used on a null value" error. For your ref, here are the two widgets:
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TableCalendar(
focusedDay: _focusedDay, ///DateTime _focusedDay=DateTime.now();
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
setState(() {
monthFromCal.of(context).updMonth(_focusedDay.month);
});
},
),
Container(
height: MediaQuery.of(context).size.height*0.3,
width: MediaQuery.of(context).size.width,
child: monthEvents(month: DateTime.now().month,),
),
],
),
How can I solve this ? Is there any other way of passing the value ?
Thanks in advance
What you are really looking for is a function not a widget here is the solution :
Declare the function somewhere
int updateMonth(int month) => month = DateTime.now().month;
& then just call it wherever you want - it will take data & return it as you're requesting
updateMonth(_focusedDay);
the only issue is - i don't really get what you're trying to calculate but this is the solution to your problem