Bermain dengan Flutter dan Gemini AI_ Membuat Aplikasi Pokedex

Bermain dengan Flutter dan Gemini AI: Membuat Aplikasi Pokedex

Selamat datang dalam artikel ini! Kita akan menjelajahi cara membuat aplikasi Pokedex yang interaktif menggunakan Flutter dan Gemini AI. Flutter adalah framework pengembangan UI open-source yang dibuat oleh Google. Ini memungkinkan pengembang untuk membuat aplikasi native cross-platform dengan cepat dan efisien. 

Di sisi lain, Gemini AI menyediakan kapabilitas kecerdasan buatan yang dapat meningkatkan interaktivitas dan fungsi aplikasi Anda. Dengan menggabungkan kekuatan keduanya, Anda akan mampu menciptakan aplikasi Pokedex yang tidak hanya menampilkan informasi lengkap tentang berbagai Pokémon, tetapi juga memberikan pengalaman pengguna yang menyenangkan dan inovatif.

Dalam tutorial ini, kita akan membahas langkah demi langkah proses pengembangan aplikasi Pokedex, mulai dari persiapan awal, desain antarmuka pengguna, integrasi Gemini AI, hingga tahap pengujian. Tujuan dari artikel ini adalah untuk memberikan panduan komprehensif yang dapat diikuti oleh pemula sekalipun. 

💻 Mulai Belajar Pemrograman

Belajar pemrograman di Dicoding Academy dan mulai perjalanan Anda sebagai developer profesional.

Daftar Sekarang

Dengan begitu, Anda akan dapat memahami dasar-dasar Flutter dan Gemini AI, serta menggunakannya untuk menciptakan aplikasi yang fungsional dan menarik. Mari kita mulai petualangan koding ini dan lihat Flutter serta Gemini AI bisa bekerja sama untuk membawa ide Anda menjadi kenyataan!

Persiapan Tools

Sebelum mulai membangun aplikasi Pokedex dengan memanfaatkan teknologi Gemini AI, Anda harus mempersiapkan beberapa hal berikut.

  • Flutter SDK
    Unduh Flutter SDK sesuai dengan perangkat yang kamu gunakan dari situs resmi Flutter.
  • Integrated Development Environment (IDE)
    Visual Studio Code atau Android Studio yang kompatibel dengan Flutter.
  • Emulator Android atau Simulator iOS
    Anda juga perlu mempersiapkan emulator Android atau simulator iOS sesuai dengan preferensi, ya! Jika virtual device tidak memungkinkan, Anda bisa gunakan real device.
  • Gemini AI API key
    Anda bisa dapatkan API key di AI Studio by Google.

Mendesain Fitur Aplikasi Pokedex

Mendesain fitur adalah langkah krusial dalam memastikan aplikasi tidak hanya fungsional, tetapi juga menarik bagi pengguna. Dalam pengembangan aplikasi ini, kita fokus pada dua area utama: halaman utama untuk navigasi dan penampilan awal, serta halaman detail untuk informasi mendalam tentang setiap Pokémon. 

Berikut adalah rancangan untuk halaman utama dan halaman detail yang akan kita implementasikan dalam aplikasi Pokedex.

membuat aplikasi pokedex membuat aplikasi pokedex

Inisialisasi Proyek

