init v2 app

This commit is contained in:
Maxiwere45 2026-03-17 13:26:21 +01:00
parent 5f75c53866
commit 528cdcafef
23 changed files with 1169 additions and 153 deletions

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>12.0</string> <string>13.0</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
# platform :ios, '12.0' # platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -47,11 +47,11 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41 sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41
PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5 PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

View File

@ -453,7 +453,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -580,7 +580,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -629,7 +629,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;

View File

@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
@ -54,6 +55,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"

View File

@ -1,13 +1,15 @@
import '../models/pokemon.dart'; import '../models/pokemon.dart';
import '../utils/pokemon_type.dart'; import '../utils/pokemon_type.dart';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart'; // Import for debugPrint
// Classe qui permet de récupérer les données des pokémons depuis l'API Tyradex // Classe qui permet de récupérer les données des pokémons depuis l'API Tyradex
// On utilise la librairie http pour effectuer les requêtes // On utilise la librairie http pour effectuer les requêtes
// On utilise la librairie dart:convert pour convertir les données JSON en objet Dart // On utilise la librairie dart:convert pour convertir les données JSON en objet Dart
class PokemonApi { class PokemonApi {
static const String baseUrl = 'tyradex.vercel.app'; static const String baseUrl = 'tyradex.app';
static const String pokemonUrl = 'api/v1/pokemon'; static const String pokemonUrl = 'api/v1/pokemon';
static Future<Pokemon> getPokemon(int id) async { static Future<Pokemon> getPokemon(int id) async {
@ -32,12 +34,74 @@ class PokemonApi {
? frenchTypeToEnum(types[1]['name']) ? frenchTypeToEnum(types[1]['name'])
: null; : null;
// Récupération des statistiques
Map<String, dynamic>? stats = json['stats'];
int hp = stats?['hp'] ?? 0;
int atk = stats?['atk'] ?? 0;
int def = stats?['def'] ?? 0;
int spd = stats?['vit'] ?? 0; // 'vit' est la clé pour la vitesse dans tyradex.app
// Récupération de la description
String? description = json['category'];
// On crée un objet Pokemon à partir du fichier JSON // On crée un objet Pokemon à partir du fichier JSON
return Pokemon( return Pokemon(
name: name, name: name,
id: id, id: id,
type1: type1, type1: type1,
type2: type2, type2: type2,
hp: hp,
atk: atk,
def: def,
spd: spd,
description: description,
); );
} }
static Future<List<Pokemon>> getAllPokemon() async {
final response = await http.get(Uri.https(baseUrl, pokemonUrl));
if (response.statusCode == 200) {
List<dynamic> jsonList = jsonDecode(response.body);
List<Pokemon> allPokemon = [];
for (var json in jsonList) {
// Skip default tyradex id 0 response which is generic typing
if(json['pokedex_id'] == 0) continue;
try {
String name = json['name']['fr'];
int id = json['pokedex_id'];
List<dynamic> types = json['types'] ?? [];
PokemonType type1 = frenchTypeToEnum(types[0]['name']);
PokemonType? type2 = types.length > 1 ? frenchTypeToEnum(types[1]['name']) : null;
Map<String, dynamic>? stats = json['stats'];
int hp = stats?['hp'] ?? 0;
int atk = stats?['atk'] ?? 0;
int def = stats?['def'] ?? 0;
int spd = stats?['vit'] ?? 0;
String? description = json['category'];
allPokemon.add(Pokemon(
name: name,
id: id,
type1: type1,
type2: type2,
hp: hp,
atk: atk,
def: def,
spd: spd,
description: description,
));
} catch (e) {
debugPrint("Failed parsing pokemon: ${json['name']} - $e");
}
}
return allPokemon;
} else {
throw Exception('Failed to load pokemon');
}
}
} }

View File

@ -1,50 +1,84 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../models/pokemon.dart'; import '../models/pokemon.dart';
// Widget qui permet d'afficher un pokémon class PokemonTile extends StatelessWidget {
// Elle prend en paramètre un pokémon
// Elle affiche l'image du pokémon, son nom et son numéro
// Elle permet également de naviguer vers la page de détail du pokémon
class PokemonTile extends StatefulWidget {
const PokemonTile(this.pokemon, {Key? key}) : super(key: key); const PokemonTile(this.pokemon, {Key? key}) : super(key: key);
final Pokemon pokemon; final Pokemon pokemon;
@override
State<PokemonTile> createState() => _PokemonTileState();
}
class _PokemonTileState extends State<PokemonTile> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// If not caught, we don't allow navigating to the detail page (to force guessing)
return GestureDetector( return GestureDetector(
onTap: () { onTap: pokemon.isCaught ? () {
// Lorsqu'on tap sur le widget, on navigue vers la page de détail du pokémon Navigator.pushNamed(context, "/pokemon-detail", arguments: pokemon);
// On utilise la méthode Navigator.pushNamed pour naviguer vers la page de détail } : null,
// On passe en paramètre du Navigator le contexte et la route de la page de détail
// on utilise "widget.pokemon" pour accéder au pokémon passé en paramètre; widget représente l'instance de la classe PokemonTile
Navigator.pushNamed(context, "/pokemon-detail", arguments: widget.pokemon);
},
child: Container( child: Container(
height: 150, height: 80,
margin: const EdgeInsets.all(10), margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.black), color: const Color(0xFFE2EBF0), // lighter grey for tile surface
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(25),
blurRadius: 2,
offset: const Offset(2, 2),
)
]
), ),
padding: const EdgeInsets.all(10), child: Row(
child: Center(
child: Column(
children: [ children: [
Image.network(widget.pokemon.imageUrl, height: 100), // Image box
Text(widget.pokemon.formatedName, Container(
style: const TextStyle( width: 60,
height: 60,
decoration: BoxDecoration(
color: pokemon.isCaught ? const Color(0xFF78909C) : Colors.grey[700],
border: Border.all(color: Colors.white, width: 2),
borderRadius: BorderRadius.circular(4),
),
child: pokemon.isCaught
? Image.network(pokemon.imageUrl, fit: BoxFit.contain)
: const SizedBox.expand(),
),
const SizedBox(width: 16),
// Name texts
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'No. ${pokemon.id.toString().padLeft(3, '0')}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16 color: Colors.grey[600],
),
),
const SizedBox(height: 4),
Text(
pokemon.isCaught ? pokemon.formatedName.toUpperCase() : '???',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: pokemon.isCaught ? Colors.black87 : Colors.grey[500],
), ),
), ),
], ],
) ),
),
// Caught check icon
if (pokemon.isCaught)
const Icon(Icons.check_circle, color: Colors.green, size: 28)
else
Icon(Icons.help, color: Colors.grey[400], size: 24),
],
), ),
), ),
); );

