Updating a legacy Flutter project: A war case

We face the challenge of updating a Flutter project with over 60 outdated dependencies. Discover step by step how we tackled this process, the issues we encountered, and the strategies we used to modernize the application without breaking it in the process.

21.02.2025 — Edu Molins — 4 min read
Legacy code

Initial state: Patient diagnosis

The initial diagnosis revealed multiple critical points:

  • Flutter version: 1.22.0
  • Over 60 dependencies, many deprecated
  • Obsolete Java version
  • Outdated iOS Pods
  • Warnings everywhere
  • Deprecated code in active use

Phase 1: Preparation

1.1 Impact analysis

Before diving into the update, it was crucial to fully understand what we were facing. We spent a complete week analyzing the project in depth. During this phase, we completed the following critical tasks:

  • Conducted a complete dependency inventory
  • Identified critical functionalities
  • Created a regression test suite
  • Backed up everything in a separate branch
1.2 Current state documentation

Documentation became our lifeline throughout the process. We established a rigorous documentation system that included:

  • Screenshots of all main screens
  • List of critical functionalities
  • Record of specific behaviors that needed to be maintained
  • Mapping of complete user flows

Phase 2: Base update

2.1 Development environment update

Updating the development environment was our first major challenge. This process required a specific sequence of steps that we carefully executed:

  • Updating Flutter to the latest stable version:
  • flutter upgrade
    flutter channel stable
    flutter doctor
  • Updating Java, ensuring compatibility with existing native plugins
  • Updating Xcode and CocoaPods:
  1. pod repo update
    pod update
2.2 First round of dependencies

The dependency update process was like defusing a bomb: each wire cut could trigger a chain reaction. We approached this challenge with a structured plan:

  • Updating pubspec.yaml with new compatible versions
  • Resolving conflicts between interdependent dependencies
  • Identifying and replacing deprecated packages

Phase 3: The dependency war

3.1 Layer update strategy

We adopted a systematic approach, updating dependencies in the following order:

  1. Core dependencies:
    • HTTP client
    • State management
    • Navigation system
  2. UI dependencies:
    • Custom widgets
    • Component libraries
    • Themes and styles
  3. Functional dependencies:
    • API integrations
    • Local data handling
    • Business-specific functionalities
  4. Support tools:
    • Analytics
    • Crash reporting
    • Performance monitoring
3.2 Common problems encountered

During the process, we faced various technical challenges that required creative solutions:

The main obstacles included:

  • Version conflicts between interdependent packages
  • Breaking changes in popular APIs
  • Incompatibilities with new iOS/Android versions
  • Deprecated methods without direct alternatives

Phase 4: Refactoring and modernization

4.1 Code updates

Modernizing the codebase required several fundamental changes:

  1. Null safety migration: A process we carried out module by module, ensuring stability at each step.
  2. Widget updates: We identified and replaced obsolete components with their modern equivalents, improving both functionality and performance.
  3. Patterns and practices: We implemented the latest practices recommended by the Flutter team, including:
    • Use of new platform widgets
    • Implementation of new state patterns
    • Widget build optimization
4.2 Architecture improvements

In this phase, we took advantage to make significant architectural improvements:

  • Clear separation of responsibilities using Clean Architecture
  • Implementation of modern state management
  • Navigation and route management optimization
  • Improvement in resource and asset handling
Legacy code2

Phase 5: Testing and validation

The testing phase was critical but we maintained a pragmatic approach, focusing on the most important aspects to ensure application stability after the update.

5.1 Test plan

We focused on verifying that core functionalities continued working correctly:

  • Manual testing of main user flows
  • External API integration verification
  • Validation of forms and critical processes
  • Specific testing in areas most affected by updates
5.2 Performance monitoring

We performed basic but essential checks:

  • Loading time verification in different sections
  • Final application size verification
  • Testing on different devices and OS versions

Lessons learned and final recommendations

The experience left us with valuable lessons that have transformed our approach to maintaining Flutter applications:

  1. Documentation as priority: Maintain detailed records of each decision and change made.
  2. Gradual update: Don't try to update everything at once, but proceed in well-defined phases.
  3. Continuous testing: Automated tests are fundamental for early problem detection.
  4. Clear communication: Keep the client informed of progress and challenges encountered.

Conclusion

Updating a legacy Flutter project may seem overwhelming, but with a well-planned strategy and methodical execution, it's possible to modernize even the oldest projects. The key is maintaining a balance between the need to update and the stability of the existing system.

If you find yourself facing a similar challenge with your Flutter project and need professional help to update or modernize it, our team can help you. We have the proven experience and methodology to take your application to the next level safely and efficiently 🚀.

💡 How much does it cost to develop an app for your business? Discover the prices