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):

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.
Audited and repaired 6 years of link rot in RSS feeds
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:

You can try out the live Flutter Web version on Cloud Run and check out the updated code on GitHub:
- Live Web App: https://amathus-client-1086570528700.europe-west1.run.app/
- GitHub Repo: https://github.com/meteatamel/amathus
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!