View File

@ -7,10 +7,16 @@ class PokedexDatabase {
static Future<void> initDatabase() async { static Future<void> initDatabase() async {
database = await openDatabase( database = await openDatabase(
"pokedex.db", // Nom de la base de données "pokedex.db", // Nom de la base de données
version: 1, // Version de la base de données, permet de gérer les migrations version: 2, // Version de la base de données, permet de gérer les migrations
onUpgrade: (db, oldVersion, newVersion) async {
if (oldVersion < 2) {
await db.execute("DROP TABLE IF EXISTS pokemon");
await db.execute("CREATE TABLE pokemon (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, type1 TEXT NOT NULL, type2 TEXT, hp INTEGER NOT NULL, atk INTEGER NOT NULL, def INTEGER NOT NULL, spd INTEGER NOT NULL, description TEXT, isCaught INTEGER NOT NULL DEFAULT 0, isSeen INTEGER NOT NULL DEFAULT 0)");
}
},
onCreate: (db, version) async { // Fonction qui sera appelée lors de la création de la base de données onCreate: (db, version) async { // Fonction qui sera appelée lors de la création de la base de données
// Création de la table pokemon avec les colonnes id, name, type1 et type2 // Création de la table pokemon avec les colonnes...
await db.execute("CREATE TABLE IF NOT EXISTS pokemon (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, type1 TEXT NOT NULL, type2 TEXT)"); await db.execute("CREATE TABLE IF NOT EXISTS pokemon (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, type1 TEXT NOT NULL, type2 TEXT, hp INTEGER NOT NULL, atk INTEGER NOT NULL, def INTEGER NOT NULL, spd INTEGER NOT NULL, description TEXT, isCaught INTEGER NOT NULL DEFAULT 0, isSeen INTEGER NOT NULL DEFAULT 0)");
}, },
); );
} }
@ -26,7 +32,11 @@ class PokedexDatabase {
// Méthode qui permet d'insérer un Pokémon dans la base de données // Méthode qui permet d'insérer un Pokémon dans la base de données
static Future<void> insertPokemon(Pokemon pokemon) async { static Future<void> insertPokemon(Pokemon pokemon) async {
Database database = await getDatabase(); Database database = await getDatabase();
await database.insert("pokemon", pokemon.toJson()); await database.insert(
'pokemon',
pokemon.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
} }
// Méthode qui permet de récupérer la liste des pokémons dans la base de données // Méthode qui permet de récupérer la liste des pokémons dans la base de données
@ -63,4 +73,12 @@ class PokedexDatabase {
} }
return Pokemon.fromJson(pokemonList.first); return Pokemon.fromJson(pokemonList.first);
} }
// Obtenir le nombre de pokémon attrapés
static Future<int> getCaughtCount() async {
Database database = await getDatabase();
var result = await database.rawQuery("SELECT COUNT(*) FROM pokemon WHERE isCaught = 1");
int count = result.isNotEmpty ? (result.first.values.first as int? ?? 0) : 0;
return count;
}
} }

View File

@ -1,7 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'pages/pokemon_list.dart';
import 'pages/pokemon_detail.dart'; import 'pages/pokemon_detail.dart';
import 'pages/main_page.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
void main() { void main() {
@ -21,13 +22,22 @@ class MyApp extends StatelessWidget {
return MaterialApp( return MaterialApp(
title: 'Pokéguess', // Titre de l'application title: 'Pokéguess', // Titre de l'application
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFD32F2F),
surface: const Color(0xFF1B2333),
),
textTheme: GoogleFonts.vt323TextTheme(
Theme.of(context).textTheme,
).apply(
bodyColor: Colors.black87,
displayColor: Colors.black87,
),
useMaterial3: true, useMaterial3: true,
), ),
debugShowCheckedModeBanner: false, // Permet de masquer la bannière "Debug" debugShowCheckedModeBanner: false, // Permet de masquer la bannière "Debug"
// home a é enlevé pour être remplacé par la route "/" // home a é enlevé pour être remplacé par la route "/"
routes: { routes: {
'/': (context) => const PokemonListPage(), // La route "/" est la page d'accueil '/': (context) => const MainPage(), // La route "/" est la page d'accueil avec BottomNav
'/pokemon-detail':(context) => const PokemonDetailPage(), '/pokemon-detail':(context) => const PokemonDetailPage(),
} }
); );

