6 Years of Tech Debt, 1 Prompt: Modernizing a Legacy App with Gemini 4

Back in early 2020, I wrote a 4-part blog series on how a friend and I modernized Amathus, a Cyprus news aggregator app, using Google Cloud’s serverless technologies.

By the end of that series, we had decomposed our legacy Windows/.NET Framework monolith into a clean serverless microservices architecture on Google Cloud Run with a C#/.NET Core backend (Amathus.Reader, Amathus.Converter, and Amathus.Web connected via Cloud Scheduler, Cloud Storage, Pub/Sub, and Firestore) and a cross-platform Flutter mobile frontend (AmathusClient):

Amathus Serverless Architecture
Amathus Serverless Architecture

And then, as often happens with side projects, life got busy. After a final commit on November 21, 2020, the repo sat untouched for nearly 6 years.

Recently, I wanted to see how Gemini 4 (running in Antigravity with the google-cloud-developer plugin) would handle bringing this 6-year-old project back to life.

Not only did Gemini 4 upgrade the entire backend and frontend and deploy everything to Google Cloud with a single prompt, it also helped me transform the app with a flurry of new features in just a few hours.

Here is the story of how it went.

Assessing 6 years of tech debt

Before touching any code, I pointed Gemini 4 at the repository and asked it to assess how outdated the stack was.

Prompt:

Here's an app I built a while back with a microservices architecture on the backend 
and a Flutter frontend. Take a look and let me know the versions of C#/.NET and 
Flutter used and how outdated they are.

Gemini came back with a breakdown of what 6 years in software looks like.

Gemini’s response:

### 1. Backend: C# / .NET (Amathus)
• Framework Version: .NET Core 3.1 (netcoreapp3.1)
    • Configured across all 5 projects: Amathus.Common.csproj, Amathus.Web.csproj,
      Amathus.Reader.csproj, Amathus.Converter.csproj, and Amathus.FuncTests.csproj.
    • Docker images use mcr.microsoft.com/dotnet/core/sdk:3.1-alpine and
      mcr.microsoft.com/dotnet/core/aspnet:3.1.
• C# Language Version: C# 8.0 (the default for netcoreapp3.1, using the older
  Program.Main + Startup.cs hosting model in Program.CreateHostBuilder).

#### How outdated is it?
• ~7 major versions behind: .NET Core 3.1 was released in December 2019 and reached
  End of Life (EOL) in December 2022 (nearly 4 years ago).
• Current version: .NET 10.0 (LTS) with C# 14 (you currently have .NET 10.0.301,
  .NET 9.0.202, and .NET 8.0.403 installed locally).
• Key ecosystem gaps:
    • The Docker base image repositories (mcr.microsoft.com/dotnet/core/...) were
      deprecated and moved to mcr.microsoft.com/dotnet/sdk and
      mcr.microsoft.com/dotnet/aspnet starting in .NET 5.0.
    • Key NuGet dependencies in Amathus.Common.csproj are from 2019–2020
      (Google.Cloud.Firestore 1.1.0 vs 3.x+, Google.Cloud.Storage.V1 2.5.0 vs 4.x+,
      Newtonsoft.Json 12.0.3 vs 13.x or built-in System.Text.Json).

### 2. Frontend: Flutter & Dart (AmathusClient)
• Dart SDK Constraint: >=2.7.0 <3.0.0 in pubspec.yaml
• Flutter Version Era: Flutter 1.20 – 1.22 (late 2020, pre-Flutter 2.0)

#### How outdated is it?
• ~6 years / 2 major versions behind: Dart 2.7–2.10 and Flutter 1.x date back to
  2020, before Dart Sound Null Safety (introduced in Dart 2.12 / Flutter 2.0 in
  March 2021) and Dart 3 (May 2023).