Memulai sebuah proyek aplikasi Pokedex dengan Flutter melibatkan beberapa tahapan inisialisasi yang penting, termasuk pengaturan awal, penambahan library yang dibutuhkan, dan setup file konfigurasi, seperti env.json.

  1. Init Project Flutter
    Langkah pertama adalah menginisialisasi proyek Flutter baru. Buka terminal atau cmd pada folder sesuai dengan keinginan, lalu jalankan perintah berikut.
    flutter create pokedex
    cd pokedex

    Perintah “flutter create” akan membuat struktur dasar proyek Flutter di dalam folder pokedex. Setelah itu, pindah ke direktori proyek dengan “cd pokedex”.

  2. Penambahan Library yang Dibutuhkan
    Selanjutnya, tambahkan library atau package yang diperlukan untuk mengimplementasikan fitur-fitur dalam aplikasi Pokedex. Berikut adalah contoh penambahan beberapa library umum yang akan digunakan pada file pubspec.yaml.
    #pubspec.yaml
    dependencies:  cupertino_icons: ^1.0.6  dartz: ^0.10.1  dio: ^5.3.3equatable: ^2.0.5flutter:

    sdk: flutter

    flutter_gemini: ^2.0.4

    go_router: ^14.2.0

    google_fonts: ^6.1.0

    image_picker: ^1.1.2

    provider: ^6.0.5

    Setelah menambahkan library baru di pubspec.yaml, jalankan perintah “flutter pub get” untuk mengunduh dan menginstal library tersebut dalam proyek Flutter.

  3. Setup env.json
    Untuk mengelola konfigurasi, seperti URL endpoint API, kunci API, atau variabel-variabel environment lainnya, buatlah file env.json dalam proyek Flutter. Berikut adalah contoh struktur dasar env.json.
    # env.json
    {
        “API_KEY”: “YOUR_APIKEY”,
        “BASE_URL”: “https://pokeapi.co/api/v2”
    }

    Pastikan untuk menambahkan env.json dalam file .gitignore jika Anda mengelola proyek menggunakan Git, untuk menjaga kerahasiaan informasi sensitif, seperti kunci API.
    Setelah membuat file env.json, kita juga perlu membuat sebuah abstract class sebagai penampung variabel dari env file. Mari kita buat file class AppConfig sebagai berikut.

    # app_config.dart
    abstract class AppConfig {
      static const String apiKey = String.fromEnvironment(‘API_KEY’);
      static const String baseUrl = String.fromEnvironment(‘BASE_URL’);
      static const String baseUrlSvgImages =
          ‘https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/’;
      static const String baseUrlPngImages =
          ‘https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/’;
      static const String baseUrlGifImages =
          ‘https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/’;
    }

    Dengan melakukan inisialisasi proyek ini secara benar, Anda siap untuk melanjutkan ke tahap berikutnya dalam pengembangan aplikasi Pokedex menggunakan Flutter. Pastikan konsisten dalam menambahkan library dan mengelola konfigurasi proyek untuk memudahkan pengembangan selanjutnya.

Pembuatan Aplikasi Pokedex

Setelah berhasil melakukan setup awal proyek Flutter, langkah selanjutnya adalah memulai pembangunan aplikasi Pokedex dengan mengimplementasikan fitur-fitur utama. Berikut adalah langkah-langkah yang perlu dilakukan untuk memulai pembuatan aplikasi.

Pengembangan Antarmuka Pengguna (UI)

Pengembangan antarmuka pengguna (UI) aplikasi Pokedex mengikuti desain yang telah direncanakan sebelumnya untuk memastikan konsistensi dan kejelasan visual. 

Halaman utama dirancang dengan memanfaatkan Scaffold sebagai kerangka antarmuka pengguna dan memanfaatkan widget lain, seperti Container, Stack, Positioned, dan beberapa widget lainnya. Tombol pencarian akan meminta gemini AI untuk mendeteksi gambar Pokémon yang dipilih oleh pengguna dan menampilkan card hasil pencarian.

Halaman utama pokedex

Halaman detail Pokémon dirancang dengan layout yang terstruktur, memanfaatkan widget Container, Column, dan Row untuk menampilkan informasi detail secara informatif. Setiap halaman detail Pokémon menampilkan gambar besar, nama lengkap, jenis, dan statistik Pokémon dengan tata letak yang estetis.

Halaman detail Pokedex

Implementasi Fitur-Fitur Utama