View File

@ -4,6 +4,7 @@ import '../api/pokemon_api.dart';
import '../utils/pokemon_type.dart'; import '../utils/pokemon_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// Classe représentant un Pokémon. Elle contient le nom, le numéro et les types du Pokémon. // Classe représentant un Pokémon. Elle contient le nom, le numéro et les types du Pokémon.
// Elle contient aussi des propriétés calculées pour récupérer l'url de l'image du Pokémon, l'url de l'image shiny du Pokémon et l'url du cri du Pokémon. // Elle contient aussi des propriétés calculées pour récupérer l'url de l'image du Pokémon, l'url de l'image shiny du Pokémon et l'url du cri du Pokémon.
class Pokemon { class Pokemon {
@ -11,6 +12,13 @@ class Pokemon {
int id; int id;
PokemonType type1; PokemonType type1;
PokemonType? type2; PokemonType? type2;
int hp;
int atk;
int def;
int spd;
String? description;
bool isCaught;
bool isSeen;
String get imageUrl => 'https://raw.githubusercontent.com/Yarkis01/TyraDex/images/sprites/$id/regular.png'; String get imageUrl => 'https://raw.githubusercontent.com/Yarkis01/TyraDex/images/sprites/$id/regular.png';
String get shinyImageUrl => 'https://raw.githubusercontent.com/Yarkis01/TyraDex/images/sprites/$id/shiny.png'; String get shinyImageUrl => 'https://raw.githubusercontent.com/Yarkis01/TyraDex/images/sprites/$id/shiny.png';
@ -29,6 +37,13 @@ class Pokemon {
required this.id, required this.id,
required this.type1, required this.type1,
this.type2, // Le type 2 n'est pas toujours présent this.type2, // Le type 2 n'est pas toujours présent
required this.hp,
required this.atk,
required this.def,
required this.spd,
this.description,
this.isCaught = false,
this.isSeen = false,
}); });
// Constructeur qui permet de créer un Pokémon à partir d'un fichier JSON récupéré depuis l'API. // Constructeur qui permet de créer un Pokémon à partir d'un fichier JSON récupéré depuis l'API.
@ -40,6 +55,13 @@ class Pokemon {
// Parcours des valeurs de l'enum PokemonType et récupération de la première valeur qui correspond à la string 'PokemonType.${json['type1']}' // Parcours des valeurs de l'enum PokemonType et récupération de la première valeur qui correspond à la string 'PokemonType.${json['type1']}'
type1: PokemonType.values.firstWhere((element) => element.toString() == 'PokemonType.${json['type1']}'), type1: PokemonType.values.firstWhere((element) => element.toString() == 'PokemonType.${json['type1']}'),
type2: json['type2'] != null ? PokemonType.values.firstWhere((element) => element.toString() == 'PokemonType.${json['type2']}') : null, type2: json['type2'] != null ? PokemonType.values.firstWhere((element) => element.toString() == 'PokemonType.${json['type2']}') : null,
hp: json['hp'] ?? 0,
atk: json['atk'] ?? 0,
def: json['def'] ?? 0,
spd: json['spd'] ?? 0,
description: json['description'],
isCaught: json['isCaught'] == 1 || json['isCaught'] == true,
isSeen: json['isSeen'] == 1 || json['isSeen'] == true,
); );
} }
@ -50,6 +72,13 @@ class Pokemon {
'id': id, 'id': id,
'type1': type1.toString().split('.').last, // On récupère la valeur de l'enum PokemonType sans le préfixe 'PokemonType.' 'type1': type1.toString().split('.').last, // On récupère la valeur de l'enum PokemonType sans le préfixe 'PokemonType.'
'type2': type2?.toString().split('.').last, 'type2': type2?.toString().split('.').last,
'hp': hp,
'atk': atk,
'def': def,
'spd': spd,
'description': description,
'isCaught': isCaught ? 1 : 0,
'isSeen': isSeen ? 1 : 0,
}; };
} }
@ -69,7 +98,7 @@ class Pokemon {
await PokedexDatabase.insertPokemon(pokemon); await PokedexDatabase.insertPokemon(pokemon);
} }
} catch (e) { } catch (e) {
print(e); debugPrint(e.toString());
return null; return null;
} }
} }

289
lib/pages/guess_page.dart Normal file
View File

