Widget Controller
To update a widget's state from parents, Flutter provides various ways:
setState, ChangeNotifier, ... We also want to suggest an alternative -
Controller.
Take an example from our tutorial, the BottomBar.
class BottomBarController extends Controller<_BottomBarState> {
int? get getIndex => currentState?.index;
set setIndex(int value) => currentState?.index = value;
}
class BottomBar extends StatefulWidget {
const BottomBar({
BottomBarController? controller,
this.index = 0,
this.width,
this.height,
this.bottomPadding = 0,
this.onChanged,
}) : super(key: controller);
final int index;
final double? width;
final double? height;
final double bottomPadding;
final void Function(int index)? onChanged;
State<BottomBar> createState() => _BottomBarState();
}
class _BottomBarState extends State<BottomBar> {
late int _index;
int get index => _index;
set index(int value) => setState(() => _index = value);
void initState() {
super.initState();
index = widget.index;
}
Widget build(BuildContext context) {
const topPadding = 25.0;
return Container(
color: ColorDecor().xFFFFFF,
padding: EdgeInsets.only(top: topPadding, bottom: widget.bottomPadding),
constraints: BoxConstraints.tightFor(
width: widget.width,
height: (widget.height ?? 0) + topPadding,
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GestureDetector(
onTap: onCoinsTap,
child: Icon(
Icons.paid_rounded,
color: index == 0 ? Color(0xFF8BC540) : Colors.grey,
),
),
GestureDetector(
onTap: onAccountTap,
child: Icon(
Icons.category_rounded,
color: index == 1 ? Color(0xFF8BC540) : Colors.grey,
),
),
],
),
);
}
void onCoinsTap() {
index = 0;
widget.onChanged?.call(index);
}
void onAccountTap() {
index = 1;
widget.onChanged?.call(index);
}
}
Focusing on BottomBarController, we can see it extends from Controller. In a
nutshell, Controller is a GlobalKey. On the other hand, Controller plays a
role similar to Agent, as a reference to an object, the widget in this case.
Controller can:
-
prevent the widget from exposing the
StatelikeGlobalKey. -
update the
Statedirectly without needing to wrap it with other State management widgets (ValueListenableBuilder, etc.), which simplifies the widget tree. -
narrow the scope of widgets that need to update states instead of the whole widget (
setState).
Controller shall be set up with APIs for updating state.
class BottomBarController extends Controller<_BottomBarState> {
int? get getIndex => currentState?.index;
set setIndex(int value) => currentState?.index = value;
}