Setelah antarmuka pengguna (UI) aplikasi Pokedex dibuat sesuai dengan desain yang telah direncanakan, langkah selanjutnya adalah menghubungkan setiap elemen UI dengan logika aplikasi menggunakan class model, view model dengan Provider, serta mengatur navigasi dengan Go Router.

Pada tahap ini, kita akan membuat class model untuk Pokémon dan detailnya. Berikut adalah penulisan kode untuk model Pokémon yang kita butuhkan.

​​class Pokemon {
  final String name;
  final String url;
  final String color;
  final ColorScheme? colorScheme;

  String get imageUrl => “${AppConfig.baseUrlPngImages}$id.png”;
  String get gifUrl => “${AppConfig.baseUrlGifImages}$id.gif”;
  String get svgUrl => “${AppConfig.baseUrlSvgImages}$id.svg”;

  String get id => url.split(‘/’).where((element) => element.isNotEmpty).last;

  Pokemon({
    required this.name,
    required this.url,
    this.color = “”,
    this.colorScheme = const ColorScheme.dark(),
  });

  Pokemon copyWith({
    String? name,
    String? url,
    String? color,
    ColorScheme? colorScheme,
  }) {
    return Pokemon(
      name: name ?? this.name,
      url: url ?? this.url,
      color: color ?? this.color,
      colorScheme: colorScheme ?? this.colorScheme,
    );
  }
  factory Pokemon.fromJson(Map<String, dynamic> json) => Pokemon(
        name: json[“name”],
        url: json[“url”],
      );

  Map<String, dynamic> toJson() => {
        “name”: name,
        “url”: url,
      };

}

 

class DetailPokemon {
  final List<Ability> abilities;
  final int baseExperience;
  final List<Species> forms;
  final int height;
  final int id;
  final bool isDefault;
  final String locationAreaEncounters;
  final List<Move> moves;
  final String name;
  final int order;
  final Species species;
  final List<Stat> stats;
  final List<Type> types;
  final List<Pokemon> evolutionChain;
  final PokemonSpecies? pokemonSpecies;
  final int weight;
  String get idEvolution =>
      species.url.split(‘/’).where((element) => element.isNotEmpty).last;
  String get idSpecies =>
      species.url.split(‘/’).where((element) => element.isNotEmpty).last;
  String get imageUrl => “${AppConfig.baseUrlPngImages}$id.png”;
  String get gifUrl => “${AppConfig.baseUrlGifImages}$id.gif”;
  String get svgUrl => “${AppConfig.baseUrlSvgImages}$id.svg”;

  DetailPokemon({
    required this.abilities,
    required this.baseExperience,
    required this.forms,
    required this.height,
    required this.id,
    required this.isDefault,
    required this.locationAreaEncounters,
    required this.moves,
    required this.name,
    required this.order,
    required this.species,
    required this.stats,
    required this.types,
    required this.weight,
    this.evolutionChain = const [],
    this.pokemonSpecies,
  });
}

Setelah membuat class model, kita membuat viewmodel yang berperan sebagai business logic dari aplikasi.

class MainViewmodel extends ChangeNotifier {
  MainViewmodel._internal();
  static final _singleton = MainViewmodel._internal();
  factory MainViewmodel() => _singleton;
  final _repository = PokemonRepository();

  AppState<DetailPokemon> _appState = AppState();
  AppState<DetailPokemon> get appState => _appState;

  Future<void> fetchDetailPokemon({required String id}) async {
    _appState = _appState.copyWith(screenState: ScreenStatus.loading);
    notifyListeners();
    final response = await _repository.fetchDetailPokemon(id: id);
    response.fold(
      (failure) {
        _appState = _appState.copyWith(
          screenState: ScreenStatus.error,
          errorMessage: failure.message,
        );
        notifyListeners();
      },
      (data) {
        _appState = _appState.copyWith(
            screenState: ScreenStatus.success, data: data, errorMessage: null);
        notifyListeners();
      },
    );
  }