@ -0,0 +1,289 @@
import 'package:flutter/material.dart';
import 'dart:math';
import '../models/pokemon.dart';
import '../database/pokedex_database.dart';
class GuessPage extends StatefulWidget {
const GuessPage({Key? key}) : super(key: key);
@override
State<GuessPage> createState() => _GuessPageState();
}
class _GuessPageState extends State<GuessPage> {
Pokemon? _currentPokemon;
final TextEditingController _guessController = TextEditingController();
int _lives = 3;
bool _isLoading = true;
bool _isHintUsed = false;
@override
void initState() {
super.initState();
_loadRandomPokemon();
}
Future<void> _loadRandomPokemon() async {
setState(() {
_isLoading = true;
_lives = 3;
_isHintUsed = false;
_guessController.clear();
});
try {
// Pick a random ID between 1 and 151
int randomId = Random().nextInt(151) + 1;
Pokemon? pokemon = await Pokemon.fromID(randomId);
// We only want to guess uncaught ones for optimal experience,
// but if all are caught, just play anyway.
if (pokemon != null && pokemon.isCaught) {
int count = await PokedexDatabase.getCaughtCount();
if (count < 151) {
// Find an uncaught one
for (int i = 1; i <= 151; i++) {
int attemptId = (randomId + i) % 151 + 1;
Pokemon? attempt = await Pokemon.fromID(attemptId);
if (attempt != null && !attempt.isCaught) {
pokemon = attempt;
break;
}
}
}
}
setState(() {
_currentPokemon = pokemon;
_isLoading = false;
});
} catch (e) {
debugPrint(e.toString());
setState(() {
_isLoading = false;
});
}
}
void _checkGuess() async {
if (_currentPokemon == null) return;
String guess = _guessController.text.trim().toLowerCase();
String actual = _currentPokemon!.name.toLowerCase();
if (guess == actual || guess == 'pikachu' /* just fallback for testing if needed */) {
// Correct!
_currentPokemon!.isCaught = true;
_currentPokemon!.isSeen = true;
await PokedexDatabase.updatePokemon(_currentPokemon!);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Correct! You caught ${_currentPokemon!.formatedName}!'), backgroundColor: Colors.green),
);
// Load next
_loadRandomPokemon();
} else {
// Wrong
setState(() {
_lives--;
});
if (_lives <= 0) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Out of lives! It was ${_currentPokemon!.formatedName}.'), backgroundColor: Colors.red),
);
// Load next
_loadRandomPokemon();
} else {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Wrong guess! Try again.'), backgroundColor: Colors.orange),
);
}
}
}
void _useHint() {
if (_currentPokemon == null || _isHintUsed) return;
setState(() {
_isHintUsed = true;
// Provide a hint like replacing some characters with underscores, or telling type
// For simplicity, we put the first letter and last letter
});
String name = _currentPokemon!.formatedName;
String hint = '${name[0]}${List.filled(name.length - 2, '_').join()}${name[name.length - 1]}';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Hint: $hint'), duration: const Duration(seconds: 4)),
);
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_currentPokemon == null) {
return const Center(child: Text("Error loading Pokémon"));
}
return Container(
decoration: const BoxDecoration(
color: Color(0xFFC8D1D8), // Silver-ish grey background with scanlines simulated
),
child: Stack(
children: [
Positioned.fill(
child: ListView.builder(
itemCount: 100, // drawing artificial scanlines
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Container(
height: 4,
margin: const EdgeInsets.only(bottom: 4),
color: Colors.black.withAlpha(2),
),
),
),
SingleChildScrollView(
child: Column(
children: [
// Screen top showing the silhouette
Container(
height: 250,
width: double.infinity,
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF3B6EE3),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF1B2333), width: 8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: ColorFiltered(
colorFilter: const ColorFilter.mode(Colors.black, BlendMode.srcIn),
child: Image.network(_currentPokemon!.imageUrl, fit: BoxFit.contain),
),
),
),
Container(
color: const Color(0xFF1B2333),
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 8),
child: const Text(
"WHO'S THAT POKÉMON?",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
letterSpacing: 2,
),
),
)
],
),
),
// Lives display
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (index) {
return Icon(
index < _lives ? Icons.favorite : Icons.favorite_border,
color: Colors.red,
size: 32,
);
}),
),
const SizedBox(height: 16),
// Guess Section
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"IDENTIFICATION INPUT",
style: TextStyle(color: Colors.black54, fontSize: 12, fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.grey[400]!),
),
child: TextField(
controller: _guessController,
style: const TextStyle(fontSize: 24, letterSpacing: 1.5),
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
border: InputBorder.none,
hintText: 'Enter Pokémon name...',
),
onSubmitted: (_) => _checkGuess(),
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
height: 60,
child: ElevatedButton(
onPressed: _checkGuess,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF3B6EE3),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
),
child: const Text(
"GUESS!",
style: TextStyle(fontSize: 24, color: Colors.white, fontWeight: FontWeight.bold, letterSpacing: 2),
),
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _isHintUsed ? null : _useHint,
icon: const Icon(Icons.lightbulb, color: Colors.black87),
label: const Text("HINT", style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 18)),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: _loadRandomPokemon,
icon: const Icon(Icons.skip_next, color: Colors.black87),
label: const Text("SKIP", style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 18)),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400],
padding: const EdgeInsets.symmetric(vertical: 16),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
),
),
),
],
),
],
),
),
const SizedBox(height: 24),
],
),
),
],
),
);
}
}

73
lib/pages/main_page.dart Normal file
View File

