diff --git a/lib/bloc/characters/characters_bloc.dart b/lib/bloc/characters/characters_bloc.dart index 5189ecb6e..3a7259dec 100644 --- a/lib/bloc/characters/characters_bloc.dart +++ b/lib/bloc/characters/characters_bloc.dart @@ -10,6 +10,7 @@ import '../../common/enums/sort_direction_type.dart'; import '../../common/enums/weapon_type.dart'; import '../../models/models.dart'; import '../../services/genshing_service.dart'; +import '../../services/settings_service.dart'; part 'characters_bloc.freezed.dart'; part 'characters_event.dart'; @@ -17,7 +18,9 @@ part 'characters_state.dart'; class CharactersBloc extends Bloc { final GenshinService _genshinService; - CharactersBloc(this._genshinService) : super(const CharactersState.loading()); + final SettingsService _settingsService; + + CharactersBloc(this._genshinService, this._settingsService) : super(const CharactersState.loading()); _LoadedState get currentState => state as _LoadedState; @@ -113,6 +116,7 @@ class CharactersBloc extends Bloc { tempCharacterFilterType: characterFilterType, sortDirectionType: sortDirectionType, tempSortDirectionType: sortDirectionType, + showCharacterDetails: _settingsService.showCharacterDetails, ); } diff --git a/lib/bloc/characters/characters_state.dart b/lib/bloc/characters/characters_state.dart index 327a59f9b..ed1f1f19e 100644 --- a/lib/bloc/characters/characters_state.dart +++ b/lib/bloc/characters/characters_state.dart @@ -6,6 +6,7 @@ abstract class CharactersState with _$CharactersState { const factory CharactersState.loaded({ @required List characters, String search, + @required bool showCharacterDetails, @required List weaponTypes, @required List tempWeaponTypes, @required List elementTypes, diff --git a/lib/bloc/weapons/weapons_bloc.dart b/lib/bloc/weapons/weapons_bloc.dart index d0a38f98f..2ec37318e 100644 --- a/lib/bloc/weapons/weapons_bloc.dart +++ b/lib/bloc/weapons/weapons_bloc.dart @@ -8,6 +8,7 @@ import '../../common/enums/weapon_filter_type.dart'; import '../../common/enums/weapon_type.dart'; import '../../models/models.dart'; import '../../services/genshing_service.dart'; +import '../../services/settings_service.dart'; part 'weapons_bloc.freezed.dart'; part 'weapons_event.dart'; @@ -15,7 +16,9 @@ part 'weapons_state.dart'; class WeaponsBloc extends Bloc { final GenshinService _genshinService; - WeaponsBloc(this._genshinService) : super(const WeaponsState.loading()); + final SettingsService _settingsService; + + WeaponsBloc(this._genshinService, this._settingsService) : super(const WeaponsState.loading()); _LoadedState get currentState => state as _LoadedState; @@ -86,6 +89,7 @@ class WeaponsBloc extends Bloc { tempWeaponFilterType: weaponFilterType, sortDirectionType: sortDirectionType, tempSortDirectionType: sortDirectionType, + showWeaponDetails: _settingsService.showWeaponDetails, ); } diff --git a/lib/bloc/weapons/weapons_state.dart b/lib/bloc/weapons/weapons_state.dart index f51632bf3..c1e1de11e 100644 --- a/lib/bloc/weapons/weapons_state.dart +++ b/lib/bloc/weapons/weapons_state.dart @@ -6,6 +6,7 @@ abstract class WeaponsState with _$WeaponsState { const factory WeaponsState.loaded({ @required List weapons, String search, + @required bool showWeaponDetails, @required List weaponTypes, @required List tempWeaponTypes, @required int rarity, diff --git a/lib/common/styles.dart b/lib/common/styles.dart index f165e19fc..f0b099d81 100644 --- a/lib/common/styles.dart +++ b/lib/common/styles.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; class Styles { static const String appIconPath = 'assets/icon/icon.png'; static final RoundedRectangleBorder cardShape = RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)); - static final RoundedRectangleBorder mainCardShape = RoundedRectangleBorder( - borderRadius: const BorderRadius.only( + static const RoundedRectangleBorder mainCardShape = RoundedRectangleBorder( + borderRadius: BorderRadius.only( bottomLeft: Radius.circular(35), bottomRight: Radius.circular(35), topLeft: Radius.circular(10), diff --git a/lib/main.dart b/lib/main.dart index 9ad73c501..e5bda0b26 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,7 +45,8 @@ class MyApp extends StatelessWidget { BlocProvider( create: (ctx) { final genshinService = getIt(); - return CharactersBloc(genshinService); + final settingsService = getIt(); + return CharactersBloc(genshinService, settingsService); }, ), BlocProvider( @@ -57,7 +58,8 @@ class MyApp extends StatelessWidget { BlocProvider( create: (ctx) { final genshinService = getIt(); - return WeaponsBloc(genshinService); + final settingsService = getIt(); + return WeaponsBloc(genshinService, settingsService); }, ), BlocProvider( diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 39707d41b..fd1f2e112 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -21,6 +21,12 @@ abstract class SettingsService { bool get isFirstInstall; set isFirstInstall(bool itIs); + bool get showCharacterDetails; + set showCharacterDetails(bool show); + + bool get showWeaponDetails; + set showWeaponDetails(bool show); + Future init(); } @@ -29,6 +35,8 @@ class SettingsServiceImpl extends SettingsService { final _accentColorKey = 'AccentColor'; final _appLanguageKey = 'AppLanguage'; final _firstInstallKey = 'FirstInstall'; + final _showCharacterDetailsKey = 'ShowCharacterDetailsKey'; + final _showWeaponDetailsKey = 'ShowWeaponDetailsKey'; bool _initialized = false; @@ -55,12 +63,24 @@ class SettingsServiceImpl extends SettingsService { @override set isFirstInstall(bool itIs) => _prefs.setBool(_firstInstallKey, itIs); + @override + bool get showCharacterDetails => _prefs.getBool(_showCharacterDetailsKey); + @override + set showCharacterDetails(bool show) => _prefs.setBool(_showCharacterDetailsKey, show); + + @override + bool get showWeaponDetails => _prefs.getBool(_showWeaponDetailsKey); + @override + set showWeaponDetails(bool show) => _prefs.setBool(_showWeaponDetailsKey, show); + @override AppSettings get appSettings => AppSettings( appTheme: appTheme, useDarkAmoled: false, accentColor: accentColor, appLanguage: language, + showCharacterDetails: showCharacterDetails, + showWeaponDetails: showWeaponDetails, ); SettingsServiceImpl(this._logger); @@ -96,6 +116,16 @@ class SettingsServiceImpl extends SettingsService { language = AppLanguageType.english; } + if (_prefs.get(_showCharacterDetailsKey) == null) { + _logger.info(runtimeType, 'Character details are shown by default'); + showCharacterDetails = true; + } + + if (_prefs.get(_showWeaponDetailsKey) == null) { + _logger.info(runtimeType, 'Weapon details are shown by default'); + showWeaponDetails = true; + } + _initialized = true; _logger.info(runtimeType, 'Settings were initialized successfully'); } diff --git a/lib/ui/pages/character_page.dart b/lib/ui/pages/character_page.dart index 03e080ad7..6da4f6a20 100644 --- a/lib/ui/pages/character_page.dart +++ b/lib/ui/pages/character_page.dart @@ -1,262 +1,21 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../bloc/bloc.dart'; -import '../../common/enums/element_type.dart'; -import '../../common/enums/weapon_type.dart'; -import '../../common/extensions/element_type_extensions.dart'; -import '../../common/extensions/weapon_type_extensions.dart'; -import '../../common/genshin_db_icons.dart'; -import '../../common/styles.dart'; -import '../../generated/l10n.dart'; -import '../../models/models.dart'; -import '../widgets/characters/character_build_card.dart'; import '../widgets/characters/character_detail.dart'; -import '../widgets/common/element_image.dart'; -import '../widgets/common/item_description.dart'; -import '../widgets/common/item_description_detail.dart'; -import '../widgets/common/loading.dart'; -import '../widgets/common/rarity.dart'; class CharacterPage extends StatelessWidget { - final double imgSize = 28; - final double imgHeight = 550; + const CharacterPage({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: SingleChildScrollView( - child: BlocBuilder( - builder: (context, state) { - return state.map( - loading: (_) => const Loading(), - loaded: (s) => Stack( - fit: StackFit.passthrough, - clipBehavior: Clip.none, - children: [ - _buildTop( - s.name, - s.rarity, - s.fullImage, - s.secondFullImage, - s.elementType, - s.weaponType, - s.region, - s.role, - s.isFemale, - context, - ), - _buildBottom( - s.description, - s.elementType, - s.skills, - s.ascentionMaterials, - s.talentAscentionsMaterials, - s.multiTalentAscentionMaterials ?? [], - s.passives, - s.constellations, - s.builds, - context, - ), - ], - ), - ); - }, - ), - ), - ), - ); - } - - Widget _buildTop( - String name, - int rarity, - String fullImage, - String secondFullImage, - ElementType elementType, - WeaponType weaponType, - String region, - String role, - bool isFemale, - BuildContext context, - ) { - final mediaQuery = MediaQuery.of(context); - final isPortrait = mediaQuery.orientation == Orientation.portrait; - final descriptionWidth = mediaQuery.size.width / (isPortrait ? 1.2 : 2); - //TODO: IM NOT SURE HOW THIS WILL LOOK LIKE IN BIGGER DEVICES - // final padding = mediaQuery.padding; - // final screenHeight = mediaQuery.size.height - padding.top - padding.bottom; - - return Container( - color: elementType.getElementColorFromContext(context), - child: Stack( - fit: StackFit.passthrough, - alignment: Alignment.center, - children: [ - Align( - alignment: Alignment.topRight, - child: Container( - transform: Matrix4.translationValues(60, -30, 0.0), - child: Opacity( - opacity: 0.5, - child: Image.asset( - secondFullImage ?? fullImage, - width: 350, - height: imgHeight, - ), - ), - ), - ), - Align( - alignment: Alignment.topLeft, - child: Image.asset( - fullImage, - width: 340, - height: imgHeight, - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Container( - width: descriptionWidth, - margin: const EdgeInsets.symmetric(horizontal: 30), - child: _buildGeneralCard(name, rarity, elementType, weaponType, region, role, isFemale, context), - ), - ), - Positioned( - top: 0.0, - left: 0.0, - right: 0.0, - child: AppBar(backgroundColor: Colors.transparent, elevation: 0.0), - ), - ], - ), - ); - } - - Widget _buildBottom( - String description, - ElementType elementType, - List skills, - List ascentionMaterials, - List talentAscentionMaterials, - List multiTalentAscentionMaterials, - List passives, - List constellations, - List builds, - BuildContext context, - ) { - final s = S.of(context); - return Card( - margin: const EdgeInsets.only(top: 380, right: 10, left: 10), - shape: Styles.cardItemDetailShape, - child: Padding( - padding: const EdgeInsets.only(top: 10, right: 10, left: 10), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - margin: const EdgeInsets.only(bottom: 10), - child: ItemDescriptionDetail( - title: s.description, - body: Container(margin: const EdgeInsets.symmetric(horizontal: 5), child: Text(description)), - textColor: elementType.getElementColorFromContext(context), - ), - ), - CharacterDetailSkillsCard(elementType: elementType, skills: skills), - if (builds.isNotEmpty) - ItemDescriptionDetail( - title: s.builds, - body: Column( - children: builds - .map((build) => CharacterBuildCard( - isForSupport: build.isForSupport, - elementType: elementType, - weapons: build.weapons, - artifacts: build.artifacts, - )) - .toList(), - ), - textColor: elementType.getElementColorFromContext(context), - ), - CharacterDetailAscentionMaterialsCard(ascentionMaterials: ascentionMaterials, elementType: elementType), - if (talentAscentionMaterials.isNotEmpty) - CharacterDetailTalentAscentionMaterialsCard.withTalents( - talentAscentionMaterials: talentAscentionMaterials, - elementType: elementType, - ), - if (multiTalentAscentionMaterials.isNotEmpty) - CharacterDetailTalentAscentionMaterialsCard.withMultiTalents( - multiTalentAscentionMaterials: multiTalentAscentionMaterials, - elementType: elementType, - ), - CharacterDetailPassiveCard(elementType: elementType, passives: passives), - CharacterDetailConstellationsCard(elementType: elementType, constellations: constellations), - ], - ), + child: Stack( + fit: StackFit.passthrough, + clipBehavior: Clip.none, + children: const [CharacterDetailTop(), CharacterDetailBottom()], + )), ), ); } - - Widget _buildGeneralCard( - String name, - int rarity, - ElementType elementType, - WeaponType weaponType, - String region, - String role, - bool isFemale, - BuildContext context, - ) { - final s = S.of(context); - final theme = Theme.of(context); - final details = Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(name, style: theme.textTheme.headline5.copyWith(fontWeight: FontWeight.bold, color: Colors.white)), - Rarity(stars: rarity, starSize: 25, alignment: MainAxisAlignment.start), - ItemDescription( - title: s.element, - widget: ElementImage.fromType(type: elementType, radius: 12, useDarkForBackgroundColor: true), - useColumn: false, - ), - ItemDescription( - title: s.region, - widget: Text( - region, - style: const TextStyle(color: Colors.white), - ), - useColumn: false, - ), - ItemDescription( - title: s.weapon, - widget: Image.asset(weaponType.getWeaponAssetPath(), width: imgSize, height: imgSize), - useColumn: false, - ), - ItemDescription( - title: s.role, - widget: Text( - role, - style: const TextStyle(color: Colors.white), - ), - useColumn: false, - ), - ItemDescription( - title: s.gender, - widget: Icon(isFemale ? GenshinDb.female : GenshinDb.male, color: isFemale ? Colors.pink : Colors.blue), - useColumn: false, - ), - ], - ); - return Card( - color: elementType.getElementColorFromContext(context).withOpacity(0.1), - elevation: Styles.cardTenElevation, - margin: Styles.edgeInsetAll5, - shape: Styles.cardShape, - child: Padding(padding: Styles.edgeInsetAll10, child: details), - ); - } } diff --git a/lib/ui/pages/characters_page.dart b/lib/ui/pages/characters_page.dart index 267c896a7..cfd3017b2 100644 --- a/lib/ui/pages/characters_page.dart +++ b/lib/ui/pages/characters_page.dart @@ -79,7 +79,7 @@ class _CharactersPageState extends State with AutomaticKeepAlive shape: Styles.modalBottomSheetShape, isDismissible: true, isScrollControlled: true, - builder: (_) => CharacterBottomSheet(), + builder: (_) => const CharacterBottomSheet(), ); } } diff --git a/lib/ui/widgets/artifacts/artifact_card.dart b/lib/ui/widgets/artifacts/artifact_card.dart index 1fa7866f4..a22a1a5b0 100644 --- a/lib/ui/widgets/artifacts/artifact_card.dart +++ b/lib/ui/widgets/artifacts/artifact_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:transparent_image/transparent_image.dart'; import '../../../bloc/bloc.dart'; import '../../../common/extensions/rarity_extensions.dart'; @@ -56,7 +57,12 @@ class ArtifactCard extends StatelessWidget { padding: Styles.edgeInsetAll5, child: Column( children: [ - Image.asset(image, width: imgWidth, height: imgHeight), + FadeInImage( + width: imgWidth, + height: imgHeight, + placeholder: MemoryImage(kTransparentImage), + image: AssetImage(image), + ), if (!withoutDetails) Center( child: Tooltip( diff --git a/lib/ui/widgets/artifacts/artifact_info_card.dart b/lib/ui/widgets/artifacts/artifact_info_card.dart index 6fa5202d0..89e428ad1 100644 --- a/lib/ui/widgets/artifacts/artifact_info_card.dart +++ b/lib/ui/widgets/artifacts/artifact_info_card.dart @@ -48,7 +48,7 @@ class ArtifactInfoCard extends StatelessWidget { final panel = ItemExpansionPanel( title: s.note, body: BulletList(items: considerations), - icon: Icon(Icons.info_outline), + icon: const Icon(Icons.info_outline), isCollapsed: isCollapsed, expansionCallback: expansionCallback, ); diff --git a/lib/ui/widgets/characters/character_ascention_materials.dart b/lib/ui/widgets/characters/character_ascention_materials.dart index 2d92d80b0..2aa911beb 100644 --- a/lib/ui/widgets/characters/character_ascention_materials.dart +++ b/lib/ui/widgets/characters/character_ascention_materials.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:transparent_image/transparent_image.dart'; import '../../../generated/l10n.dart'; @@ -12,7 +13,16 @@ class CharacterAscentionMaterials extends StatelessWidget { @override Widget build(BuildContext context) { final s = S.of(context); - final widgets = images.map((e) => Image.asset(e, width: 20, height: 20)).toList(); + final widgets = images + .map( + (e) => FadeInImage( + height: 20, + width: 20, + placeholder: MemoryImage(kTransparentImage), + image: AssetImage(e), + ), + ) + .toList(); return Tooltip( message: s.ascentionMaterials, child: Wrap( diff --git a/lib/ui/widgets/characters/character_bottom_sheet.dart b/lib/ui/widgets/characters/character_bottom_sheet.dart index 04ee0557b..5488573c0 100644 --- a/lib/ui/widgets/characters/character_bottom_sheet.dart +++ b/lib/ui/widgets/characters/character_bottom_sheet.dart @@ -18,6 +18,8 @@ import '../common/sort_direction_popupmenu_filter.dart'; import '../common/weapons_button_bar.dart'; class CharacterBottomSheet extends StatelessWidget { + const CharacterBottomSheet({Key key}) : super(key: key); + @override Widget build(BuildContext context) { final theme = Theme.of(context); diff --git a/lib/ui/widgets/characters/character_build_card.dart b/lib/ui/widgets/characters/character_build_card.dart index eb4517039..23e0c2328 100644 --- a/lib/ui/widgets/characters/character_build_card.dart +++ b/lib/ui/widgets/characters/character_build_card.dart @@ -85,7 +85,7 @@ class CharacterBuildCard extends StatelessWidget { return SizedBox( height: 120, child: ListView( - physics: const ClampingScrollPhysics(), + physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, children: widgets, ), @@ -130,7 +130,7 @@ class CharacterBuildCard extends StatelessWidget { return SizedBox( height: imgHeight, child: ListView( - physics: const ClampingScrollPhysics(), + physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, children: items, ), @@ -160,7 +160,7 @@ class CharacterBuildCard extends StatelessWidget { final widget = SizedBox( height: imgHeight, child: ListView( - physics: const ClampingScrollPhysics(), + physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, children: childs, ), diff --git a/lib/ui/widgets/characters/character_card.dart b/lib/ui/widgets/characters/character_card.dart index 71e6da1b9..b5fd48333 100644 --- a/lib/ui/widgets/characters/character_card.dart +++ b/lib/ui/widgets/characters/character_card.dart @@ -1,20 +1,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:transparent_image/transparent_image.dart'; import '../../../bloc/bloc.dart'; import '../../../common/enums/element_type.dart'; import '../../../common/enums/weapon_type.dart'; import '../../../common/extensions/element_type_extensions.dart'; import '../../../common/extensions/i18n_extensions.dart'; -import '../../../common/extensions/weapon_type_extensions.dart'; import '../../../common/styles.dart'; import '../../../generated/l10n.dart'; import '../../pages/character_page.dart'; import '../common/comingsoon_new_avatar.dart'; import '../common/element_image.dart'; import '../common/rarity.dart'; -import 'character_ascention_materials.dart'; +import 'character_card_ascention_materials_bottom.dart'; class CharacterCard extends StatelessWidget { final String image; @@ -42,8 +42,6 @@ class CharacterCard extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final s = S.of(context); - final weaponPath = weaponType.getWeaponAssetPath(); - return InkWell( onTap: () => _gotoCharacterPage(context), child: Card( @@ -58,15 +56,11 @@ class CharacterCard extends StatelessWidget { alignment: AlignmentDirectional.topCenter, fit: StackFit.passthrough, children: [ - Container( - alignment: Alignment.center, + FadeInImage( height: 280, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage(image), - fit: BoxFit.cover, - ), - ), + placeholder: MemoryImage(kTransparentImage), + image: AssetImage(image), + fit: BoxFit.cover, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -99,26 +93,10 @@ class CharacterCard extends StatelessWidget { ), ), Rarity(stars: rarity), - IntrinsicHeight( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Flexible( - fit: FlexFit.tight, - flex: 40, - child: Tooltip( - message: s.translateWeaponType(weaponType), - child: Image.asset(weaponPath, height: 50), - ), - ), - Flexible( - fit: FlexFit.tight, - flex: 60, - child: CharacterAscentionMaterials(images: materials), - ) - ], - ), - ) + CharacterCardAscentionMaterialsBottom( + materials: materials, + weaponType: weaponType, + ), ], ), ), @@ -144,7 +122,7 @@ class CharacterCard extends StatelessWidget { } context.read().add(CharacterEvent.loadFromName(name: name)); - final route = MaterialPageRoute(builder: (c) => CharacterPage()); + final route = MaterialPageRoute(builder: (c) => const CharacterPage()); await Navigator.push(context, route); } } diff --git a/lib/ui/widgets/characters/character_card_ascention_materials_bottom.dart b/lib/ui/widgets/characters/character_card_ascention_materials_bottom.dart new file mode 100644 index 000000000..147a30d9a --- /dev/null +++ b/lib/ui/widgets/characters/character_card_ascention_materials_bottom.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:transparent_image/transparent_image.dart'; + +import '../../../bloc/bloc.dart'; +import '../../../common/enums/weapon_type.dart'; +import '../../../common/extensions/i18n_extensions.dart'; +import '../../../common/extensions/weapon_type_extensions.dart'; +import '../../../generated/l10n.dart'; +import '../common/loading.dart'; +import 'character_ascention_materials.dart'; + +class CharacterCardAscentionMaterialsBottom extends StatelessWidget { + final WeaponType weaponType; + final List materials; + + const CharacterCardAscentionMaterialsBottom({ + Key key, + @required this.weaponType, + @required this.materials, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final s = S.of(context); + final weaponPath = weaponType.getWeaponAssetPath(); + return BlocBuilder( + builder: (context, state) { + return state.map( + loading: (_) => const Loading(useScaffold: false), + loaded: (settingsState) => !settingsState.showCharacterDetails + ? Container() + : IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + fit: FlexFit.tight, + flex: 40, + child: Tooltip( + message: s.translateWeaponType(weaponType), + child: FadeInImage( + height: 50, + placeholder: MemoryImage(kTransparentImage), + image: AssetImage(weaponPath), + ), + ), + ), + Flexible( + fit: FlexFit.tight, + flex: 60, + child: CharacterAscentionMaterials(images: materials), + ) + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/widgets/characters/character_detail.dart b/lib/ui/widgets/characters/character_detail.dart index ab19f3b3b..1ff7b8d28 100644 --- a/lib/ui/widgets/characters/character_detail.dart +++ b/lib/ui/widgets/characters/character_detail.dart @@ -1,5 +1,11 @@ export 'character_detail_ascention_materials_card.dart'; +export 'character_detail_bottom.dart'; export 'character_detail_constellations_card.dart'; +export 'character_detail_general_card.dart'; export 'character_detail_passive_card.dart'; export 'character_detail_skills_card.dart'; export 'character_detail_talent_ascention_materials_card.dart'; +export 'character_detail_top.dart'; + +const double imgSize = 28; +const double imgHeight = 550; diff --git a/lib/ui/widgets/characters/character_detail_bottom.dart b/lib/ui/widgets/characters/character_detail_bottom.dart new file mode 100644 index 000000000..6b126a0d5 --- /dev/null +++ b/lib/ui/widgets/characters/character_detail_bottom.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../bloc/bloc.dart'; +import '../../../common/extensions/element_type_extensions.dart'; +import '../../../common/styles.dart'; +import '../../../generated/l10n.dart'; +import '../common/item_description_detail.dart'; +import '../common/loading.dart'; +import 'character_build_card.dart'; +import 'character_detail.dart'; + +class CharacterDetailBottom extends StatelessWidget { + const CharacterDetailBottom({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return BlocBuilder( + builder: (ctx, state) => state.map( + loading: (_) => const Loading(useScaffold: false), + loaded: (state) => Card( + margin: const EdgeInsets.only(top: 380, right: 10, left: 10), + shape: Styles.cardItemDetailShape, + child: Padding( + padding: const EdgeInsets.only(top: 10, right: 10, left: 10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.only(bottom: 10), + child: ItemDescriptionDetail( + title: s.description, + body: Container(margin: const EdgeInsets.symmetric(horizontal: 5), child: Text(state.description)), + textColor: state.elementType.getElementColorFromContext(context), + ), + ), + CharacterDetailSkillsCard(elementType: state.elementType, skills: state.skills), + if (state.builds.isNotEmpty) + ItemDescriptionDetail( + title: s.builds, + body: Column( + children: state.builds + .map((build) => CharacterBuildCard( + isForSupport: build.isForSupport, + elementType: state.elementType, + weapons: build.weapons, + artifacts: build.artifacts, + )) + .toList(), + ), + textColor: state.elementType.getElementColorFromContext(context), + ), + CharacterDetailAscentionMaterialsCard( + ascentionMaterials: state.ascentionMaterials, + elementType: state.elementType, + ), + if (state.talentAscentionsMaterials.isNotEmpty) + CharacterDetailTalentAscentionMaterialsCard.withTalents( + talentAscentionMaterials: state.talentAscentionsMaterials, + elementType: state.elementType, + ), + if (state.multiTalentAscentionMaterials != null && state.multiTalentAscentionMaterials.isNotEmpty) + CharacterDetailTalentAscentionMaterialsCard.withMultiTalents( + multiTalentAscentionMaterials: state.multiTalentAscentionMaterials, + elementType: state.elementType, + ), + CharacterDetailPassiveCard(elementType: state.elementType, passives: state.passives), + CharacterDetailConstellationsCard(elementType: state.elementType, constellations: state.constellations), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/widgets/characters/character_detail_general_card.dart b/lib/ui/widgets/characters/character_detail_general_card.dart new file mode 100644 index 000000000..bf1fb36f1 --- /dev/null +++ b/lib/ui/widgets/characters/character_detail_general_card.dart @@ -0,0 +1,90 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../../../common/enums/element_type.dart'; +import '../../../common/enums/weapon_type.dart'; +import '../../../common/extensions/element_type_extensions.dart'; +import '../../../common/extensions/weapon_type_extensions.dart'; +import '../../../common/genshin_db_icons.dart'; +import '../../../common/styles.dart'; +import '../../../generated/l10n.dart'; +import '../../widgets/characters/character_detail.dart'; +import '../../widgets/common/element_image.dart'; +import '../../widgets/common/item_description.dart'; +import '../../widgets/common/rarity.dart'; + +class CharacterDetailGeneralCard extends StatelessWidget { + final String name; + final int rarity; + final ElementType elementType; + final WeaponType weaponType; + final String region; + final String role; + final bool isFemale; + + const CharacterDetailGeneralCard({ + Key key, + @required this.name, + @required this.rarity, + @required this.elementType, + @required this.weaponType, + @required this.region, + @required this.role, + @required this.isFemale, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final s = S.of(context); + final theme = Theme.of(context); + + return Card( + color: elementType.getElementColorFromContext(context).withOpacity(0.1), + elevation: Styles.cardTenElevation, + margin: Styles.edgeInsetAll5, + shape: Styles.cardShape, + child: Padding( + padding: Styles.edgeInsetAll10, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(name, style: theme.textTheme.headline5.copyWith(fontWeight: FontWeight.bold, color: Colors.white)), + Rarity(stars: rarity, starSize: 25, alignment: MainAxisAlignment.start), + ItemDescription( + title: s.element, + widget: ElementImage.fromType(type: elementType, radius: 12, useDarkForBackgroundColor: true), + useColumn: false, + ), + ItemDescription( + title: s.region, + widget: Text( + region, + style: const TextStyle(color: Colors.white), + ), + useColumn: false, + ), + ItemDescription( + title: s.weapon, + widget: Image.asset(weaponType.getWeaponAssetPath(), width: imgSize, height: imgSize), + useColumn: false, + ), + ItemDescription( + title: s.role, + widget: Text( + role, + style: const TextStyle(color: Colors.white), + ), + useColumn: false, + ), + ItemDescription( + title: s.gender, + widget: Icon(isFemale ? GenshinDb.female : GenshinDb.male, color: isFemale ? Colors.pink : Colors.blue), + useColumn: false, + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/widgets/characters/character_detail_talent_ascention_materials_card.dart b/lib/ui/widgets/characters/character_detail_talent_ascention_materials_card.dart index db1cb514f..d6653940e 100644 --- a/lib/ui/widgets/characters/character_detail_talent_ascention_materials_card.dart +++ b/lib/ui/widgets/characters/character_detail_talent_ascention_materials_card.dart @@ -13,18 +13,18 @@ class CharacterDetailTalentAscentionMaterialsCard extends StatelessWidget { final List talentAscentionMaterials; final List multiTalentAscentionMaterials; - CharacterDetailTalentAscentionMaterialsCard.withTalents({ + const CharacterDetailTalentAscentionMaterialsCard.withTalents({ Key key, @required this.elementType, @required this.talentAscentionMaterials, - }) : multiTalentAscentionMaterials = [], + }) : multiTalentAscentionMaterials = const [], super(key: key); - CharacterDetailTalentAscentionMaterialsCard.withMultiTalents({ + const CharacterDetailTalentAscentionMaterialsCard.withMultiTalents({ Key key, @required this.elementType, @required this.multiTalentAscentionMaterials, - }) : talentAscentionMaterials = [], + }) : talentAscentionMaterials = const [], super(key: key); @override @@ -36,7 +36,11 @@ class CharacterDetailTalentAscentionMaterialsCard extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: [...multiTalentAscentionMaterials.map((e) => _buildTableCard(s.talentAscentionX(e.number), e.materials, context)).toList()], + children: [ + ...multiTalentAscentionMaterials + .map((e) => _buildTableCard(s.talentAscentionX(e.number), e.materials, context)) + .toList() + ], ); } diff --git a/lib/ui/widgets/characters/character_detail_top.dart b/lib/ui/widgets/characters/character_detail_top.dart new file mode 100644 index 000000000..f14993b26 --- /dev/null +++ b/lib/ui/widgets/characters/character_detail_top.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../bloc/bloc.dart'; +import '../../../common/extensions/element_type_extensions.dart'; +import '../common/loading.dart'; +import 'character_detail.dart'; + +class CharacterDetailTop extends StatelessWidget { + const CharacterDetailTop({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final mediaQuery = MediaQuery.of(context); + final isPortrait = mediaQuery.orientation == Orientation.portrait; + final descriptionWidth = mediaQuery.size.width / (isPortrait ? 1.2 : 2); + //TODO: IM NOT SURE HOW THIS WILL LOOK LIKE IN BIGGER DEVICES + // final padding = mediaQuery.padding; + // final screenHeight = mediaQuery.size.height - padding.top - padding.bottom; + + return BlocBuilder( + builder: (ctx, state) => state.map( + loading: (_) => const Loading(useScaffold: false), + loaded: (state) => Container( + color: state.elementType.getElementColorFromContext(context), + child: Stack( + fit: StackFit.passthrough, + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.topRight, + child: Container( + transform: Matrix4.translationValues(60, -30, 0.0), + child: Opacity( + opacity: 0.5, + child: Image.asset( + state.secondFullImage ?? state.fullImage, + width: 350, + height: imgHeight, + ), + ), + ), + ), + Align( + alignment: Alignment.topLeft, + child: Image.asset( + state.fullImage, + width: 340, + height: imgHeight, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: descriptionWidth, + margin: const EdgeInsets.symmetric(horizontal: 30), + child: CharacterDetailGeneralCard( + elementType: state.elementType, + isFemale: state.isFemale, + name: state.name, + rarity: state.rarity, + region: state.region, + role: state.role, + weaponType: state.weaponType, + ), + ), + ), + Positioned( + top: 0.0, + left: 0.0, + right: 0.0, + child: AppBar(backgroundColor: Colors.transparent, elevation: 0.0), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/widgets/common/bullet_list.dart b/lib/ui/widgets/common/bullet_list.dart index 56f8f552a..50a87e13d 100644 --- a/lib/ui/widgets/common/bullet_list.dart +++ b/lib/ui/widgets/common/bullet_list.dart @@ -19,7 +19,7 @@ class BulletList extends StatelessWidget { (e) => ListTile( dense: true, contentPadding: const EdgeInsets.only(left: 10), - visualDensity: const VisualDensity(horizontal: 0, vertical: -4), + visualDensity: const VisualDensity(vertical: -4), leading: const Icon(Icons.fiber_manual_record, size: 15), title: Transform.translate( offset: Styles.listItemWithIconOffset, diff --git a/lib/ui/widgets/common/image_widget_placeholder.dart b/lib/ui/widgets/common/image_widget_placeholder.dart new file mode 100644 index 000000000..c45adaf2f --- /dev/null +++ b/lib/ui/widgets/common/image_widget_placeholder.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class ImageWidgetPlaceholder extends StatelessWidget { + final ImageProvider image; + final Widget placeholder; + + const ImageWidgetPlaceholder({ + Key key, + this.image, + this.placeholder, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Image( + image: image, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded) { + return child; + } else { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + child: frame != null ? child : placeholder, + ); + } + }, + ); + } +} diff --git a/lib/ui/widgets/common/item_description.dart b/lib/ui/widgets/common/item_description.dart index 38c366b5b..5f9598880 100644 --- a/lib/ui/widgets/common/item_description.dart +++ b/lib/ui/widgets/common/item_description.dart @@ -42,7 +42,6 @@ class ItemDescription extends StatelessWidget { return Container( margin: const EdgeInsets.symmetric(vertical: 2), child: Row( - mainAxisAlignment: MainAxisAlignment.start, children: [ Text( '$title: ', diff --git a/lib/ui/widgets/common/item_description_card.dart b/lib/ui/widgets/common/item_description_card.dart index c087267cc..d0cc61d9d 100644 --- a/lib/ui/widgets/common/item_description_card.dart +++ b/lib/ui/widgets/common/item_description_card.dart @@ -22,13 +22,13 @@ class ItemDescriptionCard extends StatelessWidget { margin: Styles.edgeInsetAll10, shape: Styles.cardShape, child: Container( - padding: const EdgeInsets.only(top: 0, bottom: 10, left: 10, right: 10), + padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ ListTile( dense: true, - leading: Icon(Icons.settings), + leading: const Icon(Icons.settings), contentPadding: EdgeInsets.zero, title: Transform.translate( offset: Styles.listItemWithIconOffset, diff --git a/lib/ui/widgets/common/sliver_nothing_found.dart b/lib/ui/widgets/common/sliver_nothing_found.dart index 2de81610f..617914756 100644 --- a/lib/ui/widgets/common/sliver_nothing_found.dart +++ b/lib/ui/widgets/common/sliver_nothing_found.dart @@ -11,7 +11,7 @@ class SliverNothingFound extends StatelessWidget { hasScrollBody: false, child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: [const NothingFound()], + children: const [NothingFound()], ), ); } diff --git a/lib/ui/widgets/home/char_card_ascention_material.dart b/lib/ui/widgets/home/char_card_ascention_material.dart index 896b95274..2c27aee8c 100644 --- a/lib/ui/widgets/home/char_card_ascention_material.dart +++ b/lib/ui/widgets/home/char_card_ascention_material.dart @@ -106,7 +106,7 @@ class CharCardAscentionMaterial extends StatelessWidget { Future _gotoCharacterPage(String image, BuildContext context) async { context.read().add(CharacterEvent.loadFromImg(image: image)); - final route = MaterialPageRoute(builder: (c) => CharacterPage()); + final route = MaterialPageRoute(builder: (c) => const CharacterPage()); await Navigator.push(context, route); } } diff --git a/lib/ui/widgets/materials/sliver_character_ascention_materials.dart b/lib/ui/widgets/materials/sliver_character_ascention_materials.dart index 8e2c6c104..35b06d2e6 100644 --- a/lib/ui/widgets/materials/sliver_character_ascention_materials.dart +++ b/lib/ui/widgets/materials/sliver_character_ascention_materials.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import '../../../models/home/today_char_ascention_materials_model.dart'; import '../home/char_card_ascention_material.dart'; diff --git a/lib/ui/widgets/weapons/weapon_card.dart b/lib/ui/widgets/weapons/weapon_card.dart index 585ffc24e..74a67fe11 100644 --- a/lib/ui/widgets/weapons/weapon_card.dart +++ b/lib/ui/widgets/weapons/weapon_card.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:transparent_image/transparent_image.dart'; import '../../../bloc/bloc.dart'; import '../../../common/enums/stat_type.dart'; @@ -12,6 +13,7 @@ import '../../../common/styles.dart'; import '../../../generated/l10n.dart'; import '../../pages/weapon_page.dart'; import '../common/gradient_card.dart'; +import '../common/loading.dart'; import '../common/rarity.dart'; class WeaponCard extends StatelessWidget { @@ -62,7 +64,12 @@ class WeaponCard extends StatelessWidget { padding: Styles.edgeInsetAll5, child: Column( children: [ - Image.asset(image, width: imgWidth, height: imgHeight), + FadeInImage( + width: imgWidth, + height: imgHeight, + placeholder: MemoryImage(kTransparentImage), + image: AssetImage(image), + ), if (!withoutDetails) Center( child: Tooltip( @@ -77,18 +84,40 @@ class WeaponCard extends StatelessWidget { ), ), Rarity(stars: rarity), - if (!withoutDetails) - Text( - '${s.translateStatTypeWithoutValue(StatType.atk)}: $baseAtk', - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - ), - if (!withoutDetails) - Text( - '${s.type}: ${s.translateWeaponType(type)}', - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - ), + BlocBuilder( + builder: (context, state) { + return state.map( + loading: (_) => const Loading(useScaffold: false), + loaded: (settingsState) { + if (withoutDetails || !settingsState.showWeaponDetails) { + return Container(); + } + return Text( + '${s.translateStatTypeWithoutValue(StatType.atk)}: $baseAtk', + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + ); + }, + ); + }, + ), + BlocBuilder( + builder: (context, state) { + return state.map( + loading: (_) => const Loading(useScaffold: false), + loaded: (settingsState) { + if (withoutDetails || !settingsState.showWeaponDetails) { + return Container(); + } + return Text( + '${s.type}: ${s.translateWeaponType(type)}', + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + ); + }, + ); + }, + ), ], ), ), diff --git a/pubspec.lock b/pubspec.lock index 85f5f016e..7e55bf710 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -866,6 +866,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.1+2" + transparent_image: + dependency: "direct main" + description: + name: transparent_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" tuple: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 14220c5b9..e6da10427 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: smooth_star_rating: 1.1.1 sprintf: ^5.0.0 sqlite3_flutter_libs: ^0.2.0 + transparent_image: 1.0.0 dev_dependencies: build_runner: ^1.10.6