We all have seen the Controller class on a different widget, like ListView, VideoPlayer… It is used for controlling(modify) properties inside a widget. Because all the field inside a widget is marked as final, we cannot change them directly. Controller is the rule as a proxy to modify property inside the widget.
Today, I’m gonna give a simple implementation of a custom controller to give you a simple understanding of controller in flutter widget.
Let say we have a simple widget, which only have a single field color
class ColorWidget extends StatefulWidget {
const ColorWidget({required this.color, Key? key}) : super(key: key); final Colors color; @override
_ColorWidgetState createState() => _ColorWidgetState();
}
class _ColorWidgetState extends State<ColorWidget> {
@override
Widget build(BuildContext context) {
return Container(
color: widget.color,
);
}
}
Let say we want to change the widget color outside this widget.
Normally, we do _changeColor(someColor); We rerender the parent widget so the child widget can update.
Colors _color;@override
Widget build(BuildContext context) {
return Container(
child: ColorWidget(color: _color),
);
}void _changeColor(Colors color) {
setState(() {
_color = color
});
}
But it is not a good practice, since the child widget is the one should be updated, and parent widget does not change anything, which should not be in the ‘render list’.
Controller is here to solve this.
Let’s create a controller class.
class ColorController extends ChangeNotifier {
Colors color;
ColorController(this.color);
changeColor(Colors color) {
this.color = color;
notifyListeners();
}
}
We create a colorController which extend ChangeNotified class, which has some useful callback function to notify listener so we don’t have to write it again.
Next thing is add controller as param
class ColorWidget extends StatefulWidget {
const ColorWidget({
required this.controller,
required this.color, Key? key})
: super(key: key);final Colors color;
final ColorController controller;@override
_ColorWidgetState createState() => _ColorWidgetState();
}
In the _ColorWidgetState, we add observer to controller so it would notify the UI when the controller has new event.
class _ColorWidgetState extends State<ColorWidget> {@override
void initState() { widget.controller.addListener(() {
setState(() {});
} super.initState();
} @override
Widget build(BuildContext context) {
return Container(
color: widget.controller.color,
);
}
}
In the parent widget, we init the controller instance.
final _controller = ColorController(_color);@override
Widget build(BuildContext context) {
return Container(
child: ColorWidget(
controller: _controller,
color: _color),
);
}void _changeColor(Colors color) {
_controller.changeColor(color);
}
Every time the controller called notifyListener, it would call the setState, and the UI would update.
That’s it, thank you for reading.