  File? _image;
  File? get image => _image;
  set image(File? value) {
    _image = value;
    notifyListeners();
  }

  AppState<Candidates> _findState = AppState<Candidates>();
  AppState<Candidates> get findState => _findState;

  void findPokemon({required File image}) async {
    if (_findState.isLoading) return;
    _findState = _findState.copyWith(screenState: ScreenStatus.loading);
    notifyListeners();
    final response = await _repository.findPokemon(image: image);
    response.fold((failure) {
      _findState = _findState.copyWith(
        screenState: ScreenStatus.error,
        errorMessage: failure.message,
      );
    }, (data) {
      _findState = _findState.copyWith(
        screenState: ScreenStatus.success,
        data: data,
        errorMessage: null,
      );
    });
    notifyListeners();
  }
}

Selanjutnya, kita perlu membuat navigasi antar halaman agar pengguna dapat menggunakan semua fitur aplikasi yang dibuat.

final routeConfig = GoRouter(initialLocation: ‘/’, routes: [
  GoRoute(
    path: ‘/’,
    builder: (context, state) => const HomeScreen(),
  ),
  GoRoute(
    path: ‘/:id’,
    pageBuilder: (context, state) {
      // slide animation
      return CustomTransitionPage(
          child: DetailScreen(
            id: state.pathParameters[‘id’],
          ),
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            return SlideTransition(
              position: Tween<Offset>(
                begin: const Offset(1, 0),
                end: Offset.zero,
              ).animate(animation),
              child: child,
            );
          });
    },
  ),
]);

Berikutnya, kita perlu mendaftarkan viewmodel dan navigation ke Material App. Anda bisa mengikuti kode berikut untuk mendaftarkan viewmodel dan navigation.

return ChangeNotifierProvider(
      create: (context) => MainViewmodel(),
      child: MaterialApp.router(
        title: ‘Pokedex’,
        theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: fromHex(“#6C79DB”)),
            useMaterial3: true,
            textTheme: GoogleFonts.poppinsTextTheme()),
        routerConfig: routeConfig,
      ),
    );

Dengan mengimplementasikan class model, view model dengan Provider, serta navigasi menggunakan Go Router, aplikasi Pokedex dapat dihubungkan secara baik antara UI, logika bisnis, dan navigasi pengguna. Ini memungkinkan aplikasi berjalan secara efisien dan fleksibel sesuai dengan kebutuhan pengembangan serta pengalaman pengguna.

Integrasi API dan Integrasi Gemini AI

Setelah berhasil menghubungkan tiap halaman serta mengimplementasikan fitur utama dari aplikasi Pokedex yang kita buat, langkah selanjutnya adalah mengintegrasikan aplikasi dengan API Gemini AI dan API dari pokeapi.co. 

Integrasi ini akan memungkinkan aplikasi dalam memperoleh data Pokémon secara dinamis dan memanfaatkan kecerdasan buatan untuk fitur yang lebih interaktif.

Integrasi API pokeapi.co