• Current version: Flutter 3.47.6 and Dart 3.13.5 (installed on your machine).
• Major breaking changes between then and now:
    1. No Null Safety: Because the SDK constraint in pubspec.yaml has an upper bound
       of <3.0.0, Dart 3+ refuses to compile the code without migrating to sound null
       safety (>=3.0.0).
    2. Removed Flutter Theme & Widget APIs:
       • ThemeData(accentColor: Colors.amber) (removed in Flutter 3.1; replaced by ColorScheme).
       • FlatButton (removed in Flutter 3.0; replaced by TextButton).
    3. Discontinued / Legacy Packages in pubspec.yaml:
       • admob_flutter: ^1.0.1 — discontinued in favor of google_mobile_ads.
       • share: ^0.6.5+4 — discontinued in favor of share_plus.
    4. Android Build Tooling:
       • build.gradle uses Kotlin 1.3.50, Android Gradle Plugin 3.5.0, and the
         shut-down jcenter() repository.

Normally, bridging a 6-year gap across both .NET and Flutter versions, fixing broken RSS feeds and redeploying a multi-service cloud architecture is a multi-weekend project. But this time was different.

From staged approvals to Turbo mode

My initial goal was simple: upgrade all the backend microservices and the Flutter frontend to the latest versions, get them running locally, and deploy them to Google Cloud.

At first, I took the cautious route. I asked Gemini to come up with a staged plan and let me approve and test each stage as the services were upgraded.

However, within 10 minutes, I realized that I was simply rubber-stamping and approving everything Gemini proposed anyway. So I quit the session, started over in Turbo mode in Antigravity and gave Gemini 4 a single prompt to do the entire job autonomously.

Prompt:

Please make a plan to upgrade both the backend and the frontend to the latest versions. 
Save the plan for me to take a look but don't wait for my approval. 

Follow this process during your upgrade:

- Upgrade the .NET backend to the latest, make sure it runs locally. 
- During the upgrade, make sure the news feeds are still active. If some of the 
  news sources do not work, try to find the new links and if you can't, just 
  remove the obsolete news sources.
- Deploy the .NET backend to Google Cloud project. Use events-atamel as my main
  project for this. Test all microservices and update scripts to deploy/test these
  services. 
- Upgrade the Flutter frontend with the latest, make sure it runs locally.
- Deploy a Flutter web frontend for it so I can test the UI afterwards.
- Make sure all READMEs and scripts for local testing and also cloud deployment
  are also updated.
- Once the upgrade is done, do a review of the UI, make sure all the icons and
  text displays properly, images are properly sized and doesn't take too much space. 
  Try to make it look pretty.
- Make all the changes locally without checking into Git. Once finished, I'll do
  the checkin myself once I'm happy with the changes.
- Try to be as autonomous as possible unless you really need my feedback

What Gemini 4 did on its own

Once I hit enter, Gemini 4 went to work. It pulled in the gcloud and cloud-run-basics skills from the google-cloud-developer plugin, created a 5-phase implementation_plan.md and a task.md checklist to track its own progress, and worked through the entire modernization end-to-end without a single intervention from me.

Here is a brief look at the implementation_plan.md it generated:

# Amathus Backend & Frontend Upgrade Plan

## Phase 1: .NET Backend Upgrade (`netcoreapp3.1` → `net10.0`)
1. Project & Package Upgrades: Update <TargetFramework> to net10.0 across all 5 .csproj
   files and upgrade NuGet dependencies (Google.Cloud.Firestore, Google.Cloud.Storage.V1,
   System.ServiceModel.Syndication, etc.).
2. Code & Reliability Improvements: Fix async/await bug in ReadController.cs, update
   FeedReader to use HttpClient, add an image proxy endpoint in Amathus.Web for Flutter
   Web CORS, and update Dockerfiles to .NET 10.0 images.

## Phase 2: Audit & Update RSS News Sources
1. Test all 31 RSS feeds in amathussources.json and their logo URLs.
2. Discover updated RSS/logo URLs on active websites and remove defunct sources.
3. Update Source.cs, FeedConverter.cs, and functional tests, and verify with dotnet test.

## Phase 3: Deploy .NET Backend to Google Cloud (`events-atamel`)
Update Amathus/scripts/ to target events-atamel and deploy amathus-reader,
amathus-converter, and amathus-web to Cloud Run.