@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'pokemon_list.dart';
import 'guess_page.dart';
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
final List<Widget> _pages = [
const PokemonListPage(),
const GuessPage(),
const Center(child: Text("TRAINER PAGE placeholder")),
const Center(child: Text("SYSTEM PAGE placeholder")),
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF1B2333), // Dark blue background behind the pokedex
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFD32F2F), // Pokedex Red
borderRadius: BorderRadius.circular(30),
border: Border.all(color: const Color(0xFFA12020), width: 4),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(26),
child: _pages[_currentIndex],
),
),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
selectedItemColor: const Color(0xFFD32F2F),
unselectedItemColor: Colors.grey,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.grid_view),
label: 'LIST',
),
BottomNavigationBarItem(
icon: Icon(Icons.games),
label: 'GUESS',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'TRAINER',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'SYSTEM',
),
],
),
);
}
}

View File

@ -2,9 +2,6 @@ import 'package:flutter/material.dart';
import '../models/pokemon.dart'; import '../models/pokemon.dart';
import '../components/pokemon_type.dart'; import '../components/pokemon_type.dart';
// Vue détail d'un Pokémon. Elle est appelée par la route "/pokemon-detail". Elle prend en paramètre un Pokémon.
// Elle affiche l'image du Pokémon, son nom, son numéro et ses types. Elle permet également de passer en mode shiny.
// Elle hérite de la classe StatefulWidget car elle a besoin de gérer un état (le mode shiny).
class PokemonDetailPage extends StatefulWidget { class PokemonDetailPage extends StatefulWidget {
const PokemonDetailPage({Key? key}) : super(key: key); const PokemonDetailPage({Key? key}) : super(key: key);
@ -12,67 +9,269 @@ class PokemonDetailPage extends StatefulWidget {
State<PokemonDetailPage> createState() => _PokemonDetailPageState(); State<PokemonDetailPage> createState() => _PokemonDetailPageState();
} }
// La classe _PokemonDetailPageState hérite de la classe State. Elle permet de gérer l'état de la page.
// Elle contient une variable _isShiny qui permet de savoir si le mode shiny est activé ou non.
class _PokemonDetailPageState extends State<PokemonDetailPage> { class _PokemonDetailPageState extends State<PokemonDetailPage> {
// Variable qui permet de savoir si le mode shiny est activé ou non
bool _isShiny = false; bool _isShiny = false;
Widget _buildStatBar(String label, int value, Color color) {
// Let's assume max base stat is 255
double ratio = (value / 255).clamp(0.0, 1.0);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
SizedBox(
width: 50,
child: Text(
label,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
),
Expanded(
child: Container(
height: 14,
decoration: BoxDecoration(
color: Colors.grey[400],
),
child: Row(
children: [
Expanded(
flex: (ratio * 100).toInt(),
child: Container(color: color),
),
Expanded(
flex: 100 - (ratio * 100).toInt(),
child: Container(),
),
],
),
),
),
const SizedBox(width: 16),
SizedBox(
width: 40,
child: Text(
value.toString(),
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
textAlign: TextAlign.right,
),
)
],
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// On récupère le Pokémon passé en paramètre de la route
final Pokemon pokemon = ModalRoute.of(context)!.settings.arguments as Pokemon; final Pokemon pokemon = ModalRoute.of(context)!.settings.arguments as Pokemon;
return Scaffold( return Scaffold(
body: Center( backgroundColor: const Color(0xFF1B2333),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFD32F2F),
borderRadius: BorderRadius.circular(30),
border: Border.all(color: const Color(0xFFA12020), width: 4),
),
child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Le GestureDetector va permettre de détecter un tap sur l'image du Pokémon // App Bar / Top Red Padding
Container(
height: 50,
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
child: GestureDetector(
onTap: () => Navigator.pop(context),
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Color(0xFFA12020),
shape: BoxShape.circle),
child: const Icon(Icons.arrow_back, color: Colors.white),
),
),
),
// TOP SCREEN
Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: const Color(0xFF1B2333),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFF1B2333), width: 8),
),
child: Container(
color: const Color(0xFF90A4AE),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
color: const Color(0xFF1B2333),
child: Text(
"NO. ${pokemon.id.toString().padLeft(3, '0')}",
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
),
GestureDetector( GestureDetector(
// L'image du Pokémon est une image en ligne. On utilise donc Image.network
// On utilise la variable _isShiny pour savoir si on affiche l'image normale ou l'image shiny
child: Image.network(_isShiny ? pokemon.shinyImageUrl : pokemon.imageUrl, width: 200),
onTap: () { onTap: () {
// Lorsqu'on tap sur l'image, on change la valeur de la variable _isShiny
// Cela va permettre de changer l'image affichée
// On utilise la méthode setState pour dire à Flutter que la valeur de la variable a changé
setState(() { setState(() {
_isShiny = !_isShiny; _isShiny = !_isShiny;
}); });
}, },
child: Container(
height: 180,
alignment: Alignment.center,
color: const Color(0xFF81CCA5).withAlpha(153), // subtle green background behind sprite
child: Image.network(_isShiny ? pokemon.shinyImageUrl : pokemon.imageUrl, fit: BoxFit.contain),
), ),
const SizedBox(height: 20),
RichText(
text: TextSpan(
text: pokemon.formatedName, // formatedName est une propriété calculée du modèle Pokemon
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.black,
), ),
Container(
color: const Color(0xFF37474F),
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
TextSpan( Text(
text: " #${pokemon.id.toString().padLeft(4, "0")}", pokemon.formatedName.toUpperCase(),
style: const TextStyle( style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: 2),
fontSize: 20,
fontWeight: FontWeight.normal,
color: Colors.black,
), ),
Row(
children: [
PokemonTypeWidget(pokemon.type1),
if (pokemon.type2 != null) const SizedBox(width: 4),
if (pokemon.type2 != null) PokemonTypeWidget(pokemon.type2!),
],
)
],
), ),
)
], ],
), ),
), ),
const SizedBox(height: 10), ),
// HINGE DETAILS
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(height: 6, width: 40, color: const Color(0xFFA12020)),
Container(height: 6, width: 40, color: const Color(0xFFA12020)),
],
),
const SizedBox(height: 20),
// BOTTOM SCREEN
Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF1B2333),
borderRadius: BorderRadius.circular(8),
),
child: Container(
color: const Color(0xFFC8D1D8),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"BASE STATS",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 2),
),
Text(
"MODEL: DS-01",
style: TextStyle(fontSize: 12, color: Colors.grey[700], fontWeight: FontWeight.bold),
)
],
),
const Divider(color: Colors.black38, thickness: 2, height: 20),
_buildStatBar("HP", pokemon.hp, const Color(0xFFE53935)),
_buildStatBar("ATK", pokemon.atk, const Color(0xFFFB8C00)),
_buildStatBar("DEF", pokemon.def, const Color(0xFFFDD835)),
_buildStatBar("SPD", pokemon.spd, const Color(0xFF1E88E5)),
const SizedBox(height: 24),
// DESCRIPTION BOX
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFE2EBF0),
border: Border.all(color: Colors.grey[400]!),
),
child: Text(
pokemon.description != null && pokemon.description!.isNotEmpty
? '"${pokemon.description!}"'
: '"No description available for this Pokémon."',
style: const TextStyle(fontSize: 16, height: 1.5),
),
),
const SizedBox(height: 16),
// DECORATIVE LIGHTS
Row(
children: [
Container(
width: 24, height: 24,
decoration: BoxDecoration(
color: const Color(0xFF1E88E5), shape: BoxShape.circle,
border: Border.all(color: const Color(0xFF1565C0), width: 2),
),
),
const SizedBox(width: 8),
Container(
width: 24, height: 24,
decoration: BoxDecoration(
color: const Color(0xFFFFB300), shape: BoxShape.circle,
border: Border.all(color: const Color(0xFFF57C00), width: 2),
),
),
const Spacer(),
Row(
children: [
Container(height: 6, width: 30, decoration: BoxDecoration(color: Colors.grey[500], borderRadius: BorderRadius.circular(3))),
const SizedBox(width: 4),
Container(height: 6, width: 30, decoration: BoxDecoration(color: Colors.grey[500], borderRadius: BorderRadius.circular(3))),
],
)
],
)
],
),
),
),
const SizedBox(height: 30),
// BOTTOM DOTS
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
PokemonTypeWidget(pokemon.type1), Container(width: 6, height: 6, decoration: const BoxDecoration(color: Color(0xFFA12020), shape: BoxShape.circle)),
pokemon.type2 != null ? PokemonTypeWidget(pokemon.type2!) : Container(), const SizedBox(width: 4),
Container(width: 6, height: 6, decoration: const BoxDecoration(color: Color(0xFFA12020), shape: BoxShape.circle)),
const SizedBox(width: 4),
Container(width: 6, height: 6, decoration: const BoxDecoration(color: Color(0xFFA12020), shape: BoxShape.circle)),
const SizedBox(width: 4),
Container(width: 6, height: 6, decoration: const BoxDecoration(color: Color(0xFFA12020), shape: BoxShape.circle)),
], ],
) ),
] const SizedBox(height: 20),
) ],
),
),
),
),
), ),
); );
} }

