The code below is a simple example of a Flutter widget I want to refactor, to improve both organization and readability.
I want to refactor _incrementCounter
and _decrementCounter
so that the _MySimpleWidgetState
class isn’t so cluttered. I’ve tried extending _MySimpleWidgetState
class by moving the _incrementCounter
and _decrementCounter
functions there, but that made the code less readable.
What am I missing here? I vaguely recall knowing how to do this, but the technique isn’t coming to mind right now. Please forgive me if this is something really basic.
class MySimpleWidget extends StatefulWidget {
const MySimpleWidget({super.key});
@override
State<MySimpleWidget> createState() => _MySimpleWidgetState();
}
class _MySimpleWidgetState extends State<MySimpleWidget> {
int _counter = 0;
_incrementCounter() {
setState(() {
_counter++;
});
}
_decrementCounter() {
setState(() {
_counter--;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: Text('Counter Value: $_counter'),
),
TextButton(onPressed: () => _incrementCounter(),
child: const Text('Increment Counter')),
TextButton(onPressed: () => _decrementCounter(),
child: const Text('Decrement Counter'))
],
);
}
}
2
Answers
To make your code more organized and readable, you can separate the logic from the UI by creating a separate class to handle the counting logic (incrementing and decrementing).
This way, the
State
class of your widget can focus on the UI, making your code easier to manage.Here’s how to do it:
CounterController
class: This class will manage the logic for incrementing and decrementing the counter.MySimpleWidget
and_MySimpleWidgetState
focused on the UI: These classes will only handle the user interface.CounterController
to the widget’s state, maintaining a clear separation between logic and UI.Refactored Code:
Explanation:
CounterController Class: This class manages the counting logic, so the widget’s state no longer directly handles it.
_MySimpleWidgetState Class: This class is responsible for the UI. It creates an instance of
CounterController
and uses its methods to update the counter.This approach keeps your code modular, separating business logic from the UI, which makes it easier to maintain and scale in the future.
The key idea is to move the logic that doesn’t directly involve UI concerns (in this case, the counter manipulation logic) out of the widget’s state class. We can achieve this by introducing a separate class that handles the counter logic. This approach follows the Single Responsibility Principle, where the _MySimpleWidgetState will focus solely on the UI, and the counter logic will reside in a dedicated class.
Create a Separate Counter Logic Class: This class will handle the incrementing and decrementing logic. It keeps the state and logic related to the counter separate from the UI, making it easier to maintain.
Use the Counter Class in the State: The state class will delegate counter manipulation to the newly created counter logic class. The state class will still call setState() to notify Flutter of any changes, but it no longer needs to directly manage the logic itself.
Counter Class
: This is a simple class that holds the counter’s value and the logic for incrementing and decrementing the counter.Separation of Concerns: The _MySimpleWidgetState no longer manages the logic for changing the counter value. Instead, it simply tells the Counter class to perform those operations and calls setState() to rebuild the UI.
Improved Readability: The _MySimpleWidgetState is now cleaner and only focuses on rendering the UI, making the code more readable and maintainable.
Hope this helps!