## Phase 4: Flutter Frontend Upgrade (`AmathusClient`) & Web Deployment
1. Upgrade pubspec.yaml to Dart 3 / Flutter 3.41 and replace discontinued packages.
2. Migrate to Sound Null Safety and SharedPreferences for Web compatibility.
3. Modernize UI with Material 3, responsive layouts, and deploy amathus-client to Cloud Run.

## Phase 5: Scripts & Documentation Update
Update READMEs and helper scripts, leaving all changes uncommitted for user review.

And the task.md checklist it updated as it completed each phase:

# Amathus Upgrade Tasks

- [x] **Phase 1: .NET Backend Upgrade (`net10.0`)**
  - [x] Upgrade all 5 `.csproj` files from `netcoreapp3.1` to `net10.0` and update NuGet packages
  - [x] Fix async upload bug in `ReadController.cs` and improve `FeedReader` / `DateTolerantXmlTextReader`
  - [x] Update Dockerfiles to `.NET 10.0` images and `appsettings*.json` to `events-atamel`
- [x] **Phase 2: Verify & Update News Feeds (`amathussources.json`)**
  - [x] Audit all 31 RSS feed URLs and logo URLs
  - [x] Find updated RSS/logo URLs for moved sources and remove obsolete sources (17 active feeds verified)
  - [x] Update `Source.cs`, `FeedConverter.cs`, `FeedTest.cs`, and `FeedtemTest.cs`
  - [x] Verify all functional tests pass (`dotnet test` — 35/35 passed) and backend runs locally (`InMemory`)
- [x] **Phase 3: Deploy Backend Microservices to Google Cloud (`events-atamel`)**
  - [x] Inspect existing resources on `events-atamel` (Cloud Run, Cloud Storage, Pub/Sub, Firestore, Cloud Scheduler)
  - [x] Update `Amathus/scripts/` for `events-atamel` and test scripts (`test_services`)
  - [x] Build and deploy `amathus-reader`, `amathus-converter`, and `amathus-web` to Cloud Run
  - [x] End-to-end test all 3 deployed backend microservices
- [x] **Phase 4: Upgrade Flutter Frontend (`AmathusClient`) & Deploy Web App**
  - [x] Upgrade `pubspec.yaml` to Dart 3 / Flutter 3.41 and modern packages
  - [x] Migrate codebase to Sound Null Safety and Web compatibility
  - [x] Redesign & polish UI (icons, typography, properly sized images, cards, responsive web layout)
  - [x] Verify Flutter app locally and deploy Flutter Web frontend (`amathus-client`) to Cloud Run
- [x] **Phase 5: Update READMEs and Scripts**
  - [x] Update `README.md`, `AmathusClient/README.md`, and deployment/testing scripts

Let’s break down what Gemini did in each subtask.

Upgraded the backend from .NET Core 3.1 to .NET 10.0

Gemini updated all 5 .csproj files to net10.0, upgraded all NuGet packages and Dockerfiles, fixed a subtle async void bug in ReadController.cs where Cloud Storage uploads weren’t being awaited, and even added an image proxy endpoint to prevent CORS issues on Flutter Web.

Gemini’s response:

▸ Thought for 22s: The plan is to upgrade the Amathus project's `.csproj` files to a newer target framework
▸ Thought for 35s: Examining the Dockerfiles for the three services, the plan is to update the base image to the .NET 10.0 runtime
▸ Thought for 40s: Build verification confirms successful compilation on the specified .NET version, with no detected errors
...
#### 1. .NET Backend Upgrade (netcoreapp3.1 -> net10.0)
• Upgraded all 5 projects (Amathus.Common.csproj, Amathus.Reader.csproj,
  Amathus.Converter.csproj, Amathus.Web.csproj, Amathus.FuncTests.csproj) to
  .NET 10.0 (net10.0) and updated all NuGet packages (Google.Cloud.Firestore 3.10.0,
  Google.Cloud.Storage.V1 4.13.0, Microsoft.AspNetCore.Mvc.NewtonsoftJson 10.0.0,
  System.ServiceModel.Syndication 10.0.0, MSTest 3.8.2).