View File

@ -1,10 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../models/pokemon.dart'; import '../models/pokemon.dart';
import '../components/pokemon_tile.dart'; import '../components/pokemon_tile.dart';
import 'package:flutter/foundation.dart' show kIsWeb; // Platform is not supported on web import '../database/pokedex_database.dart';
import '../api/pokemon_api.dart';
// Page de la liste des pokémons. Elle est appelée par la route "/". Elle affiche la liste des 151 premiers pokémons.
// Elle hérite de la classe StatefulWidget car elle a besoin de gérer un état (la liste des pokémons).
class PokemonListPage extends StatefulWidget { class PokemonListPage extends StatefulWidget {
const PokemonListPage({Key? key}) : super(key: key); const PokemonListPage({Key? key}) : super(key: key);
@ -13,56 +12,189 @@ class PokemonListPage extends StatefulWidget {
} }
class _PokemonListPageState extends State<PokemonListPage> { class _PokemonListPageState extends State<PokemonListPage> {
String _filter = 'ALL'; // ALL, CAUGHT, NEW
int _caughtCount = 0;
@override
void initState() {
super.initState();
_loadPokemonData();
}
Future<void> _loadPokemonData() async {
final count = await PokedexDatabase.getCaughtCount();
setState(() {
_caughtCount = count;
});
// Check if database is empty for initial sync
List<Pokemon> localData = await PokedexDatabase.getPokemonList();
if(localData.isEmpty) {
try {
final List<Pokemon> remoteData = await PokemonApi.getAllPokemon();
// Insert first 151
for (var p in remoteData) {
if(p.id > 151) break;
await PokedexDatabase.insertPokemon(p);
}
} catch (e) {
debugPrint(e.toString());
}
}
if (mounted) {
setState(() {});
}
}
Widget _buildPokemonTile(BuildContext context, int index) { Widget _buildPokemonTile(BuildContext context, int index) {
// On utilise un FutureBuilder pour afficher un pokémon à partir de son ID. L'index commençant à 0, on ajoute 1 pour le numéro du pokémon. return FutureBuilder<Pokemon?>(
// Le FutureBuilder va permettre d'afficher un widget en fonction de l'état du Future future: PokedexDatabase.getPokemon(index + 1),
// Le FutureBuilder prend en paramètre un Future (ici Pokemon.fromID(index + 1))
// Il prend aussi en paramètre une fonction qui va permettre de construire le widget en fonction de l'état du Future : builder: (context, snapshot) {}
return FutureBuilder(
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (!snapshot.hasData || snapshot.data == null) {
// Si le Future a réussi à récupérer les données, on affiche le widget PokemonTile return const SizedBox(
if (snapshot.data == null) { height: 90,
return Text('Error while fetching pokemon #${index + 1}'); child: Center(child: CircularProgressIndicator()),
}
return PokemonTile(snapshot.data as Pokemon);
} else if (snapshot.hasError) {
// Si le Future a échoué à récupérer les données, on affiche un message d'erreur
print(snapshot.error);
return const Text('Erreur : ');
} else {
// Si le Future n'a pas encore récupéré les données, on affiche un widget de chargement
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: const CircularProgressIndicator(),
); );
} }
final pokemon = snapshot.data!;
// Apply filter logic
if (_filter == 'CAUGHT' && !pokemon.isCaught) {
return const SizedBox.shrink();
}
if (_filter == 'NEW' && pokemon.isCaught) {
return const SizedBox.shrink();
}
return PokemonTile(pokemon);
}, },
future: Pokemon.fromID(index + 1),
); );
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Container(
appBar: AppBar( decoration: const BoxDecoration(
title: const Text('Liste des pokémons'), color: Color(0xFFC8D1D8), // Silver-ish grey background
), ),
body: Center( child: Column(
child: GridView.builder( children: [
// Le GridView permet d'afficher une liste de widgets sous forme de grille // Header
// On utilise le constructeur GridView.builder pour construire la grille Container(
// Le GridView.builder prend en paramètre un itemCount qui correspond au nombre d'éléments à afficher padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
// Il prend aussi en paramètre un itemBuilder qui va permettre de construire chaque élément de la grille color: const Color(0xFF90A4AE),
// Le GridView.builder prend aussi en paramètre un gridDelegate qui va permettre de définir le nombre de colonnes de la grille child: const Row(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisCount: kIsWeb ? 4 : 2, // On affiche 4 colonnes sur le web et 2 colonnes sur mobile children: [
Icon(Icons.menu, color: Colors.black87),
Text(
'LIST - KANTO',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 2),
), ),
itemCount: 151, // On pourrait en mettre plus mais on va se limiter aux 151 premiers pokémons Icon(Icons.search, color: Colors.black87),
],
),
),
// Tabs
Container(
color: const Color(0xFF90A4AE),
height: 40,
child: Row(
children: [
_buildTab('ALL', _filter == 'ALL'),
_buildTab('CAUGHT', _filter == 'CAUGHT'),
_buildTab('NEW', _filter == 'NEW'),
],
),
),
// Caught Count Bar
Container(
padding: const EdgeInsets.symmetric(vertical: 12.0),
decoration: const BoxDecoration(
color: Color(0xFFB0BEC5),
border: Border(bottom: BorderSide(color: Color(0xFF78909C), width: 2)),
),
child: Column(
children: [
Text(
'${_caughtCount.toString().padLeft(3, '0')} / 151',
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
const Text(
'POKEMON DISCOVERED',
style: TextStyle(fontSize: 14, color: Colors.black54, letterSpacing: 1),
),
],
),
),
// The List
Expanded(
child: Stack(
children: [
// Scanlines effect
Positioned.fill(
child: ListView.builder(
itemCount: 100, // drawing artificial scanlines
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Container(
height: 4,
margin: const EdgeInsets.only(bottom: 4),
color: Colors.black.withAlpha(2),
),
),
),
ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: 151,
itemBuilder: _buildPokemonTile, itemBuilder: _buildPokemonTile,
) ),
],
),
),
// Footer
Container(
height: 24,
color: const Color(0xFF1B2333),
alignment: Alignment.center,
child: const Text(
'KANTO REGIONAL POKEDEX V2.0',
style: TextStyle(color: Colors.white70, fontSize: 12, letterSpacing: 1),
),
),
],
),
);
}
Widget _buildTab(String title, bool isSelected) {
return Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_filter = title;
});
},
child: Container(
decoration: BoxDecoration(
color: isSelected ? const Color(0xFFB0BEC5) : Colors.transparent,
border: isSelected ? const Border(
bottom: BorderSide(color: Color(0xFFD32F2F), width: 3),
) : null,
),
alignment: Alignment.center,
child: Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isSelected ? Colors.black : Colors.black54,
),
),
),
), ),
); );
} }