Pertama-tama, kita akan mengintegrasikan aplikasi dengan API dari pokeapi.co. API ini menyediakan data lengkap mengenai Pokémon, termasuk nama, jenis, gambar, dan statistik. Berikut adalah langkah-langkah implementasi untuk mengambil data Pokémon dari API pokeapi.co.

  1. Setup Package HTTP/Dio: Gunakan package, seperti http atau dio untuk melakukan permintaan HTTP ke endpoint API pokeapi.co.
  2. Pengambilan Data Pokémon: Buat fungsi atau metode dalam view model untuk mengambil data Pokémon dari API. Contohnya, mengambil detail Pokémon berdasarkan ID.
    class PokemonDatasources {
      final Dio _dio = Dio();

      Future<Map<String, dynamic>> getPokemonDetails(int id) async =>
    tryRequest(() async {
            final response = await get(“/pokemon/$id”);
            final pokemon = DetailPokemon.fromJson(response.data);
            final species = await fetchSpecies(id: pokemon.idSpecies);

            return pokemon.copyWith(
              pokemonSpecies: species,
              evolutionChain: await fetchEvolutionChain(id: species.idEvo),
            );
          });
      Future<PokemonSpecies> fetchSpecies({required String id}) =>
          tryRequest(() async {
            final response = await get(“/pokemon-species/$id”);
            final species = PokemonSpecies.fromJson(response.data);
            return species;
          });
      Future<List<Pokemon>> fetchEvolutionChain({required int id}) =>
          tryRequest(() async {
            final response = await get(“/evolution-chain/$id”);
            final evolutionChain = EvolutionChain.fromJson(response.data);

            return await Future.wait<Pokemon>(evolutionChain.speciesList
                .map((e) => e.fetchColorScheme())
                .toList());
          });

    }
  3. Pemrosesan Data: Setelah mendapatkan respons dari API lalu menyimpannya sebagai objek dari class pokemon, data akan dikirimkan ke viewmodel untuk ditampilkan oleh antarmuka pengguna yang telah dibuat sebelumnya.

Integrasi Gemini AI

Selain mengintegrasikan dengan API pokeapi.co, kita juga akan memanfaatkan Gemini AI untuk fitur-fitur yang lebih canggih, seperti pengenalan gambar atau pencarian suara Pokémon. Berikut adalah langkah-langkah integrasi dengan Gemini AI.

  1. Setup Library Gemini AI: Kamu bisa mengikuti dokumentasi Flutter Gemini.
  2. Implementasi Fitur Pengenalan Gambar: Buat fungsi atau metode dalam aplikasi yang memanfaatkan Gemini AI untuk pengenalan gambar Pokémon. Contoh sederhana sebagai berikut.
    import ‘dart:io’;
    import ‘package:flutter_gemini/flutter_gemini.dart’;

    class GeminiService {
      final Gemini client = Gemini.instance;
      GeminiService();
      Future<Candidates> findPokemon({
        required File image,
      }) async {
        final response = await client.textAndImage(
          text:
              “if this image doesn’t contain any single pokemon give response \”pokemon not found\”, if any pokemon give short description about one pokemon in this picture start with name of selected pokemon”,
          images: [
            image.readAsBytesSync(),
          ],
        );
        if (response != null) {
          return response;
        }
        throw Exception(“Failed to find pokemon”);
      }
    }
  3. Pemrosesan Data: Setelah mendapatkan respons dari Gemini API, data akan dikirimkan ke viewmodel untuk ditampilkan pada antarmuka pengguna yang telah dibuat sebelumnya.

Pengujian Fitur Utama

Setelah fitur aplikasi selesai dibuat, kita akan mengecek bahwa fitur sudah berjalan dengan baik atau tidak. Kita akan menjalankan aplikasi dengan command berikut.

flutter run -dart-define-from-file=env.json

Berikut adalah rekaman dari aplikasi yang berhasil dijalankan oleh penulis.

Pokedex

Setelah dijalankan, lihat bahwa aplikasi Pokedex dapat berjalan dengan baik, serta mampu menyajikan informasi terkait Pokémon secara mudah dan detail.

Kesimpulan

Artikel ini membahas pembuatan aplikasi Pokedex dengan Flutter dan Gemini AI. Penulis mengintegrasikan API pokeapi.co untuk data Pokémon dan Gemini AI untuk fitur pengenalan gambar. 

Untuk melihat kode lengkap proyek, kunjungi repositori Github Pokedex. Terima kasih telah mengikuti artikel ini, yakni tutorial membuat aplikasi Flutter dengan memanfaatkan Gemini AI.


Belajar Pemrograman Gratis
Belajar pemrograman di Dicoding Academy dan mulai perjalanan Anda sebagai developer profesional.