• Updated all 3 Dockerfiles to mcr.microsoft.com/dotnet/sdk:10.0 and
  mcr.microsoft.com/dotnet/aspnet:10.0.
• Fixed an async void bug in ReadController.Read (await Task.WhenAll(...) instead
  of feeds.ForEach(async ...)).
• Updated FirestoreFeedStore and CloudStorageSyndFeedStore to use
  GoogleCredential.FromComputeCredential() when running on Cloud Run (K_SERVICE)
  and reuse the StorageClient instance.
• Added ImageProxyController (GET /api/v1/imageproxy?url=...) to proxy remote news
  logos and article thumbnails with CORS and cache headers for Flutter Web.

Six years is an eternity for local newspaper websites. Out of the 31 RSS feeds in amathussources.json, many had changed their feed URLs, switched logo paths, or shut down entirely. Gemini wrote a script to probe every RSS feed and logo URL, discovered the new links on the active newspaper websites, removed defunct sources, and updated the XML parsers and functional tests.

Gemini’s response:

▸ Thought for 37s: Running a script to check the status of all RSS feeds and their logos should provide a current overview
▸ Thought for 22s: 33 out of 39 tests passed immediately, indicating an initial success rate.
▸ Thought for 1m 10s: Initial data retrieval seems successful: the feed fetch completed quickly with all expected items
...
#### 2. RSS News Sources Audit & Update
• Audited all 31 sources in amathussources.json. Updated moved RSS feed URLs and
  HTTPS logo URLs for 17 active sources (cyprustoday, detaykibris, diyalog,
  gazeddakibris, giynik, gundemkibris, haberalkibrisli, halkinsesi, havadis,
  kibrisgazetesi, kibrisgercek, kibrismanset, kibristime, londragazete, vatan,
  yenicag, yeniduzen) and removed defunct/blocked sources.
• Updated DateTolerantXmlTextReader and the feed item converters in
  Amathus.Common/Converter to handle modern <atom:link rel="self">, <enclosure>,
  and <media:content> tags.
• Verified all 35/35 functional tests pass (dotnet test Amathus.sln).

Migrated Flutter 1.x to 3.41 and visually debugged the UI

On the frontend, Gemini migrated AmathusClient from Dart 2.7 to Dart 3 Sound Null Safety and Flutter 3.41 with Material 3, replaced dart:io file storage with SharedPreferences for web compatibility, and containerized the Flutter Web build with Nginx.

Most impressively, after deploying the web app, Gemini opened the live URL, took screenshots to visually inspect its own UI work, and fixed visual issues it spotted — such as placing white transparent newspaper logos on dark contrast badges and fixing clipped card edges.

Gemini’s response:

▸ Thought for 31s: Upgrading the Flutter frontend, `AmathusClient`, begins with updating the Dart SDK constraint in `pubspec.yaml`
▸ Thought for 57s: Implemented image constraints for different feed item types: fixed size thumbnails for compact lists
▸ Thought for 16s: Attempting to capture a screenshot of the specified URL to visually inspect the rendered content
▸ Thought for 41s: It's necessary to investigate why the right edge of cards were clipped in a previous screenshot
▸ Thought for 15s: Successfully built the web application. Proceeding to rebuild and redeploy the client to the cloud environment
...
#### 3. Flutter Frontend Upgrade (AmathusClient) & UI Redesign
• Upgraded pubspec.yaml from Dart 2.7 to Dart 3.11 / Flutter 3.41 with sound null
  safety and web-compatible packages (http, shared_preferences, url_launcher,
  share_plus, flutter_widget_from_html_core, font_awesome_flutter, timeago).
• Replaced dart:io file storage in FeedsStorage with SharedPreferences so custom
  source ordering works across Web, mobile, and desktop.