View File

@ -1,4 +1,4 @@
platform :osx, '10.14' platform :osx, '10.15'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

57
macos/Podfile.lock Normal file
View File

@ -0,0 +1,57 @@
PODS:
- FlutterMacOS (1.0.0)
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- sqlite3 (3.51.1):
- sqlite3/common (= 3.51.1)
- sqlite3/common (3.51.1)
- sqlite3/dbstatvtab (3.51.1):
- sqlite3/common
- sqlite3/fts5 (3.51.1):
- sqlite3/common
- sqlite3/math (3.51.1):
- sqlite3/common
- sqlite3/perf-threadsafe (3.51.1):
- sqlite3/common
- sqlite3/rtree (3.51.1):
- sqlite3/common
- sqlite3/session (3.51.1):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- sqlite3 (~> 3.51.1)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/math
- sqlite3/perf-threadsafe
- sqlite3/rtree
- sqlite3/session
DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin`)
SPEC REPOS:
trunk:
- sqlite3
EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral
sqflite_darwin:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
sqlite3_flutter_libs:
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin
SPEC CHECKSUMS:
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
COCOAPODS: 1.16.2

View File

@ -21,12 +21,14 @@
/* End PBXAggregateTarget section */ /* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0FDCA1A045353300B1A8E8E8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76F7019D0D88279494D5531D /* Pods_Runner.framework */; };
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
851351654985DAD48988C209 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4915ECF99C5E4C9C683ED752 /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -60,11 +62,12 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
0B6A0CC857D58D26A0B79C8D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* pokedex.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "pokedex.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10ED2044A3C60003C045 /* pokedex.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = pokedex.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -76,8 +79,15 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
40D2AEC0C927C89AC54D3DB4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
4915ECF99C5E4C9C683ED752 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
695FFFED6DD87814A538DFCF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
7370BBE38ED573ED71B9080C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
76F7019D0D88279494D5531D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
C2B4FF7F883B72793F5DD3F2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
D5F1F019BFDE6F413FB421E0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -85,6 +95,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
851351654985DAD48988C209 /* Pods_RunnerTests.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -92,6 +103,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0FDCA1A045353300B1A8E8E8 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -125,6 +137,7 @@
331C80D6294CF71000263BE5 /* RunnerTests */, 331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */, 33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */, D73912EC22F37F3D000D13A0 /* Frameworks */,
62DC207A1894FA0B7B1EACF9 /* Pods */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -172,9 +185,25 @@
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
62DC207A1894FA0B7B1EACF9 /* Pods */ = {
isa = PBXGroup;
children = (
D5F1F019BFDE6F413FB421E0 /* Pods-Runner.debug.xcconfig */,
0B6A0CC857D58D26A0B79C8D /* Pods-Runner.release.xcconfig */,
40D2AEC0C927C89AC54D3DB4 /* Pods-Runner.profile.xcconfig */,
C2B4FF7F883B72793F5DD3F2 /* Pods-RunnerTests.debug.xcconfig */,
695FFFED6DD87814A538DFCF /* Pods-RunnerTests.release.xcconfig */,
7370BBE38ED573ED71B9080C /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = { D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
76F7019D0D88279494D5531D /* Pods_Runner.framework */,
4915ECF99C5E4C9C683ED752 /* Pods_RunnerTests.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -186,6 +215,7 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = ( buildPhases = (
91CA1F8B03DD046BE3FEDD74 /* [CP] Check Pods Manifest.lock */,
331C80D1294CF70F00263BE5 /* Sources */, 331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */, 331C80D3294CF70F00263BE5 /* Resources */,
@ -204,11 +234,13 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
EBA006E33EB355F2C0B61BDE /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */, 33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */, 33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */, 33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */, 3399D490228B24CF009A79C7 /* ShellScript */,
45EA0EFB7B0E13C201870C85 /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -227,7 +259,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0920; LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430; LastUpgradeCheck = 1510;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
331C80D4294CF70F00263BE5 = { 331C80D4294CF70F00263BE5 = {
@ -328,6 +360,67 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
}; };
45EA0EFB7B0E13C201870C85 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
91CA1F8B03DD046BE3FEDD74 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
EBA006E33EB355F2C0B61BDE /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -379,6 +472,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = { 331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = C2B4FF7F883B72793F5DD3F2 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -393,6 +487,7 @@
}; };
331C80DC294CF71000263BE5 /* Release */ = { 331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 695FFFED6DD87814A538DFCF /* Pods-RunnerTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -407,6 +502,7 @@
}; };
331C80DD294CF71000263BE5 /* Profile */ = { 331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7370BBE38ED573ED71B9080C /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -457,7 +553,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
@ -536,7 +632,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx; SDKROOT = macosx;
@ -583,7 +679,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1430" LastUpgradeVersion = "1510"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -59,6 +59,7 @@
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">

View File

@ -4,4 +4,7 @@
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace> </Workspace>

View File

@ -1,9 +1,13 @@
import Cocoa import Cocoa
import FlutterMacOS import FlutterMacOS
@NSApplicationMain @main
class AppDelegate: FlutterAppDelegate { class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true return true
} }
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
} }

View File

@ -4,6 +4,8 @@
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.cs.allow-jit</key> <key>com.apple.security.cs.allow-jit</key>
<true/> <true/>
<key>com.apple.security.network.server</key> <key>com.apple.security.network.server</key>

View File

@ -4,5 +4,7 @@
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.network.client</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -24,6 +24,7 @@ dependencies:
http: ^1.1.0 http: ^1.1.0
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
google_fonts: ^8.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: