unified_game_services_google_play_android 0.1.0 copy "unified_game_services_google_play_android: ^0.1.0" to clipboard
unified_game_services_google_play_android: ^0.1.0 copied to clipboard

PlatformAndroid

Native Google Play Games provider via the Play Games v2 Java SDK over package:jni (pure Dart, no Flutter). Android only; fires native achievement/leaderboard UI.

example/lib/main.dart

import 'dart:async';
import 'dart:ui' show PlatformDispatcher;

import 'package:flutter/material.dart';
import 'package:jni/jni.dart' show JObject;
import 'package:jni_flutter/jni_flutter.dart';
import 'package:unified_game_services_google_play_android/unified_game_services_google_play_android.dart';
import 'package:unified_game_services_platform_interface/unified_game_services_platform_interface.dart';

/// Minimal Flutter host for [GooglePlayAndroidProvider].
///
/// The provider package itself is pure Dart (no Flutter dependency). This app
/// only exists to satisfy the provider's two host requirements on a real
/// device:
///   1. a running ART VM with `package:jni` bound to it (Flutter on Android
///      gives us this for free), and
///   2. the current `Activity` jobject — obtained here with
///      `Jni.getCurrentActivity()` and passed into the provider.
///
/// Reads are not wired yet (see the provider docs: `Task<T>` binding pending),
/// so this demo exercises the write path — the calls that fire the native
/// Play Games toasts/overlay: unlock / increment / reveal / submitScore.
///
/// Before it works on-device you must, in `android/`:
///   - set the real Play Games app id string resource (see AndroidManifest +
///     `app/build.gradle.kts`), and
///   - replace the placeholder achievement / leaderboard ids below with ids
///     from your Play Console game.
void main() => runApp(const DemoApp());

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) => MaterialApp(
    title: 'GPG Android (native) demo',
    theme: ThemeData(colorSchemeSeed: Colors.green, useMaterial3: true),
    home: const HomePage(),
  );
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Replace with ids from your Play Console game.
  static const _achievementId = 'CgkI_REPLACE_ME_ach';
  static const _leaderboardId = 'CgkI_REPLACE_ME_lb';

  GooglePlayAndroidProvider? _provider;
  StreamSubscription<GameServiceEvent>? _eventsSub;
  final List<String> _log = [];

  @override
  void initState() {
    super.initState();
    _init();
  }

  void _init() {
    try {
      // The host Activity jobject — the one piece the provider can't get on
      // its own. jni_flutter resolves it from the running Flutter engine. We
      // pass a *resolver* (not the activity itself): it's volatile and must be
      // fetched fresh, synchronously, before each native call.
      JObject resolveActivity() =>
          androidActivity(PlatformDispatcher.instance.engineId!)!;
      // Probe once so misconfiguration (not on Android) fails clearly here.
      if (androidActivity(PlatformDispatcher.instance.engineId!) == null) {
        _append('init failed: no Android Activity (run on Android)');
        return;
      }
      final provider = GooglePlayAndroidProvider(
        activityResolver: resolveActivity,
      );
      _eventsSub = provider.events.listen(
        (e) => _append('event: ${e.runtimeType}'),
      );
      _provider = provider;
      _append('provider ready (caps: ${provider.capabilities})');
    } catch (e) {
      _append('init failed: $e');
    }
  }

  void _append(String line) {
    if (!mounted) return;
    setState(() => _log.insert(0, line));
  }

  /// Runs [action], logging success or the mapped [GameServiceException].
  Future<void> _run(String label, Future<void> Function() action) async {
    final p = _provider;
    if (p == null) {
      _append('$label skipped — provider not initialized');
      return;
    }
    try {
      await action();
      _append('$label ✓');
    } on GameServiceException catch (e) {
      _append('$label ✗ ${e.runtimeType}: ${e.message}');
    } catch (e) {
      _append('$label ✗ $e');
    }
  }

  @override
  void dispose() {
    _eventsSub?.cancel();
    _provider?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final p = _provider;
    return Scaffold(
      appBar: AppBar(title: const Text('GPG native write demo')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(12),
            child: Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                FilledButton(
                  onPressed: p == null ? null : () => _run('signIn', p.signIn),
                  child: const Text('Sign in'),
                ),
                FilledButton(
                  onPressed: p == null
                      ? null
                      : () => _run(
                          'unlock',
                          () => p.unlockAchievement(_achievementId),
                        ),
                  child: const Text('Unlock achievement'),
                ),
                FilledButton(
                  onPressed: p == null
                      ? null
                      : () => _run(
                          'increment',
                          () => p.incrementAchievement(_achievementId, 1),
                        ),
                  child: const Text('Increment +1'),
                ),
                FilledButton(
                  onPressed: p == null
                      ? null
                      : () => _run(
                          'reveal',
                          () => p.revealAchievement(_achievementId),
                        ),
                  child: const Text('Reveal achievement'),
                ),
                FilledButton(
                  onPressed: p == null
                      ? null
                      : () => _run(
                          'submitScore',
                          () => p.submitScore(
                            leaderboardId: _leaderboardId,
                            score: 1234,
                          ),
                        ),
                  child: const Text('Submit score 1234'),
                ),
              ],
            ),
          ),
          const Divider(height: 1),
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              itemCount: _log.length,
              itemBuilder: (_, i) => Text(
                _log[i],
                style: const TextStyle(fontFamily: 'monospace', fontSize: 13),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
6
downloads
screenshot

Documentation

API reference

Publisher

verified publisheraprimo.dev

Weekly Downloads

Native Google Play Games provider via the Play Games v2 Java SDK over package:jni (pure Dart, no Flutter). Android only; fires native achievement/leaderboard UI.

Repository (GitHub)
View/report issues

Topics

#game-services #achievements #leaderboards #google-play-games #android

License

MIT (license)

Dependencies

jni, unified_game_services_platform_interface

More

Packages that depend on unified_game_services_google_play_android