feat: Introduce PokemonImage component for robust image loading with fallback and shiny support, and update iOS platform to 15.0.
This commit is contained in:
parent
8cca5a64de
commit
4faf259aaa
@ -20,7 +20,5 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
@ -24,6 +26,29 @@
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneClassName</key>
|
||||
<string>UIWindowScene</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>flutter</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>FlutterSceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
@ -41,9 +66,5 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
87
lib/components/pokemon_image.dart
Normal file
87
lib/components/pokemon_image.dart
Normal file
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
|
||||
@ -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<GameOverPage> {
|
||||
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(
|
||||
|
||||
@ -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<GuessPage> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -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<PokemonDetailPage> {
|
||||
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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user