Recently, I conducted a diagnostic on a Flutter project.
The app has not been maintained for half a year since the last time, and it looks like it has many long-standing issues.
In fact, the entire architecture is very unstable, and a slight touch could cause the building to collapse.
When evaluating the quality of a project, the first thing to look at is the dependency relationship.
If it is well organized, it will save time and effort, and if not, it will be difficult to make progress, like a fishbone stuck in the throat.
To be frank, the quality of the current project can be described as terrible.
This is not only due to a lack of mobile development experience, but also a significant improvement in engineering ability and habits.
Next, I will analyze what fatal factors have affected the project.
Problem 1: Chaotic Dependencies
Dependencies are like building blocks.
The current situation is that the blocks are stacked high but the foundation is not solid, making it very difficult to add new blocks or modify one of them.
Problem 2: Heavy Use of Generators
Including dependency injection and APIs, the heavy use of generators seems to liberate our hands, but it actually restricts us, making it difficult to troubleshoot and customize.
Problem 3: Ignoring Lint
First of all, the lint rules are still using the oldest rules and not the best practices.
Even so, there are too many lint issues accumulated, and these issues will reflect as runtime problems.
For example, function parameters are reassigned within the function, which can easily cause problems.
Problem 4: Treating Flutter as a Tool and Lack of Understanding of Flutter Style Development and Ecosystem
By choosing Flutter as the technology, it means that we should follow the best practices of Flutter.
Currently, the project is not following the best practices and is based on feelings.
For example, how to define the theme color, where to define it, and whether to use theme extension.
Also, the project is still using arb for multi-language support, and there has been no basic technical selection for routing and state management.
Below is a simple example code using Flutter and Riverpod to implement the MVI architecture:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Counter {
int count;
Counter({this.count = 0});
}
class CounterNotifier extends StateNotifier<Counter> {
CounterNotifier() : super(Counter());
void increment() {
state = Counter(count: state.count + 1);
}
void decrement() {
state = Counter(count: state.count - 1);
}
}
final counterProvider = StateNotifierProvider<CounterNotifier, Counter>((ref) {
return CounterNotifier();
});
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final counter = watch(counterProvider);
return Scaffold(
appBar: AppBar(title: Text('Counter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Count', style: TextStyle(fontSize: 24)),
Text('${counter.count}', style: TextStyle(fontSize: 48)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
context.read(counterProvider.notifier).decrement();
},
child: Text('-'),
),
SizedBox(width: 16),
ElevatedButton(
onPressed: () {
context.read(counterProvider.notifier).increment();
},
child: Text('+'),
),
],
),
],
),
),
);
}
}
void main() {
runApp(ProviderScope(child: CounterWidget()));
}
In this example, we use Riverpod to create a Counter class that manages the application’s state. Then, we create a CounterNotifier that manages the Counter state and handles all state changes. Next, we create a Provider that provides an instance of CounterNotifier. Finally, we create a CounterWidget that uses the Provider to access the Counter state and respond to user interactions. When the user presses the + or — button, we call the increment or decrement method of CounterNotifier to change the state and use the watch method to access the current value of the counter to update the UI.
The UI architecture should follow this pattern, which is very clear.
Currently, the project is heavily using bloc, which is actually high maintenance and low readability.
Problem 5: Unable to Keep Up with Future Flutter Versions
Flutter released version 3.10 with many new features and Dart 3 at Google IO this year. However, if the project’s quality is not up to standard, it is almost impossible to keep up in the future. All popular libraries will follow Flutter versions, and when a new feature is added, it will be found that the minimum version requirement of the library does not match.
The above is a preliminary diagnosis of the project, and my conclusion is that there is no other solution except to rewrite it.
Please visit my Medium for more articles: medium.com/@hamber