• Redesigned the UI across FeedsList, FeedListTile, FeedImage, FeedItemListTile,
  FeedItemListTileWide, FeedItemView, AppDrawer, AppBottomNavigationBar,
  SettingsView, and ContactView:
    • Contrast-safe logo badges: Automatically places white-text transparent logos
      (cyprustoday, detaykibris, gundemkibris, haberalkibrisli, kibrisgazetesi,
      londragazete) on a dark slate badge and dark/colored logos on a white badge
      alongside the source name and last updated time.
    • Properly sized images: Constrained article thumbnails (96x76 in source lists,
      124x92 in recent news cards, and 280px max height in article detail view)
      with rounded corners and BoxFit.cover.
    • Responsive layout: Adapts from a 1-column list on mobile to a 2/3-column
      grid on tablet/desktop screens.

Deployed everything to Cloud Run

Using the gcloud and cloud-run-basics skills, Gemini set up the Cloud Storage bucket, Pub/Sub topic and push subscription, Firestore database, and Cloud Scheduler job, and deployed all three backend microservices and the Flutter Web frontend to Cloud Run in events-atamel (europe-west1).

Gemini’s response:

● Read(~/.gemini/config/plugins/google-cloud-developer/skills/gcloud/SKILL.md)
▸ Thought for 18s: Considering the deployment target is Cloud Run, information relevant to "cloud-run-basics" must be retrieved...
▸ Thought for 1m 4s: Creating the storage bucket and Pub/Sub topic are the initial infrastructure steps.
▸ Thought for 13s: Services are now deployed and accessible. Proceeding to configure the necessary infrastructure components
▸ Thought for 26s: Investigating the root cause of the "default credentials not found" error in `amathus-converter`
▸ Thought for 11s: Deployment of the latest `amathus-client` image to Cloud Run in the specified region and project is required
...
Both the .NET backend (Amathus) and the Flutter frontend (AmathusClient) have been
upgraded to the latest versions, verified locally, and deployed to your Google Cloud
project (events-atamel in europe-west1). All changes remain uncommitted in your
local working tree so you can review and commit them when ready.
──────
### Live Cloud Run Services (events-atamel / europe-west1)
 Service           | Role                                        | Live URL
-------------------|---------------------------------------------|----------------------------------------------
 amathus-client    | Flutter Web Frontend                        | amathus-client-1086570528700.europe-west1.run.app
 amathus-web       | Public REST API (/api/v1/feeds,             | amathus-web-1086570528700.europe-west1.run.app
                   | /api/v1/feeditems, /api/v1/imageproxy)      |
 amathus-reader    | Scheduled RSS Feed Reader (amathus-reader-  | amathus-reader-1086570528700.europe-west1.run.app
                   | job every 10m -> gs://amathus-events-       |
                   | atamel-bucket)                              |
 amathus-converter | Pub/Sub Push Converter (amathus-events-     | amathus-converter-1086570528700.europe-west1.run.app
                   | atamel-topic -> Firestore feeds)            |

Going further: A flurry of new features

Seeing Gemini 4 complete a 6-year full-stack upgrade and cloud deployment in one go was earth-shattering. I didn’t want to stop there. Staying in Turbo mode, I kept prompting Gemini to evolve the app into something more (see all commits).

Bilingual UI & navigation cleanup

First, I asked for a few quick UX improvements.

Prompt:

Great. I want you to make minor changes to the UI:

1- Currently the app is in Turkish, but add English as well and a way to toggle
   between Turkish and English in settings.
2- Make "latest news" the main/first tab and "all news" as second. Also, rename
   "all news" to something else. It's basically news from each news source, 
   maybe something like "newspapers"?
3- Change "Contact" page and only include my Twitter: https://twitter.com/meteatamel 
   and my dev email

Gemini’s response:

All three UI updates have been implemented, tested locally, and redeployed to Cloud Run:

• Live Web App URL: https://amathus-client-1086570528700.europe-west1.run.app

### Summary of Changes

