I have several custom Widgets in my Flutter app that edit the variables that I send and I need the new value.
I have tried this in two ways, but I don’t know which one is the best.
I have done it via sending a callback that gets me the new value:
class ClosedDaysSelector extends StatefulWidget {
final Function(Map<String, bool>)? onChanged;
const ClosedDaysSelector({Key? key, this.onChanged}) : super(key: key);
@override
State<ClosedDaysSelector> createState() => _ClosedDaysSelectorState();
}
class _ClosedDaysSelectorState extends State<ClosedDaysSelector> {
List<String> weekdays = ['L', 'M', 'X', 'J', 'V', 'S', 'D'];
List<bool> values = List.filled(7, false);
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text('Días de cierre'),
Row(
children: [
for (int i = 0; i < weekdays.length; i++)
Expanded(
child: MyCheckboxListTile(
title: weekdays[i],
value: values[i],
onChanged: (bool? value) {
setState(() {
values[i] = value!;
});
if (widget.onChanged != null) {
widget.onChanged!(toJson());
}
},
),
),
],
),
],
);
}
Map<String, bool> toJson() {
final jsonMap = <String, bool>{};
for (int i = 0; i < weekdays.length; i++) {
jsonMap[weekdays[i]] = values[i];
}
return jsonMap;
}
}
Also I have done it with text editing controllers, and this is the more similar way to pass by reference that I have found:
class DropdownZonasWidget extends StatefulWidget {
final TextEditingController dropdownValueController;
final Function(String) onDropdownValueChanged;
final bool enabled;
const DropdownZonasWidget({Key? key, required this.dropdownValueController, required this.onDropdownValueChanged, required this.enabled}) : super(key: key);
@override
State<DropdownZonasWidget> createState() => _DropdownZonasState();
}
class _DropdownZonasState extends State<DropdownZonasWidget> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: FutureBuilder(
future: MyDatabase.instance.get_zonas(),
builder: (BuildContext context, AsyncSnapshot<List<Zona>> snapshot) {
if (snapshot.hasData) {
List<Zona> salas = snapshot.data!;
List<DropdownMenuItem<String>> defaultValue = [
const DropdownMenuItem<String>(
value: '',
child: Text(""),
)
];
if(snapshot.data!.any((item) => item.zona == '#') && widget.dropdownValueController.text == '')
{
widget.dropdownValueController.text = '#';
}
return Padding(
padding: const EdgeInsets.only(right: 10),
child: IgnorePointer(
ignoring: widget.enabled,
child: DropdownButtonFormField<String>(
value: widget.dropdownValueController.text,
style: const TextStyle(color: Colors.black),
onChanged: (String? newValue) {
widget.dropdownValueController.text = newValue ?? '';
widget.onDropdownValueChanged(newValue ?? '');
},
items: defaultValue +
salas.map<DropdownMenuItem<String>>((Zona value) {
return DropdownMenuItem<String>(
value: value.zona.toString(),
child: Text(
value.zona.toString(),
style: const TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.w600),
),
);
}).toList(),
hint: const Text(
"Zonas",
style: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.w600),
),
validator: (value) {
if (value == null)
{
return "Por favor asigna una zona";
}
else
{
return null;
}
}
),
)
);
} else {
return const Text("Sin zonas");
}
}),
);
}
}
2
Answers
In Dart (the language Flutter is written in), all variables are essentially passed by reference because everything is an object. However, Dart doesn’t have the same concept of "reference" like in languages such as C++ or C#. Instead, when you pass a variable to a function or widget, you’re passing a reference to the object the variable points to.
However, the catch is that primitive types (like
int
,double
,bool
) are immutable. So, even though you’re passing their reference, changing them inside a function won’t affect their original value. If you need to modify such values inside a custom widget and reflect those changes outside, you’d typically use a callback or a state management solution.Here’s how you might approach this:
Using Callbacks:
If you want to send a variable to a widget and get updates when it changes, you can use a callback.
Using State Management:
There are various state management solutions in Flutter, such as
Provider
,Riverpod
,Bloc
, etc. Using them, you can effectively "pass by reference" by accessing and mutating shared state across widgets.Here’s a simple example using
Provider
:Using
GlobalKey
:While not recommended for simple cases due to its more verbose nature, a
GlobalKey
can be used to access the state of a stateful widget from outside.Among these approaches, callbacks and state management solutions are most commonly used, while
GlobalKey
is typically reserved for more complex cases or when you need direct access to a widget’s state from outside. Choose the approach that best fits the complexity and requirements of your application.Both approaches are valid, however, I suppose using
Callbacks
is more common for Flutter applications among developers.You can also use state
management solutions
likeBloc
orProvider
, andInherited widgets
to achieve the same result.