change variable value of other instances in flutter - flutter

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.

Related

setState hold focus and keyboard visibility

I tried to create something like the textfield widget in the gif below. I listen to the controller for changes and call setState(). I choose between two widgets depending on the controller.text.isEmpty. The problem is whenever setState is called the textField loses focus and the keyboard disappears. How do I stop this from happening?
Widget textInputInactive = Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
IconButton(onPressed: () {}, icon: Icon(Icons.camera_alt, size: 28, color: Color(0xff99999A),)),
IconButton(onPressed: () {}, icon: Icon(Icons.upload_file, size: 28, color: Color(0xff99999A),)),
Expanded(
child: textField
),
],
),
);
Widget textInputActive = Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(child: textField),
IconButton(onPressed: () {}, icon: Icon(Icons.send, color: Color(0xff99999A)))
],
),
);
Widget currTextInput = _textController.text.isNotEmpty ? textInputActive : textInputInactive;
If you need the keyboard to gain focus after the setState method is called. You need to create a focus node object and initialize it in your initState, and add it as a parameter to your focus node in the textfield. Then after you call setState, request focus, here is an example below.
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
// create the FocusNode object
FocusNode node = FocusNode();
#override
void initState() {
super.initState();
// request focus of the textfield
node.requestFocus();
}
#override
void dispose() {
// disposing it
node.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// assume this is where you call your setState
// to call back focus on the textfield
setState(() {});
node.requestFocus();
return TextField(
// add the node object here
focusNode: node,
);
}
}
You can try to use Value Notifier instead of setState, since setState will render the whole page and create a new focus node. check this link for value notifier and use it only for text filed

Checkbox state not preserved

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

flutter, How to judge whether another widget is in the tree

Actually, I have a parent widget, and It has some of the child widgets in its Column.
like this
Container(
width: double.infinity,
color: Colors.white,
padding: EdgeInsets.fromLTRB(20.0, 50.0, 20.0, 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Title(),
SizedBox(height: 80.0,),
confirmLoginType(),
SizedBox(height: 10.0),
LoginButton(),
PolicyTips(
key: IndexGlobalKey.policyTipsKey,
updateState: receiveMessageUpdateState
),
Bottom()
],
),
),
in the PolicyTips, I pass a key to it, and I want to get the key in the LoginButton , but It has always been null when I get currentState.
The code is below:LoginButton
class LoginButton extends StatefulWidget {
LoginButton({Key key}) : super(key: key);
#override
_LoginButtonState createState() => _LoginButtonState();
}
class _LoginButtonState extends State<LoginButton> {
#override
Widget build(BuildContext context) {
policyTipsKey = IndexGlobalKey.policyTipsKey.currentState;
return Container(
child: Text()
)
}
}
what can I do? help me please, thanks.
This is IndexGlobalKey code.
class IndexGlobalKey {
static final GlobalKey<_PolicyTipsState> policyTipsKey = GlobalKey<_PolicyTipsState>();
static GlobalKey<_FormState> phoneLoginKey = GlobalKey<_FormState>();
static GlobalKey<_FormForIdCardLoginState> idCardLoginKey = GlobalKey<_FormForIdCardLoginState>();
}
Build method of _LoginButtonState runs before PolicyTips renders and before IndexGlobalKey.policyTipsKey is actually set. The reason is LoginButton goes before PolicyTips in column. Thats why you get null when you call IndexGlobalKey.policyTipsKey.currentState from build of _LoginButtonState.
To solve this you need to call IndexGlobalKey.policyTipsKey.state right where you use it. For example, when you need to get policy tips state on button tap just use it inside onPressed callback:
class _LoginButtonState extends State<LoginButton> {
#override
Widget build(BuildContext context) {
// An example of your button
return TextButton(
onPressed: () {
final policyTipsState = IndexGlobalKey.policyTipsKey.currentState;
// Here you can use policyTipsState
},
child: Text('button'),
);
}
}

Flutter/Dart: Can I have multiple widgets with state in my Widgets tree?