1. Turkish & English Localization + Settings Toggle:
    • Added bilingual (tr / en) localization state and persistence via SharedPreferences
      in constants.dart and reactive rebuilding in AmathusApp (including timeago relative
      timestamps in feeditem_view.dart, feed_list_tile.dart, feeditem_list_tile.dart, and
      feeditem_list_tile_wide.dart).
    • Added a Language selector card (Türkçe / English SegmentedButton) at the top of
      SettingsView.
2. Reordered Tabs & Renamed "All News" to "Newspapers" (Gazeteler):
    • Made Latest News (Son Haberler / Latest News, FeedItemsRecentView) the default home
      screen and first tab (index: 0).
    • Made Newspapers (Gazeteler / Newspapers, FeedsView) the second tab (index: 1)
      across AppBottomNavigationBar and AppDrawer.
3. Updated Contact Page:
    • Updated ContactView and constants.dart to remove Facebook and display only your
      Twitter (https://twitter.com/meteatamel) and dev email (atameldev@gmail.com).

Expanding to all of Cyprus: Turkish, Greek, and English

Originally, Amathus only aggregated Turkish-language newspapers from Cyprus. I asked Gemini to research and add news sources from across the entire island of Cyprus in Turkish, Greek, and English, verify that every feed had current articles and working logos, and add language filtering with flag icons.

Prompt:

Currently this news reader app only contains news sources in Turkish
(and some English) from northern Cyprus. I want to expand it to include news
sources from all Cyprus and in Greek as well. Can you do a research on what other
news sources I can add? I'm looking for news sources from Cyprus with RSS feeds
and logos and in Turkish, Greek, or English.

Gemini inspected the XML of every candidate RSS feed, filtered out empty or stale feeds, onboarded 17 new Greek, English, and Turkish feeds (doubling the total to 34 active Cyprus newspapers), and added full Greek (Ελληνικά) UI localization and vector flag icons.

Gemini’s response:

### Summary of Changes

#### 1. Backend (Amathus/) — 34 Cyprus News Sources & Language Support
- amathussources.json: Added all 17 verified news sources (8 Greek: AlphaNews.Live,
  Dialogos, Lemesos Tv, Pafos Press, Philenews, Politis, Sigmalive, ToThemaOnline;
  5 English: Cyprus Mail, Cyprus Today, Financial Mirror, Politis English, T-Vine;
  21 Turkish total) along with a "Language" property ("tr", "el", or "en").
- Source.cs & Feed.cs: Added source ID constants and [FirestoreProperty] public string
  Language { get; set; } so language metadata is persisted in Firestore and returned
  by the API.
- Amathus.Web: Added optional ?language= query parameter filtering to /api/v1/feeds
  and /api/v1/feeditems.

#### 2. Frontend (AmathusClient/) — Trilingual UI & Flag Icons
- Rebranded app from North Cyprus News (KKTC) to Cyprus News (Kıbrıs Haber /
  Ειδήσεις Κύπρου / Cyprus News).
- Added full Greek (Ελληνικά) UI localization and timeago messages alongside Turkish
  and English.
- Created FlagIcon widget rendering crisp vector Turkish, Greek, and British flags
  across all platforms, and added SourceLanguageFilterBar to filter news sources by
  All, Turkish, Greek, or English.

On-demand translation & “Cyprus Bicommunal News”

Having Turkish, Greek, and English newspapers side-by-side naturally begged the next question: what if you can’t read Greek or Turkish? I asked Gemini to add article translation using a Google Cloud service, allow hiding specific newspapers in Settings, rebrand the app to Cyprus Bicommunal News, and even generate a new logo for it.

Prompt:

Thanks. Now, I want a translation feature. For example, if news is in Turkish, 
I want to be able to translate to Greek or English, and if it's Greek, I want to 
be able to translate to Turkish and English, and if it's purely English, then 
translate to Turkish or Greek. I want this button in every news item, ideally 
by the share button on the top, but feel free to find a better place. Not sure 
what you'd use for translation API, but prefer to use Google or Google Cloud 
service and inform me about your decision.
...
I want the app to be called "Cyprus Bicommunal News"... Also, have a way to 
unselect/hide some of the newspapers in settings... And I want a logo for my 
Cyprus Bicommunal News app. Ideally, it should include the map of Cyprus and a 
group of people (men, women, children) and some newspaper theme.

Gemini’s response:

### 1. Translation API Decision: Google Cloud Translation API V2 (translate.googleapis.com)
- Why this service:
  1. Already enabled in events-atamel: translate.googleapis.com was already active on
     your GCP project, and amathus-web authenticates automatically on Cloud Run via
     Application Default Credentials (ADC) — so no API keys are needed or exposed to
     the Flutter client.
  2. HTML-aware translation: Using Google.Cloud.Translation.V2 (TranslateTextAsync for
     Title/Summary and TranslateHtmlAsync for Detail) with in-memory caching.

### 2. Frontend Translation, Newspaper Hiding & Branding
- Added TranslateController and TranslateIconButton offering source-aware target
  translation languages with flag icons (Turkish -> Greek/English, Greek ->
  Turkish/English, English -> Turkish/Greek) and [Source Flag] -> [Translation Flag]
  indicators.
- Added an eye/visibility icon button per newspaper in SettingsView to hide unselected
  newspapers from Latest News and Newspapers.
- Integrated the new Cyprus Bicommunal News logo across the AppBar banners, navigation
  drawer header, web favicon/PWA icons, and Android/iOS/macOS launcher icons.

One-click bulk translation & UI polish

As I played with the live web app, I realized translating articles one by one was too much clicking when browsing headlines. Over a few quick follow-up prompts, I asked Gemini to replace the per-card translate buttons with a single top-right bulk Translate dropdown button and clean up the card layout.

Prompt:

Instead of having translate buttons for each news item in both latest news and 
newspapers pages, just keep the one translate button on top right. Somehow make 
it more obvious that this is a translation button. When translation is in effect,
 still show the to/from flags but move it in place of the translation button of 
 the news item now (left of share button basically). This should simplify UI.

Gemini’s response:

All updates have been applied and deployed to amathus-client:

1. Single Prominent Top-Right Translate Button (BulkTranslateIconButton):
   Styled the top-right translate control as a clear dropdown button showing
   [Translate Icon] Translate ▾ (localized to Çevir / Μετάφραση / Translate), and
   [Translate Icon / Spinner] [Flag] <Language> ▾ when a translation is active.
2. Left Source Dropdown:
   Updated SourceLanguageFilterBar to place the source language dropdown (All,
   Türkçe, Ελληνικά, English) on the left and BulkTranslateIconButton on the right.
3. Removed Per-Item Translate Buttons in Lists:
   Removed the individual translate buttons from each news card on both Latest News
   and Newspaper pages, and placed the [From Flag] → [To Flag] indicator directly to
   the left of the Share button when a translation is active.

The Final Result

Here is what Cyprus Bicommunal News looks like running live on the web today:

Cyprus Bicommunal News
Cyprus Bicommunal News

You can try out the live Flutter Web version on Cloud Run and check out the updated code on GitHub:

Wrapping up

Looking back at my 2020 app modernization blog series, every phase of modernization back then, lifting and shifting, containerizing, moving to serverless, and migrating frameworks took weekends of manual effort and careful troubleshooting.

Fast forward to today: Gemini 4 took a 6-year-old codebase, migrated the backend across 7 major versions of .NET, migrated the frontend from Flutter 1.x to Flutter 3.41 with Sound Null Safety, repaired dozens of broken RSS feeds, deployed 4 microservices to Cloud Run, visually debugged its own UI via screenshots, and then helped me turn a single-language news reader into a trilingual, real-time translated Cyprus Bicommunal News web and mobile app in a matter of hours.

This is just the beginning. With Gemini 4, I have so many ideas on how to improve the app further, such as adding a AI news overview, AI news categorization, a daily AI generated podcast and more. Sky is the limit!

If you have an old side project gathering dust on GitHub, now is a great time to point Gemini 4 at it and see how far you can go.

Great time to be alive as a builder!


See also