Build a Professional Unit Converter App in Flutter with Clean Architecture
Creating a Unit Converter app in Flutter is a powerful way to move beyond beginner-level development and start thinking like a professional developer. In this guide, you will not only build the app but also learn how to structure your code using clean architecture principles, making your project scalable, maintainable, and production-ready.
Unlike basic tutorials, this guide focuses on writing modular code, separating business logic from UI, and building reusable components. By the end, you will have a robust unit converter app that can easily be extended with new features.
What Makes This Project Advanced
- Separation of UI and business logic.
- Reusable widgets for dropdowns and inputs.
- Centralized conversion engine.
- Scalable folder structure.
- Easily extendable unit categories.
Project Architecture Overview
We will use a simplified clean architecture approach. This means dividing the app into layers such as presentation, domain, and data. Each layer has a specific responsibility.
- Presentation Layer – UI and user interaction.
- Domain Layer – Business logic and rules.
- Data Layer – Static or dynamic data sources.
Folder Structure
lib/
├── main.dart
├── core/
│ └── constants.dart
├── data/
│ └── unit_data.dart
├── domain/
│ └── converter_service.dart
├── presentation/
│ ├── screens/
│ └── widgets/
This structure keeps your project organized and makes it easier to manage as it grows.
Creating Unit Data
Instead of hardcoding values inside UI, store all units in a centralized file.
class UnitData {
static const lengthUnits = ['Meter', 'Kilometer', 'Mile'];
static const weightUnits = ['Gram', 'Kilogram', 'Pound'];
}
Building the Conversion Engine
The conversion engine is the heart of your app. It should be flexible and reusable across different categories.
class ConverterService {
double convert(String category, double value, String from, String to) {
switch (category) {
case 'Length':
return _convertLength(value, from, to);
case 'Weight':
return _convertWeight(value, from, to);
default:
return value;
}
}
double _convertLength(double value, String from, String to) {
Map<String, double> factors = {
'Meter': 1,
'Kilometer': 1000,
'Mile': 1609
};
return value * (factors[from]! / factors[to]!);
}
double _convertWeight(double value, String from, String to) {
Map<String, double> factors = {
'Gram': 1,
'Kilogram': 1000,
'Pound': 453.592
};
return value * (factors[from]! / factors[to]!);
}
}
Designing Reusable Widgets
Reusable widgets reduce code duplication and improve maintainability. Create a custom dropdown widget.
class CustomDropdown extends StatelessWidget {
final String value;
final List<String> items;
final Function(String?) onChanged;
const CustomDropdown({required this.value, required this.items, required this.onChanged});
@override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: value,
items: items.map((item) {
return DropdownMenuItem(value: item, child: Text(item));
}).toList(),
onChanged: onChanged,
);
}
}
Creating the Main Screen
The home screen connects UI with logic. It collects user input, calls the converter service, and displays results.
double result = 0;
void calculate() {
final service = ConverterService();
setState(() {
result = service.convert(category, inputValue, fromUnit, toUnit);
});
}
Handling User Input Safely
Always validate user input to prevent crashes. Use try-catch when parsing numbers.
double value = double.tryParse(inputController.text) ?? 0;
Adding Category Selection
Use a dropdown or tabs to switch between categories like Length, Weight, and Temperature.
Optimizing Performance
- Avoid unnecessary rebuilds.
- Use const widgets where possible.
- Separate widgets into smaller components.
- Cache repeated calculations if needed.
Adding Dark Mode
Dark mode improves user experience. Flutter makes it easy using ThemeData.
theme: ThemeData.dark(),
Testing Strategy
Testing ensures your app works correctly. Focus on unit testing your conversion logic.
test('Meter to Kilometer', () {
final service = ConverterService();
expect(service.convert('Length', 1000, 'Meter', 'Kilometer'), 1);
});
Scaling the App
To scale your app, you can add APIs, store user preferences, or integrate cloud services.
Real-World Improvements
- Add currency conversion using live APIs.
- Store recent conversions locally.
- Add multilingual support.
- Use animations for better UX.
Conclusion
This advanced Unit Converter app demonstrates how to build scalable Flutter applications using clean architecture and reusable components. By separating logic from UI, your app becomes easier to maintain and extend.
Continue improving this project by adding more categories, improving design, and integrating real-world data sources.
Codecrown