I was always wondering, why I have to "lift the state up" of some widget down in the tree, to reflect changes in the widget's UI..
Can't I just simply have multiple stateful Widgets? and for example, import the lowest stateful widget down the tree into my top-level Widget, and from there could I not just call some method of my widget which triggers its setState() method and just updates that part in the DOM tree that is concerned with my widget?
And secondly, I would then have to move my properties and other important parts from the lower widget also up into my higher state widget, risking to clutter that class with unrelated functions and properties at some time, in my opinion, React solves that way better by just passing method callbacks down as it suits...
And is there always only one stateful widget in my Flutter app at the highest level?
Parents don't have access to child widgets/states, only the other way around. So you can not "import the lowest stateful widget down the tree into my top-level Widget"
You only have to "lift the state up" when you want to share that state between different branches of the widget tree. Then the children can lookup the parent and access the shared state.
Don't be afraid of StatefulWidget - you can use as many as you need
Research Flutter state management solutions which exist exactly for the purpose of keeping things separated (ChangeNotifier, Provider, BLOC, Redux, etc.)
Can I have multiple widgets with state in my Widgets tree?
The answer is, yes you can create multiple StatefulWidget in your widget. You can also create a callback function from the lowest StatefulWidget with Function(yourcallback). In my opinion, flutter also support component base model, and make as dynamic as possible to customize our own widget.
For example:
child widget
child widget has it's state.
class Child extends StatefulWidget {
final int counter;
final Function(int childCounter) callback; //here is your callback
const Child({
Key key,
this.counter,
this.callback,
}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
String _childState = '';
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green[400],
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Child"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.exposure_minus_1_rounded),
onPressed: () {
this.widget.callback(this.widget.counter - 1); //here is your callback
setState(() {
_childState = "minus one";
});
}),
IconButton(
icon: Icon(Icons.plus_one_rounded),
onPressed: () {
this.widget.callback(this.widget.counter + 1); //here is your callback
setState(() {
_childState = "plus one";
});
})
],
),
Text(_childState)
],
),
);
}
}
Parent Widget
the parent get the callback from the child.
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Multiple State"),
),
body: Container(
child: Column(
children: [
Child(
counter: _counter,
callback: (childCounter) { //here is callback from the child
print(childCounter);
setState(() {
_counter = childCounter;
});
},
),
Text("Parent"),
Center(
child: Text(_counter.toString()),
)
],
),
),
);
}
}
to change the child state from it's parent
you can use didUpdateWidget() here you can see the docs

When to use Valuechanged<> versus Valuesetter<> in flutter

I've been following some of the beginner flutter tutorials on their website and was doing this tutorial for basic interactivity, specifically the part where a parent widget is used to manage the state of a child widget. There is a ParentWidget and _ParentWidgetState class the code for which is as follows:
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
TapboxB is a class which is a child of ParentWidget, the code for which is as follows:
class TapboxB extends StatelessWidget {
TapboxB({this.active: false, #required this.onChanged});
final bool active;
final ValueChanged<bool> onChanged;
void _handleTap() {
onChanged(!active);
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Column(
//aligns column in the centre vertically
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
//sets text depending on _active boolean
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 20.0, color: Colors.white),
),
Text(
'Tapbox B',
style: TextStyle(fontSize: 14.0, color: Colors.white),
),
],
),
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
//sets colour depending on _active boolean
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}
The _handleTap method is called when the widget is clicked, which calls the onChanged callback, which toggles the active variable. In the definition for onChanged the type is ValueChanged<bool> which is documented as a "signature for callbacks that report that an underlying value has changed." If I change this however to ValueSetter<bool> the app works in the exact same way and nothing seems to change. So my question is what is the difference between the two of these? Is one better in this particular scenario?
I searched the documentation for you, using just flutter ValueChanged ValueSetter, and quickly found this:
void ValueSetter (T Value)
Signature for callbacks that report that a value has been set.
This is the same signature as ValueChanged, but is used when the callback is called even if the underlying value has not changed. For example, service extensions use this callback because they call the callback whenever the extension is called with a value, regardless of whether the given value is new or not.
typedef ValueSetter<T> = void Function(T value);
So they're just typedefs to the same underlying type, but one has a different semantic meaning, presumably used as self-documenting code despite the same underlying signature.
If you don't need the latter meaning of ValueSetter, then use ValueChanged, as the API already said.