diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 1dc6cf7..391a902 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -20,7 +20,5 @@
????
CFBundleVersion
1.0
- MinimumOSVersion
- 13.0
diff --git a/ios/Podfile b/ios/Podfile
index 620e46e..c7a0964 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '13.0'
+platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -39,5 +39,8 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
+ target.build_configurations.each do |config|
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
+ end
end
end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 10cbf8e..5a56e30 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -6,25 +6,25 @@ PODS:
- 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 (3.52.0):
+ - sqlite3/common (= 3.52.0)
+ - sqlite3/common (3.52.0)
+ - sqlite3/dbstatvtab (3.52.0):
- sqlite3/common
- - sqlite3/fts5 (3.51.1):
+ - sqlite3/fts5 (3.52.0):
- sqlite3/common
- - sqlite3/math (3.51.1):
+ - sqlite3/math (3.52.0):
- sqlite3/common
- - sqlite3/perf-threadsafe (3.51.1):
+ - sqlite3/perf-threadsafe (3.52.0):
- sqlite3/common
- - sqlite3/rtree (3.51.1):
+ - sqlite3/rtree (3.52.0):
- sqlite3/common
- - sqlite3/session (3.51.1):
+ - sqlite3/session (3.52.0):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- - sqlite3 (~> 3.51.1)
+ - sqlite3 (~> 3.52.0)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/math
@@ -56,9 +56,9 @@ SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
- sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
- sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41
+ sqlite3: a51c07cf16e023d6c48abd5e5791a61a47354921
+ sqlite3_flutter_libs: b3e120efe9a82017e5552a620f696589ed4f62ab
-PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
+PODFILE CHECKSUM: 4b015915ec662986b54bf30ab778da63f7dda016
COCOAPODS: 1.16.2
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index b636303..c30b367 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -1,13 +1,16 @@
-import UIKit
import Flutter
+import UIKit
@main
-@objc class AppDelegate: FlutterAppDelegate {
+@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+
+ func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
+ GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
+ }
}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index f3ff95b..3e9f4f2 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -2,6 +2,8 @@
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -24,6 +26,29 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ FlutterSceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,9 +66,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
diff --git a/lib/components/pokemon_image.dart b/lib/components/pokemon_image.dart
new file mode 100644
index 0000000..848b804
--- /dev/null
+++ b/lib/components/pokemon_image.dart
@@ -0,0 +1,87 @@
+import 'package:flutter/material.dart';
+
+class PokemonImage extends StatelessWidget {
+ final String imageUrl;
+ final String? fallbackUrl;
+ final BoxFit fit;
+ final double? width;
+ final double? height;
+ final Color? color;
+ final BlendMode? colorBlendMode;
+
+ const PokemonImage({
+ super.key,
+ required this.imageUrl,
+ this.fallbackUrl,
+ this.fit = BoxFit.contain,
+ this.width,
+ this.height,
+ this.color,
+ this.colorBlendMode,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Image.network(
+ imageUrl,
+ fit: fit,
+ width: width,
+ height: height,
+ color: color,
+ colorBlendMode: colorBlendMode,
+ errorBuilder: (context, error, stackTrace) {
+ // If the primary image fails and we have a fallback, try the fallback
+ if (fallbackUrl != null && fallbackUrl != imageUrl) {
+ return Image.network(
+ fallbackUrl!,
+ fit: fit,
+ width: width,
+ height: height,
+ color: color,
+ colorBlendMode: colorBlendMode,
+ errorBuilder: (context, error, stackTrace) {
+ // If the fallback also fails, show a placeholder
+ return _buildPlaceholder();
+ },
+ );
+ }
+ // No fallback, show placeholder
+ return _buildPlaceholder();
+ },
+ loadingBuilder: (context, child, loadingProgress) {
+ if (loadingProgress == null) return child;
+ return Center(
+ child: CircularProgressIndicator(
+ value: loadingProgress.expectedTotalBytes != null
+ ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
+ : null,
+ ),
+ );
+ },
+ );
+ }
+
+ Widget _buildPlaceholder() {
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(
+ Icons.help_outline,
+ size: (width ?? 40) * 0.5,
+ color: Colors.grey[400],
+ ),
+ if ((width ?? 100) > 60)
+ Text(
+ "Not Found",
+ style: TextStyle(
+ color: Colors.grey[600],
+ fontSize: 10,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/components/pokemon_tile.dart b/lib/components/pokemon_tile.dart
index e9dbeb3..e165173 100644
--- a/lib/components/pokemon_tile.dart
+++ b/lib/components/pokemon_tile.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../models/pokemon.dart';
+import 'pokemon_image.dart';
class PokemonTile extends StatelessWidget {
const PokemonTile(this.pokemon, {Key? key}) : super(key: key);
@@ -41,7 +42,7 @@ class PokemonTile extends StatelessWidget {
borderRadius: BorderRadius.circular(4),
),
child: pokemon.isCaught
- ? Image.network(pokemon.imageUrl, fit: BoxFit.contain)
+ ? PokemonImage(imageUrl: pokemon.imageUrl, fit: BoxFit.contain)
: const SizedBox.expand(),
),
const SizedBox(width: 16),
diff --git a/lib/pages/game_over_page.dart b/lib/pages/game_over_page.dart
index f205685..c8c23bc 100644
--- a/lib/pages/game_over_page.dart
+++ b/lib/pages/game_over_page.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../database/pokedex_database.dart';
+import '../components/pokemon_image.dart';
class GameOverPage extends StatefulWidget {
const GameOverPage({Key? key}) : super(key: key);
@@ -95,7 +96,10 @@ class _GameOverPageState extends State {
if (pokemonImage.isNotEmpty)
SizedBox(
height: 140,
- child: Image.network(pokemonImage, fit: BoxFit.contain),
+ child: PokemonImage(
+ imageUrl: pokemonImage,
+ fit: BoxFit.contain,
+ ),
),
const SizedBox(height: 12),
Text(
diff --git a/lib/pages/guess_page.dart b/lib/pages/guess_page.dart
index 8e19049..c3f0e1a 100644
--- a/lib/pages/guess_page.dart
+++ b/lib/pages/guess_page.dart
@@ -4,6 +4,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../models/pokemon.dart';
import '../database/pokedex_database.dart';
import 'main_page.dart';
+import '../components/pokemon_image.dart';
class GuessPage extends StatefulWidget {
const GuessPage({Key? key}) : super(key: key);
@@ -258,13 +259,17 @@ class _GuessPageState extends State {
child: Padding(
padding: const EdgeInsets.all(16.0),
child: _isGuessed
- ? Image.network(_currentPokemon!.imageUrl, fit: BoxFit.contain)
- : ColorFiltered(
- colorFilter: ColorFilter.mode(
- _isShiny ? Colors.yellow[700]! : Colors.black,
- BlendMode.srcIn
- ),
- child: Image.network(_currentPokemon!.imageUrl, fit: BoxFit.contain),
+ ? PokemonImage(
+ imageUrl: _isShiny ? _currentPokemon!.shinyImageUrl : _currentPokemon!.imageUrl,
+ fallbackUrl: _currentPokemon!.imageUrl,
+ fit: BoxFit.contain,
+ )
+ : PokemonImage(
+ imageUrl: _isShiny ? _currentPokemon!.shinyImageUrl : _currentPokemon!.imageUrl,
+ fallbackUrl: _currentPokemon!.imageUrl,
+ fit: BoxFit.contain,
+ color: _isShiny ? Colors.yellow[700]! : Colors.black,
+ colorBlendMode: BlendMode.srcIn,
),
),
),
diff --git a/lib/pages/pokemon_detail.dart b/lib/pages/pokemon_detail.dart
index 5ff291d..8caea2d 100644
--- a/lib/pages/pokemon_detail.dart
+++ b/lib/pages/pokemon_detail.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import '../models/pokemon.dart';
import '../components/pokemon_type.dart';
+import '../components/pokemon_image.dart';
class PokemonDetailPage extends StatefulWidget {
const PokemonDetailPage({Key? key}) : super(key: key);
@@ -126,7 +127,11 @@ class _PokemonDetailPageState extends State {
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),
+ child: PokemonImage(
+ imageUrl: _isShiny ? pokemon.shinyImageUrl : pokemon.imageUrl,
+ fallbackUrl: _isShiny ? pokemon.imageUrl : null,
+ fit: BoxFit.contain,
+ ),
),
),
Container(