Google Play Games Leaderboard not updating for new Android players (Flutter + Flame)

9 hours ago 1
ARTICLE AD BOX

I’m having a problem with Google Play Games leaderboards on Android in a Flutter + Flame game, and I can’t figure out what’s going wrong.

Environment

Flutter Flame games_services: ^4.1.1 Android & iOS builds Google Play Games Services (new v2 API)

Android setup

In pubspec.yaml:

dependencies: games_services: ^4.1.1

In android/app/build.gradle:

dependencies { implementation "com.google.android.gms:play-services-games-v2:+" implementation "com.google.android.gms:play-services-auth:21.3.0" implementation "com.google.android.play:integrity:1.6.0" }

Google Play Console setup:

Play Games Services enabled Leaderboard created and published Google Cloud project linked OAuth credentials configured App signed and uploaded correctly Internal / production testing works

Dart class where I implemented the comunication with the game service:

import 'dart:async'; import 'package:games_services/games_services.dart'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'game_data.dart'; // Import your GameData class class GamesServicesController { // Your leaderboard IDs static const String iosLeaderboardId = [iosLeaderboardId]; static const String androidLeaderboardId = [androidLeaderboardId]; // Migration tracking key static const String _migrationCompleteKey = 'score_migration_complete'; static final GamesServicesController _instance = GamesServicesController._internal(); factory GamesServicesController() => _instance; GamesServicesController._internal(); bool _signedIn = false; bool _isInitialized = false; // Public getters bool get signedIn => _signedIn; bool get isInitialized => _isInitialized; /// Initialize games services - call this once at app startup Future<void> initialize() async { if (_isInitialized) return; try { debugPrint('Initializing Games Services...'); await GamesServices.signIn(); _signedIn = await GamesServices.isSignedIn; debugPrint('Games Services signed in: $_signedIn'); // If signed in successfully, sync cloud score and migrate if needed if (_signedIn) { await syncCloudScoreToLocal(); await _migrateOfflineScoreIfNeeded(); } } on Exception catch (e) { debugPrint('Games Services initialization failed: $e'); _signedIn = false; } _isInitialized = true; } /// Sync cloud score down to local storage Future<void> syncCloudScoreToLocal() async { if (!_signedIn) { debugPrint('Cannot sync cloud score - not signed in'); return; } try { // Get the player's score from the cloud int? cloudScore = await GamesServices.getPlayerScore( iOSLeaderboardID: iosLeaderboardId, androidLeaderboardID: androidLeaderboardId, ); if (cloudScore != null && cloudScore > 0) { // Get local score final localScore = await GameData.getBestScore(); // If cloud score is higher, update local score if (cloudScore > localScore) { debugPrint('Syncing cloud score ($cloudScore) to local storage (was $localScore)'); await GameData.saveBestScore(cloudScore); } } } on Exception catch (e) { debugPrint('Failed to sync cloud score to local: $e'); } } /// Migrate offline score to online leaderboard (one-time operation) Future<void> _migrateOfflineScoreIfNeeded() async { try { final prefs = await SharedPreferences.getInstance(); final migrationComplete = prefs.getBool(_migrationCompleteKey) ?? false; if (migrationComplete) { debugPrint('Score migration already completed'); return; } final offlineBestScore = await GameData.getBestScore(); if (offlineBestScore > 0) { debugPrint('Migrating offline score: $offlineBestScore'); // Submit the offline score to the leaderboard await GamesServices.submitScore( score: Score( iOSLeaderboardID: iosLeaderboardId, androidLeaderboardID: androidLeaderboardId, value: offlineBestScore, ), ); debugPrint('Offline score migrated successfully'); } // Mark migration as complete await prefs.setBool(_migrationCompleteKey, true); } on Exception catch (e) { debugPrint('Failed to migrate offline score: $e'); // Don't mark as complete if migration failed } } /// Submit score to leaderboard (enhanced version) Future<void> submitScore(int score) async { // Only submit if signed in if (_signedIn) { try { int? onlinePlayerScore = await GamesServices.getPlayerScore( iOSLeaderboardID: iosLeaderboardId, androidLeaderboardID: androidLeaderboardId, ); if (onlinePlayerScore == null || score > onlinePlayerScore) { await GamesServices.submitScore( score: Score( iOSLeaderboardID: iosLeaderboardId, androidLeaderboardID: androidLeaderboardId, value: score, ), ); } } on Exception catch (e) { debugPrint('Failed to submit score online: $e'); } } } /// Show the platform's leaderboard UI Future<void> showLeaderboard() async { if (!_signedIn) { debugPrint('Cannot show leaderboard - not signed in to Games Services'); return; } try { // Sync with a timeout to avoid long delays await forceSyncOfflineScore().timeout( const Duration(seconds: 2), onTimeout: () { debugPrint('Score sync timed out, showing leaderboard anyway'); }, ); await GamesServices.showLeaderboards( iOSLeaderboardID: iosLeaderboardId, androidLeaderboardID: androidLeaderboardId, ); } on Exception catch (e) { debugPrint('Failed to show leaderboard: $e'); } } /// Sign in manually (if auto sign-in failed) Future<bool> signIn() async { try { await GamesServices.signIn(); _signedIn = await GamesServices.isSignedIn; // If sign-in successful, sync cloud score and try migration if (_signedIn) { await syncCloudScoreToLocal(); await _migrateOfflineScoreIfNeeded(); } return _signedIn; } on Exception catch (e) { debugPrint('Manual sign-in failed: $e'); return false; } } /// Force sync offline scores (useful for debugging or manual sync) Future<void> forceSyncOfflineScore() async { if (!_signedIn) { debugPrint('Cannot sync - not signed in'); return; } final offlineBestScore = await GameData.getBestScore(); if (offlineBestScore > 0) { await submitScore(offlineBestScore); } } /// Reset migration flag (for testing purposes) Future<void> resetMigrationFlag() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove(_migrationCompleteKey); debugPrint('Migration flag reset'); } }

On Android, existing players already present on the leaderboard can:

Submit scores Update their best score correctly

On iOS, everything works as expected:

Both new and existing players appear on the leaderboard Scores are uploaded correctly

The problem

On Android only: New players (who were never on the leaderboard before):

Can play the game Can reach high scores (even higher than existing players)

But their scores never appear on the leaderboard!

Existing Android players continue to update their scores normally

I am really confused here because if this were a code issue, existing Android players should also fail ,or iOS should show the same problem

If Google Cloud / Play Games integration were broken, scores shouldn’t upload for anyone.

But instead, only new Android users are affected.

What could cause Google Play Games leaderboards on Android to accept score submissions from existing players and silently ignore or not display scores from new players?

Any insight or debugging tips would be greatly appreciated.

Read Entire Article