diff --git a/assets/images/card_placeholder.webp b/assets/images/card_placeholder.webp deleted file mode 100644 index 9899169..0000000 Binary files a/assets/images/card_placeholder.webp and /dev/null differ diff --git a/lib/http/ArticleApi.dart b/lib/api/ArticleApi.dart similarity index 62% rename from lib/http/ArticleApi.dart rename to lib/api/ArticleApi.dart index 4a17d8f..25cdf27 100644 --- a/lib/http/ArticleApi.dart +++ b/lib/api/ArticleApi.dart @@ -1,37 +1,40 @@ -import 'package:duel_links_meta/http/http.dart'; +import 'package:duel_links_meta/api/http.dart'; import 'package:duel_links_meta/type/Article.dart'; import 'package:get/get.dart'; -class ArticleApi extends Net { - Future>> articleList(Map _params) { - final params = {}; - params['limit'] = _params['limit'] ?? '8'; - params['page'] = _params['page']; +class ArticleApi { + factory ArticleApi() { + return _instance; + } + + ArticleApi._constructor(); - params['field'] = '-markdown'; - params['sort'] = '-featured,-date'; - params[r'hidden[$ne]'] = 'true'; - params[r'category[$ne]'] = 'quick-news'; + static final _instance = ArticleApi._constructor(); - return httpClient.get('/api/v1/articles', query: params, decoder: (data) => (data as List).map(Article.fromJson).toList()); + Future>> articleList(Map params) { + return http.get( + '/api/v1/articles', + query: params, + decoder: (data) => (data as List).map(Article.fromJson).toList(), + ); } Future>> activeEventLst(DateTime countdownDate) { - return httpClient.get( + return http.get( '/api/v1/articles?category=event&countdownDate[\$gte]=$countdownDate&sort=-date&hidden[\$ne]=true&limit=10', decoder: (data) => (data as List).map(Article.fromJson).toList(), ); } Future>> pastEventLst(DateTime countdownDate, int page) { - return httpClient.get( + return http.get( '/api/v1/articles?category=event&countdownDate[\$lt]=$countdownDate&sort=-date&hidden[\$ne]=true&limit=10&page=$page', decoder: (data) => (data as List).map(Article.fromJson).toList(), ); } Future>> pinnedFarmingEvent() { - return httpClient.get( + return http.get( r'/api/v1/articles?category=farming&subCategory=pinned&sort=-date&hidden[$ne]=true', decoder: (data) => (data as List).map(Article.fromJson).toList(), ); diff --git a/lib/http/BanListChangeApi.dart b/lib/api/BanListChangeApi.dart similarity index 58% rename from lib/http/BanListChangeApi.dart rename to lib/api/BanListChangeApi.dart index c4a0a22..7763567 100644 --- a/lib/http/BanListChangeApi.dart +++ b/lib/api/BanListChangeApi.dart @@ -1,9 +1,17 @@ -import 'package:duel_links_meta/http/http.dart'; +import 'package:duel_links_meta/api/http.dart'; import 'package:duel_links_meta/type/ban_list_change/BanListChange.dart'; import 'package:get/get_connect/http/src/response/response.dart'; -class BanListChangeApi extends Net { - Future>> list({Map? params}) => httpClient.get( +class BanListChangeApi { + factory BanListChangeApi() { + return _instance; + } + + BanListChangeApi._constructor(); + + static final _instance = BanListChangeApi._constructor(); + + Future>> list({Map? params}) => http.get( '/api/v1/banlist-changes', query: params, decoder: (data) => (data as List).map(BanListChange.fromJson).toList(), diff --git a/lib/http/CardApi.dart b/lib/api/CardApi.dart similarity index 67% rename from lib/http/CardApi.dart rename to lib/api/CardApi.dart index 5a98047..739b4da 100644 --- a/lib/http/CardApi.dart +++ b/lib/api/CardApi.dart @@ -1,24 +1,32 @@ -import 'package:duel_links_meta/http/http.dart'; +import 'package:duel_links_meta/api/http.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:get/get.dart'; -class CardApi extends Net { - Future>> getByIds(String ids) => httpClient.get( +class CardApi { + factory CardApi() { + return _instance; + } + + CardApi._constructor(); + + static final _instance = CardApi._constructor(); + + Future>> getByIds(String ids) => http.get( '/api/v1/cards?_id[\$in]=$ids', decoder: (data) => (data as List).map(MdCard.fromJson).toList(), ); - Future> getById(String id) => httpClient.get( + Future> getById(String id) => http.get( '/api/v1/cards?_id[\$in]=$id&limit=1', decoder: MdCard.fromJson, ); - Future>> getByObtainSource(String sourceId) => httpClient.get( + Future>> getByObtainSource(String sourceId) => http.get( '/api/v1/cards?obtain.source=$sourceId&sort=-rarity&limit=0', decoder: (data) => (data as List).map(MdCard.fromJson).toList(), ); - Future>> list(Map? params) => httpClient.get( + Future>> list(Map? params) => http.get( '/api/v1/cards', query: params, decoder: (data) => (data as List).map(MdCard.fromJson).toList(), diff --git a/lib/api/CharacterApi.dart b/lib/api/CharacterApi.dart new file mode 100644 index 0000000..09d7b4d --- /dev/null +++ b/lib/api/CharacterApi.dart @@ -0,0 +1,15 @@ +import 'package:get/get.dart'; + +import 'http.dart'; + +class CharacterApi { + + factory CharacterApi() { + return _instance; + } + CharacterApi._constructor(); + + static final _instance = CharacterApi._constructor(); + + Future> list() => http.get('/api/v1/characters?npc[\$ne]=true&limit=0&sort=-linkedArticle.date'); +} diff --git a/lib/api/DeckTypeApi.dart b/lib/api/DeckTypeApi.dart new file mode 100644 index 0000000..f4b3744 --- /dev/null +++ b/lib/api/DeckTypeApi.dart @@ -0,0 +1,17 @@ +import 'package:duel_links_meta/type/deck_type/DeckType.dart'; +import 'package:get/get_connect/http/src/response/response.dart'; + +import 'package:duel_links_meta/api/http.dart'; + +class DeckTypeApi{ + factory DeckTypeApi() { + return _instance; + } + + DeckTypeApi._constructor(); + + static final _instance = DeckTypeApi._constructor(); + + Future> getDetailByName(String name) => + http.get('/api/v1/deck-types?name=$name&limit=1&aggregate=aboveThresh', decoder: DeckType.fromJson); +} diff --git a/lib/http/NavTabApi.dart b/lib/api/NavTabApi.dart similarity index 72% rename from lib/http/NavTabApi.dart rename to lib/api/NavTabApi.dart index e8deddd..a3f1787 100644 --- a/lib/http/NavTabApi.dart +++ b/lib/api/NavTabApi.dart @@ -1,8 +1,8 @@ -import 'package:duel_links_meta/http/http.dart'; +import 'package:duel_links_meta/api/http.dart'; import 'package:duel_links_meta/type/NavTab.dart'; import 'package:get/get.dart'; -class NavTabApi extends Net { +class NavTabApi{ factory NavTabApi() { return _instance; } @@ -11,7 +11,7 @@ class NavTabApi extends Net { static final NavTabApi _instance = NavTabApi._privateConstructor(); - Future>> list() => httpClient.get( + Future>> list() => http.get( '/api/v1/nav-tabs', decoder: (data) => (data as List).map(NavTab.fromJson).toList(), ); diff --git a/lib/http/PackSetApi.dart b/lib/api/PackSetApi.dart similarity index 50% rename from lib/http/PackSetApi.dart rename to lib/api/PackSetApi.dart index b77a4ed..a19154a 100644 --- a/lib/http/PackSetApi.dart +++ b/lib/api/PackSetApi.dart @@ -1,9 +1,16 @@ -import 'package:duel_links_meta/http/http.dart'; import 'package:duel_links_meta/type/pack_set/PackSet.dart'; import 'package:get/get.dart'; -class PackSetApi extends Net { - Future>> list() => httpClient.get( +import 'http.dart'; + +class PackSetApi { + factory PackSetApi(){ + return _instance; + } + PackSetApi._constructor(); + static final _instance = PackSetApi._constructor(); + + Future>> list() => http.get( '/api/v1/sets?sort=-release&limit=0', decoder: (data) => (data as List).map(PackSet.fromJson).toList(), ); diff --git a/lib/api/SkillApi.dart b/lib/api/SkillApi.dart new file mode 100644 index 0000000..8cffd9c --- /dev/null +++ b/lib/api/SkillApi.dart @@ -0,0 +1,21 @@ +import 'package:duel_links_meta/api/http.dart'; +import 'package:duel_links_meta/type/skill/Skill.dart'; +import 'package:get/get_connect/http/src/response/response.dart'; + +class SkillApi { + factory SkillApi() { + return _instance; + } + + SkillApi._constructor(); + + static final _instance = SkillApi._constructor(); + + Future> getByName(String name) => http.get( + '/api/v1/skills?name[\$in]=$name&limit=1', + decoder: Skill.fromJson, + ); + + Future>> getByCharacterId(String characterId) => + http.get('/api/v1/skills?characters.character[\$or]=$characterId&archive[\$or]=true&rush[\$ne]=true&sort=name'); +} diff --git a/lib/api/SkillStatsApi.dart b/lib/api/SkillStatsApi.dart new file mode 100644 index 0000000..b32c8ce --- /dev/null +++ b/lib/api/SkillStatsApi.dart @@ -0,0 +1,14 @@ +import 'package:duel_links_meta/api/http.dart'; +import 'package:get/get_connect/connect.dart'; + +class SkillStatsApi { + factory SkillStatsApi() { + return _instance; + } + + SkillStatsApi._constructor(); + + static final _instance = SkillStatsApi._constructor(); + + Future>> getByName(String name) => http.get('/api/v1/skills/stats?name=$name'); +} diff --git a/lib/api/TierListApi.dart b/lib/api/TierListApi.dart new file mode 100644 index 0000000..1629feb --- /dev/null +++ b/lib/api/TierListApi.dart @@ -0,0 +1,29 @@ +import 'package:duel_links_meta/api/http.dart'; +import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking.dart'; +import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier.dart'; +import 'package:get/get_connect/http/src/response/response.dart'; + +class TierListApi { + factory TierListApi() { + return _instance; + } + + TierListApi._constructor(); + + static final TierListApi _instance = TierListApi._constructor(); + + Future>> getTopTiers() => http.get( + r'/api/v1/deck-types?tier[$in]=0,1,2,3,4&limit=0&sort=name&fields=name,tier', + decoder: (data) => (data as List).map(TierList_TopTier.fromJson).toList(), + ); + + Future>> getPowerRankings() => http.get( + r'/api/v1/deck-types?rush[$ne]=true&tournamentPower[$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend', + decoder: (data) => (data as List).map(TierList_PowerRanking.fromJson).toList(), + ); + + Future>> getRushRankings() => http.get( + r'/api/v1/deck-types?rush=true&tournamentPower[$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend,rush', + decoder: (data) => (data as List).map(TierList_PowerRanking.fromJson).toList(), + ); +} diff --git a/lib/api/TopDeckApi.dart b/lib/api/TopDeckApi.dart new file mode 100644 index 0000000..c6e9af4 --- /dev/null +++ b/lib/api/TopDeckApi.dart @@ -0,0 +1,24 @@ +import 'package:duel_links_meta/api/http.dart'; +import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; +import 'package:get/get_connect/http/src/response/response.dart'; + +class TopDeckApi { + factory TopDeckApi() { + return _instance; + } + + TopDeckApi._constructor(); + + static final _instance = TopDeckApi._constructor(); + + Future> getBreakdownSample(Map params) => + http.get('/api/v1/top-decks', query: params, decoder: TopDeck.fromJson); + + Future>> list(Map params) => http.get( + '/api/v1/top-decks', + query: params, + decoder: (data) { + return (data as List).map(TopDeck.fromJson).toList(); + }, + ); +} diff --git a/lib/api/WorldApi.dart b/lib/api/WorldApi.dart new file mode 100644 index 0000000..24eef5a --- /dev/null +++ b/lib/api/WorldApi.dart @@ -0,0 +1,15 @@ +import 'package:get/get.dart'; + +import 'http.dart'; + +class WorldApi { + factory WorldApi() { + return _instance; + } + + WorldApi._constructor(); + + static final _instance = WorldApi._constructor(); + + Future>> list() => http.get('/api/v1/worlds'); +} diff --git a/lib/http/http.dart b/lib/api/http.dart similarity index 100% rename from lib/http/http.dart rename to lib/api/http.dart diff --git a/lib/components/MdCardItemView.dart b/lib/components/MdCardItemView.dart index 53cb81e..c5f17b2 100644 --- a/lib/components/MdCardItemView.dart +++ b/lib/components/MdCardItemView.dart @@ -37,8 +37,8 @@ class _MdCardItemViewState extends State { Stack( children: [ CachedNetworkImage( - placeholder: (context, url) => Image.asset('assets/images/card_placeholder.webp'), - errorWidget: (context, url, err) => Image.asset('assets/images/card_placeholder.webp'), + // placeholder: (context, url) => Image.asset('assets/images/card_placeholder.webp'), + // errorWidget: (context, url, err) => Image.asset('assets/images/card_placeholder.webp'), fadeInDuration: const Duration(milliseconds: 0), fadeOutDuration: null, imageUrl: 'https://s3.duellinksmeta.com/cards/${mdCard.oid}_w100.webp'), diff --git a/lib/components/MdCardItemView2.dart b/lib/components/MdCardItemView2.dart index 25cc075..ed321f1 100644 --- a/lib/components/MdCardItemView2.dart +++ b/lib/components/MdCardItemView2.dart @@ -1,9 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/api/CardApi.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; import 'package:duel_links_meta/store/BanCardStore.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:flutter/material.dart'; diff --git a/lib/components/MdCardsBoxLayout.dart b/lib/components/MdCardsBoxLayout.dart index d4b28ef..ff330b1 100644 --- a/lib/components/MdCardsBoxLayout.dart +++ b/lib/components/MdCardsBoxLayout.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import '../constant/colors.dart'; - class MdCardsBoxLayout extends StatelessWidget { const MdCardsBoxLayout({super.key, this.child}); @@ -10,13 +8,21 @@ class MdCardsBoxLayout extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.only(left: 8, right: 8, top: 8, bottom: 0), - decoration: BoxDecoration( - // color: BaColors.theme, - border: Border.all(color: const Color(0xff385979), width: 1), - borderRadius: const BorderRadius.all(Radius.circular(4)), - image: const DecorationImage(image: AssetImage('assets/images/modal_bg.webp'), fit: BoxFit.fitWidth), + padding: const EdgeInsets.only( + left: 8, + right: 8, + top: 8, + bottom: 0, + ), + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xff385979), + width: 1, ), - child: child); + borderRadius: const BorderRadius.all(Radius.circular(4)), + image: const DecorationImage(image: AssetImage('assets/images/modal_bg.webp'), fit: BoxFit.fitWidth), + ), + child: child, + ); } } diff --git a/lib/components/ModalBottomSheetWrap.dart b/lib/components/ModalBottomSheetWrap.dart new file mode 100644 index 0000000..d138af4 --- /dev/null +++ b/lib/components/ModalBottomSheetWrap.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class ModalBottomSheetWrap extends StatelessWidget { + const ModalBottomSheetWrap({required this.child, super.key}); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(34), + topRight: Radius.circular(34), + ), + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(24)), + child: ColoredBox( + color: Theme.of(context).colorScheme.onPrimary, + child: SingleChildScrollView( + child: Column( + children: [ + child, + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/components/SettingModalView.dart b/lib/components/SettingModalView.dart index cbf6d4c..dfa53a4 100644 --- a/lib/components/SettingModalView.dart +++ b/lib/components/SettingModalView.dart @@ -1,8 +1,11 @@ -import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/components/ModalBottomSheetWrap.dart'; +import 'package:duel_links_meta/components/ThemeColorSelectorPanelModalView.dart'; import 'package:duel_links_meta/hive/db/DarkModeHiveDb.dart'; import 'package:duel_links_meta/pages/open_source_licenses/index.dart'; import 'package:duel_links_meta/store/AppStore.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -28,6 +31,16 @@ class _SettingModalViewState extends State { githubHash = const String.fromEnvironment('GITHUB_HASH'); } + Future _handleOpenThemeColorModal() async { + await showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (context) { + return const ThemeColorSelectorPanelModalView(); + }, + ); + } + @override void initState() { super.initState(); @@ -37,18 +50,17 @@ class _SettingModalViewState extends State { @override Widget build(BuildContext context) { - return ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(24), - topRight: Radius.circular(24), - ), + return ModalBottomSheetWrap( child: Container( - padding: const EdgeInsets.only(top: 30), - color: Theme.of(context).colorScheme.onPrimary, + // height: 400, + padding: const EdgeInsets.only(top: 20), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + const SizedBox( + height: 28, + ), const Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Text( @@ -56,25 +68,49 @@ class _SettingModalViewState extends State { style: TextStyle(fontSize: 24), ), ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ListTile( + title: Text('Theme mode ${Get.isDarkMode}'), + contentPadding: const EdgeInsets.only(right: 10, left: 16), + trailing: Row( + mainAxisSize: MainAxisSize.min, children: [ - const Padding( - padding: EdgeInsets.only(left: 6), - child: Text('Theme mode'), - ), - Row( + Stack( children: [ + Positioned.fill( + child: Center( + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary.withOpacity(Get.isDarkMode ? 0 : 0.1), + borderRadius: BorderRadius.circular(20), + ), + ), + ), + ), IconButton( isSelected: !Get.isDarkMode, onPressed: () { Get.changeThemeMode(ThemeMode.light); DarkModeHiveDb().set(ThemeMode.light); }, - - icon: const Icon(Icons.sunny), + icon: Icon(Icons.sunny, color: !Get.isDarkMode ? Theme.of(context).colorScheme.primary : null), + ), + ], + ), + Stack( + children: [ + Positioned.fill( + child: Center( + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary.withOpacity(!Get.isDarkMode ? 0 : 0.1), + borderRadius: BorderRadius.circular(20), + ), + ), + ), ), IconButton( isSelected: Get.isDarkMode, @@ -82,13 +118,55 @@ class _SettingModalViewState extends State { Get.changeThemeMode(ThemeMode.dark); DarkModeHiveDb().set(ThemeMode.dark); }, - icon: const Icon(Icons.nightlight_rounded), + icon: Icon(Icons.nightlight_rounded, color: Get.isDarkMode ? Theme.of(context).colorScheme.primary : null), ), ], - ), + ) ], ), ), + Material( + color: Colors.transparent, + child: ListTile( + onTap: appStore.toggleShowWebviewNavs, + title: const Text('Show all navs'), + trailing: Obx( + () => Switch( + inactiveTrackColor: Colors.grey.withOpacity(0.4), + inactiveThumbColor: Colors.grey, + value: appStore.showWebviewNavs.value, + activeColor: Theme.of(context).colorScheme.primary, + trackOutlineColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return Colors.transparent; + } else { + return Colors.grey; + } + }), + onChanged: (checked) => appStore.toggleShowWebviewNavs(), + ), + ), + ), + ), + Material( + color: Colors.transparent, + child: ListTile( + onTap: _handleOpenThemeColorModal, + title: const Text('Theme color'), + trailing: Obx( + () => Container( + width: 26, + height: 26, + decoration: BoxDecoration( + color: appStore.themeColor, + borderRadius: const BorderRadius.all( + Radius.circular(40), + ), + ), + ), + ), + ), + ), const Padding( padding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), child: Text( @@ -96,87 +174,75 @@ class _SettingModalViewState extends State { style: TextStyle(fontSize: 24), ), ), - Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('App version'), - Text(packageInfo?.version ?? ''), - ], - ), + ListTile( + title: const Text('App version'), + trailing: Text(packageInfo?.version ?? ''), ), - Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Commit ID'), - Text(githubHash), - ], - ), + ListTile( + title: const Text('Commit ID'), + trailing: Text(githubHash), ), Material( color: Colors.transparent, - child: InkWell( + child: ListTile( onTap: () { final uri = Uri.parse(repos); launchUrl(uri).ignore(); }, - child: Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Github'), - Icon(Icons.arrow_forward, color: Theme.of(context).iconTheme.color?.withOpacity(0.6)), - ], - ), - ), + title: const Text('Github'), + trailing: const Icon(Icons.arrow_forward), ), ), Material( color: Colors.transparent, - child: InkWell( + child: ListTile( + title: const Text('Open Source Licenses'), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const OpenSourceLicensePage())); }, - child: Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Open source licenses'), - Icon(Icons.arrow_forward, color: Theme.of(context).iconTheme.color?.withOpacity(0.6),), - ], - ), - ), + trailing: const Icon(Icons.arrow_forward), ), ), Material( color: Colors.transparent, - child: InkWell( + child: ListTile( + title: const Text('Duel Links Meta'), onTap: () { - final uri = Uri.parse(duelLinksMetaUrl); - launchUrl(uri).ignore(); + final uri = Uri.parse(duelLinksMetaUrl); + launchUrl(uri).ignore(); }, - child: Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Duel Link Meta'), - Icon(Icons.arrow_forward, color: Theme.of(context).iconTheme.color?.withOpacity(0.6),), - ], - ), - ), + trailing: const Icon(Icons.arrow_forward), ), - ), + ) + ], + ), + ), + ), + ); + } +} + +class SettingItem extends StatelessWidget { + SettingItem({super.key, this.onTap, required this.title}); + + void Function()? onTap; + String title; + Widget? tailing; + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap?.call, + child: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title), + tailing ?? const SizedBox(), ], ), ), diff --git a/lib/components/SkillModalView.dart b/lib/components/SkillModalView.dart index b64c93c..7c2674a 100644 --- a/lib/components/SkillModalView.dart +++ b/lib/components/SkillModalView.dart @@ -1,12 +1,13 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/api/SkillApi.dart'; import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/gen/assets.gen.dart'; import 'package:duel_links_meta/hive/db/SkillHiveDb.dart'; -import 'package:duel_links_meta/http/SkillApi.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/skill/Skill.dart'; import 'package:flutter/material.dart'; +import '../gen/assets.gen.dart'; + class SkillModalView extends StatefulWidget { const SkillModalView({required this.name, super.key, this.skill}); @@ -90,21 +91,19 @@ class _SkillModalViewState extends State { const SizedBox(height: 4), if (_skill.relatedCards.isNotEmpty) SizedBox( - height: 60, + height: 65, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: _skill.relatedCards.length, itemBuilder: (context, index) { return Row( children: [ - Container( - color: Colors.white38, - height: 60, - width: 60 / 1.4, - child: CachedNetworkImage( - fit: BoxFit.cover, - imageUrl: 'https://s3.duellinksmeta.com/cards/${_skill.relatedCards[index].oid}_w100.webp', - ), + CachedNetworkImage( + fit: BoxFit.fitWidth, + width: 45, + imageUrl: 'https://s3.duellinksmeta.com/cards/${_skill.relatedCards[index].oid}_w100.webp', + placeholder: (context, url) => Assets.images.cardPlaceholder.image(), + errorWidget: (context, url, obj) => Assets.images.cardPlaceholder.image(), ), const SizedBox(width: 4), ], @@ -117,25 +116,26 @@ class _SkillModalViewState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Characters', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), + const Text( + 'Characters', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500), + ), ListView.builder( padding: EdgeInsets.zero, - // scrollDirection: Axis.horizontal, itemCount: _skill.characters.length, physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { return Container( - padding: EdgeInsets.only(bottom: 4), + padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ SizedBox( - width: 36, - height: 42, + width: 40, + height: 40 * 1.16, child: CachedNetworkImage( fit: BoxFit.cover, - imageUrl: - 'https://s3.duellinksmeta.com${_skill.characters[index].character.thumbnailImage}', + imageUrl: 'https://s3.duellinksmeta.com${_skill.characters[index].character.thumbnailImage}', ), ), const SizedBox(width: 4), @@ -167,10 +167,12 @@ class _SkillModalViewState extends State { ) ], ), - - if (_pageStatus == PageStatus.loading) Positioned(child: Center( - child: CircularProgressIndicator(), - )) + if (_pageStatus == PageStatus.loading) + const Positioned( + child: Center( + child: CircularProgressIndicator(), + ), + ) ], ), ), diff --git a/lib/components/ThemeColorSelectorPanelModalView.dart b/lib/components/ThemeColorSelectorPanelModalView.dart new file mode 100644 index 0000000..253bfca --- /dev/null +++ b/lib/components/ThemeColorSelectorPanelModalView.dart @@ -0,0 +1,91 @@ +import 'package:duel_links_meta/components/ModalBottomSheetWrap.dart'; +import 'package:duel_links_meta/store/AppStore.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class ThemeColorSelectorPanelModalView extends StatefulWidget { + const ThemeColorSelectorPanelModalView({super.key}); + + @override + State createState() => _ThemeColorSelectorPanelModalViewState(); +} + +class _ThemeColorSelectorPanelModalViewState extends State { + var _index = 0; + var appStore = Get.put(AppStore()); + + @override + Widget build(BuildContext context) { + return ModalBottomSheetWrap( + child: Container( + padding: EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Text( + 'Choose theme color', + style: TextStyle(fontSize: 24), + ), + ), + GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: appStore.themeColorList.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 7, + childAspectRatio: 1, + mainAxisSpacing: 6, + crossAxisSpacing: 6, + ), + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + appStore.changeThemeColorIndex(index); + }, + child: Stack( + children: [ + Container( + decoration: BoxDecoration( + color: appStore.themeColorList[index], + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.4), + spreadRadius: 1, + blurRadius: 6, + offset: const Offset(2, 2), + ) + ], + ), + ), + Positioned( + child: Center( + child: Obx(() => AnimatedScale( + duration: const Duration(milliseconds: 200), + curve: Curves.fastOutSlowIn, + scale: appStore.themeColorIndex.value == index ? 1 : 0, + child: AnimatedOpacity( + opacity: appStore.themeColorIndex.value == index ? 1 : 0, + duration: const Duration(milliseconds: 200), + child: const Icon( + Icons.check, + size: 20, + color: Colors.white, + ), + ), + )), + ), + ) + ], + ), + ); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/components/TopDeckItem.dart b/lib/components/TopDeckItem.dart index 0bfb39a..2bfed87 100644 --- a/lib/components/TopDeckItem.dart +++ b/lib/components/TopDeckItem.dart @@ -1,14 +1,19 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; -import 'package:duel_links_meta/type/top_deck/TopDeckSimple.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -import '../type/top_deck/TopDeck.dart'; class TopDeckItem extends StatelessWidget { - const TopDeckItem({super.key, required this.topDeck, this.topLeft, this.bottomRight, this.isNew, this.coverUrl, this.isActive, required this.onTap}); + const TopDeckItem({ + required this.topDeck, + required this.onTap, + super.key, + this.topLeft, + this.bottomRight, + this.isNew, + this.coverUrl, + this.isActive, + }); final String? coverUrl; final TopDeck topDeck; @@ -26,12 +31,12 @@ class TopDeckItem extends StatelessWidget { children: [ Column( children: [ - SizedBox( + const SizedBox( height: 6, ), Row( children: [ - SizedBox( + const SizedBox( width: 4, ), Expanded( @@ -45,11 +50,9 @@ class TopDeckItem extends StatelessWidget { children: [ Container( color: const Color(0xff002142), - // color: const Color(0xff91c8da), child: CachedNetworkImage( width: 32, fit: BoxFit.cover, - // https://wsrv.nl/?url=https://s3.duellinksmeta.com/img/tournaments/logos/kog.webp&w=100&output=webp&we&n=-1&maxage=7d imageUrl: coverUrl ?? 'https://imgserv.duellinksmeta.com/v2/dlm/deck-type/${Uri.encodeComponent(topDeck.deckType.name)}?portrait=true&width=50', ), @@ -60,9 +63,8 @@ class TopDeckItem extends StatelessWidget { Container( padding: const EdgeInsets.symmetric(horizontal: 22), decoration: BoxDecoration( - color: isActive == true ? Color(0xff91c8da) : Color(0xFFa9bcc3), - // color: Color(0xFFa9bcc3), - boxShadow: [ + color: (isActive ?? false) == true ? const Color(0xff91c8da) : const Color(0xFFa9bcc3), + boxShadow: const [ BoxShadow(color: Colors.pinkAccent), BoxShadow( color: Colors.pinkAccent, diff --git a/lib/pages/cards_viewpager/CardView.dart b/lib/components/cards_viewpager/CardView.dart similarity index 94% rename from lib/pages/cards_viewpager/CardView.dart rename to lib/components/cards_viewpager/CardView.dart index fa0c16c..15e5ea0 100644 --- a/lib/pages/cards_viewpager/CardView.dart +++ b/lib/components/cards_viewpager/CardView.dart @@ -1,21 +1,17 @@ import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/api/CardApi.dart'; +import 'package:duel_links_meta/components/IfElseBox.dart'; +import 'package:duel_links_meta/extension/DateTime.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; import 'package:duel_links_meta/store/BanCardStore.dart'; import 'package:duel_links_meta/type/MdCard.dart'; -import 'package:duel_links_meta/util/time_util.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; -import 'package:intl/intl.dart'; - -import '../../components/IfElseBox.dart'; class CardView extends StatefulWidget { const CardView({required this.card, super.key}); @@ -30,7 +26,6 @@ class CardView extends StatefulWidget { // 如果传入的card是只有基础信息(id,name)的card,需要从本地获取完整信息的card // 如果从本地获取不到card,则请求获取一次再存到本地 class _CardViewState extends State { - // MdCard? _card = MdCard()..oid="60c2b3a9a0e24f2d54a517cf"; MdCard? _card; BanCardStore banCardStore = Get.put(BanCardStore()); @@ -42,11 +37,9 @@ class _CardViewState extends State { super.dispose(); } - var banStatusStore = Get.put(BanCardStore()); + BanCardStore banStatusStore = Get.put(BanCardStore()); Future init() async { - log('_init, _card == null: ${_card == null}, widget.card: ${widget.card}'); - // _card已经有值 if (_card != null) return; @@ -62,12 +55,10 @@ class _CardViewState extends State { final card = await CardHiveDb().get(widget.card.oid); if (card != null) { - log('本地获取到card111'); setState(() { _card = card; }); } else { - log('请求获取card111'); final (err, res) = await CardApi().getById(_card!.oid).toCatch; if (err != null || res == null) return; @@ -265,7 +256,7 @@ class _CardViewState extends State { const SizedBox(height: 10), if (_card!.release != null) Text( - 'Released on ${TimeUtil.format(_card!.release)}', + 'Released on ${_card!.release?.format}', style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 12), ), ], diff --git a/lib/pages/cards_viewpager/index.dart b/lib/components/cards_viewpager/index.dart similarity index 95% rename from lib/pages/cards_viewpager/index.dart rename to lib/components/cards_viewpager/index.dart index f67de1f..19ea360 100644 --- a/lib/pages/cards_viewpager/index.dart +++ b/lib/components/cards_viewpager/index.dart @@ -1,10 +1,10 @@ -import 'package:duel_links_meta/pages/cards_viewpager/CardView.dart'; import 'package:duel_links_meta/type/MdCard.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; +import 'CardView.dart'; + class CardsViewpagerPage extends StatefulWidget { const CardsViewpagerPage({required this.cards, required this.index, super.key}); diff --git a/lib/constant/colors.dart b/lib/constant/colors.dart deleted file mode 100644 index c59e5a0..0000000 --- a/lib/constant/colors.dart +++ /dev/null @@ -1,23 +0,0 @@ -// import 'dart:ui'; - -import 'package:flutter/material.dart'; - -class BaColors { - // static var momo = const Color.fromRGBO(251, 151, 169, 1); - - // static const MaterialColor momo = MaterialColor( - // _momo, - // {}, - // ); - // static const int _momo = 0xFFfb97a9; - // - // static const MaterialColor blue = MaterialColor( - // _blue, - // {}, - // ); - // static const int _blue = 0xFF3bc7ff; - - static const Color theme = Color(0xFF001b35); - - static const Color main = Color(0xFF001427); -} diff --git a/lib/constant/exception/HttpCacheException.dart b/lib/constant/exception/HttpCacheException.dart deleted file mode 100644 index 04543c1..0000000 --- a/lib/constant/exception/HttpCacheException.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'dart:io'; - -class HttpCacheException extends HttpException { - HttpCacheException(super.message); - -} \ No newline at end of file diff --git a/lib/extension/DateTime.dart b/lib/extension/DateTime.dart index 0222ff3..1329d08 100644 --- a/lib/extension/DateTime.dart +++ b/lib/extension/DateTime.dart @@ -1,5 +1,7 @@ -import '../util/time_util.dart'; +import 'package:intl/intl.dart'; + +var _formatter = DateFormat.yMMMMd(); extension DateTimeEx on DateTime { - String get format => TimeUtil.format(this); // 扩展方法 -} \ No newline at end of file + String get format => _formatter.format(this); +} diff --git a/lib/extension/Function.dart b/lib/extension/Function.dart deleted file mode 100644 index 4cc0e53..0000000 --- a/lib/extension/Function.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -extension FunctionEx on Function { - // TODO: execute immediately - debounce(int milliseconds) { - Timer? timer; - - log('debounce init $timer'); - - return (dynamic args) { - log('debounce 11111'); - timer?.cancel(); - - timer = Timer(Duration(milliseconds: milliseconds), () { - log('debounce execute, args: $args'); - this(args); - }); - }; - } - - throttle(int milliseconds) { - Timer? timer; - - log('throttle init'); - - return () { - if (timer == null) { - log('throttle execute, this: ${this}'); - - timer = Timer(Duration(milliseconds: milliseconds), () { - timer = null; - this.call(); - }); - - // this.call(); - } - }; - } - - throttleWith1Parameter(int milliseconds) { - Timer? timer; - - log('throttle init'); - - return (dynamic arg) { - if (timer == null) { - log('throttle execute, this: ${this}'); - - timer = Timer(Duration(milliseconds: milliseconds), () { - timer = null; - this.call(arg); - }); - - // this.call(); - } - }; - } -} diff --git a/lib/extension/Future.dart b/lib/extension/Future.dart index f9a6c4e..15948c3 100644 --- a/lib/extension/Future.dart +++ b/lib/extension/Future.dart @@ -7,14 +7,9 @@ import 'package:get/get_connect/connect.dart'; extension FutureEx on Future> { Future<(Exception?, T?)> get toCatch async{ - log('进入自定义 toCatch'); - try { - var res = await this; - log('[toCatch] code: ${res.statusCode}, body is null: ${res.body == null}, statusText: ${res.statusText}'); - + final res = await this; if (res.statusCode != 200) { - // return (HttpException('status: ${res.statusCode}, msg: ${res.statusText}'), null); throw HttpException('status: ${res.statusCode}'); } diff --git a/lib/extension/String.dart b/lib/extension/String.dart index 868b028..f188e28 100644 --- a/lib/extension/String.dart +++ b/lib/extension/String.dart @@ -4,15 +4,11 @@ import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; extension StringEx on String { - toast() { - print('[toast] $this'); - + void toast() { SmartDialog.showToast(this, alignment: const Alignment(0, 0.8)); } - copy(String? toastText) { - print('toastText: $toastText'); - + void copy(String? toastText) { Clipboard.setData(ClipboardData(text: this)); if (toastText != null) { diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 579513a..7e9b6d1 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -14,14 +14,18 @@ import 'package:flutter/services.dart'; class $AssetsImagesGen { const $AssetsImagesGen(); - /// File path: assets/images/card_placeholder.webp + /// File path: assets/images/card_placeholder.jpg AssetGenImage get cardPlaceholder => - const AssetGenImage('assets/images/card_placeholder.webp'); + const AssetGenImage('assets/images/card_placeholder.jpg'); /// File path: assets/images/common_card_new.png AssetGenImage get commonCardNew => const AssetGenImage('assets/images/common_card_new.png'); + /// File path: assets/images/dlm_logo.png + AssetGenImage get dlmLogo => + const AssetGenImage('assets/images/dlm_logo.png'); + /// File path: assets/images/icon_amount_1.webp AssetGenImage get iconAmount1 => const AssetGenImage('assets/images/icon_amount_1.webp'); @@ -150,6 +154,14 @@ class $AssetsImagesGen { AssetGenImage get rarityUr => const AssetGenImage('assets/images/rarity_ur.webp'); + /// File path: assets/images/site-logo-dlm.png + AssetGenImage get siteLogoDlmPng => + const AssetGenImage('assets/images/site-logo-dlm.png'); + + /// File path: assets/images/site-logo-dlm.svg + SvgGenImage get siteLogoDlmSvg => + const SvgGenImage('assets/images/site-logo-dlm.svg'); + /// File path: assets/images/tier_1.png AssetGenImage get tier1Png => const AssetGenImage('assets/images/tier_1.png'); @@ -210,6 +222,7 @@ class $AssetsImagesGen { List get values => [ cardPlaceholder, commonCardNew, + dlmLogo, iconAmount1, iconAmount2, iconAmount3, @@ -242,6 +255,8 @@ class $AssetsImagesGen { rarityR, raritySr, rarityUr, + siteLogoDlmPng, + siteLogoDlmSvg, tier1Png, tier1Webp, tier1Dark, diff --git a/lib/hive/MyHive.dart b/lib/hive/MyHive.dart index d962e91..ce97bc1 100644 --- a/lib/hive/MyHive.dart +++ b/lib/hive/MyHive.dart @@ -13,10 +13,12 @@ import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier_Expire. import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; import 'package:hive_flutter/adapters.dart'; -// const boxName = 'todo_box'; -const boxName2 = 'todo_box2'; +const boxName = 'todo_box2'; class MyHive { + + MyHive._(); + static const int tier_list_top_tier = 1; static const int tier_list_power_ranking = 2; static const int tier_list_top_tier_expire = 3; @@ -44,13 +46,7 @@ class MyHive { static const int skill_related_character_character = 25; static const int article = 26; - // static const int expire_data = 10; - - // static late Box> box; - // static late Box box; - static late LazyBox box2; - - MyHive._(); + static late LazyBox box; static Future init() async { await Hive.initFlutter(); @@ -85,6 +81,6 @@ class MyHive { // Hive.registerAdapter(TLoginFormAdapter()); // box = await Hive.openBox(boxName); - box2 = await Hive.openLazyBox(boxName2); + box = await Hive.openLazyBox(boxName); } } diff --git a/lib/hive/db/ArticleHiveDb.dart b/lib/hive/db/ArticleHiveDb.dart index 7aedf38..6453072 100644 --- a/lib/hive/db/ArticleHiveDb.dart +++ b/lib/hive/db/ArticleHiveDb.dart @@ -25,7 +25,7 @@ class ArticleHiveDb { List
? articles; try { - final data = await MyHive.box2.get(key) as List?; + final data = await MyHive.box.get(key) as List?; articles = data?.map((e) => e as Article).toList(); } catch (e) { log('转换失败: $e'); @@ -39,7 +39,7 @@ class ArticleHiveDb { final key = _getExpireTimeKey(category); DateTime? time; try { - time = await MyHive.box2.get(key) as DateTime?; + time = await MyHive.box.get(key) as DateTime?; } catch (e) { log('转换失败: $e'); return null; @@ -51,12 +51,12 @@ class ArticleHiveDb { Future set(List
articles, String category) { final key = _getKey(category); - return MyHive.box2.put(key, articles); + return MyHive.box.put(key, articles); } Future setExpireTime(DateTime? time, String category) { final key = _getExpireTimeKey(category); - return MyHive.box2.put(key, time); + return MyHive.box.put(key, time); } } diff --git a/lib/hive/db/BanListChangeHiveDb.dart b/lib/hive/db/BanListChangeHiveDb.dart index 0722fa9..42bb15b 100644 --- a/lib/hive/db/BanListChangeHiveDb.dart +++ b/lib/hive/db/BanListChangeHiveDb.dart @@ -19,7 +19,7 @@ class BanListChangeHiveDb { List? data; try { - final list = await MyHive.box2.get(_key) as List?; + final list = await MyHive.box.get(_key) as List?; data = list?.map((e) => e as BanListChange).toList(); } catch (e) { log('转换失败 $e'); @@ -32,7 +32,7 @@ class BanListChangeHiveDb { Future getExpireTime() async { DateTime? expireTime; try { - expireTime = await MyHive.box2.get(_expireKey) as DateTime?; + expireTime = await MyHive.box.get(_expireKey) as DateTime?; } catch (e) { log('转换失败 $e'); return null; @@ -41,10 +41,10 @@ class BanListChangeHiveDb { } Future set(List data) { - return MyHive.box2.put(_key, data); + return MyHive.box.put(_key, data); } Future setExpireTime(DateTime time) { - return MyHive.box2.put(_expireKey, time); + return MyHive.box.put(_expireKey, time); } } diff --git a/lib/hive/db/CardHiveDb.dart b/lib/hive/db/CardHiveDb.dart index dc24076..e66f3b9 100644 --- a/lib/hive/db/CardHiveDb.dart +++ b/lib/hive/db/CardHiveDb.dart @@ -21,7 +21,7 @@ class CardHiveDb { MdCard? card; try { - card = await MyHive.box2.get(key) as MdCard?; + card = await MyHive.box.get(key) as MdCard?; } catch (e) { log('转换失败'); return null; @@ -33,6 +33,6 @@ class CardHiveDb { Future set(MdCard card) async { final key = _getKey(card.oid); - return MyHive.box2.put(key, card); + return MyHive.box.put(key, card); } } diff --git a/lib/hive/db/DarkModeHiveDb.dart b/lib/hive/db/DarkModeHiveDb.dart index 56b4b1c..51e03eb 100644 --- a/lib/hive/db/DarkModeHiveDb.dart +++ b/lib/hive/db/DarkModeHiveDb.dart @@ -15,11 +15,11 @@ class DarkModeHiveDb { void set(ThemeMode mode) { - return MyHive.box2.put(_key, mode.name).ignore(); + return MyHive.box.put(_key, mode.name).ignore(); } Future get() async { - final mode = await MyHive.box2.get(_key); + final mode = await MyHive.box.get(_key); if (mode == ThemeMode.dark.name) { return ThemeMode.dark; diff --git a/lib/hive/db/DeckTypeDetailHiveDb.dart b/lib/hive/db/DeckTypeDetailHiveDb.dart index 1e8c9ac..9508866 100644 --- a/lib/hive/db/DeckTypeDetailHiveDb.dart +++ b/lib/hive/db/DeckTypeDetailHiveDb.dart @@ -25,7 +25,7 @@ class DeckTypeDetailHiveDb { DeckType? deckType; try { - deckType = await MyHive.box2.get(key) as DeckType?; + deckType = await MyHive.box.get(key) as DeckType?; } catch (e) { log('转换失败 $e'); return null; @@ -36,18 +36,18 @@ class DeckTypeDetailHiveDb { Future getExpireTime(String deckTypeName) async { final key = _getExpireTimeKey(deckTypeName); - final date = await MyHive.box2.get(key) as DateTime?; + final date = await MyHive.box.get(key) as DateTime?; return date; } Future set(DeckType deckType) { final key = _getKey(deckType.name); - return MyHive.box2.put(key, deckType); + return MyHive.box.put(key, deckType); } Future setExpireTime(DeckType deckType, DateTime date) { final key = _getExpireTimeKey(deckType.name); - return MyHive.box2.put(key, date); + return MyHive.box.put(key, date); } } diff --git a/lib/hive/db/NavHiveDb.dart b/lib/hive/db/NavHiveDb.dart index 32e4582..c717d06 100644 --- a/lib/hive/db/NavHiveDb.dart +++ b/lib/hive/db/NavHiveDb.dart @@ -13,7 +13,7 @@ class HomeHiveDb { final String _navTabFetchDateKey = 'nav_tab:fetch_date'; Future?> getNavTabList() async { - final hiveData = await MyHive.box2.get(_navTabKey) as List?; + final hiveData = await MyHive.box.get(_navTabKey) as List?; if (hiveData == null) return null; @@ -25,24 +25,24 @@ class HomeHiveDb { } Future deleteNavTabList() { - return MyHive.box2.delete(_navTabKey); + return MyHive.box.delete(_navTabKey); } Future deleteNavTabListExpireTime() { - return MyHive.box2.delete(_navTabFetchDateKey); + return MyHive.box.delete(_navTabFetchDateKey); } Future getNavTabListExpireTime() async { - final expireTime = await MyHive.box2.get(_navTabFetchDateKey) as DateTime?; + final expireTime = await MyHive.box.get(_navTabFetchDateKey) as DateTime?; return expireTime; } Future setNavTabList(List? data) { - return MyHive.box2.put(_navTabKey, data); + return MyHive.box.put(_navTabKey, data); } Future setNavTabListExpireTime(DateTime? time) { - return MyHive.box2.put(_navTabFetchDateKey, time); + return MyHive.box.put(_navTabFetchDateKey, time); } } diff --git a/lib/hive/db/PackHiveDb.dart b/lib/hive/db/PackHiveDb.dart index 386965c..665c40a 100644 --- a/lib/hive/db/PackHiveDb.dart +++ b/lib/hive/db/PackHiveDb.dart @@ -18,7 +18,7 @@ class PackHiveDb { List? ids; try { - ids = await MyHive.box2.get(key) as List?; + ids = await MyHive.box.get(key) as List?; } catch (e) { return null; } @@ -29,6 +29,6 @@ class PackHiveDb { Future setIds(String packId, List ids) { final key = _getKey(packId); - return MyHive.box2.put(key, ids); + return MyHive.box.put(key, ids); } } diff --git a/lib/hive/db/PacksHiveDb.dart b/lib/hive/db/PacksHiveDb.dart index fd5aefa..7d94132 100644 --- a/lib/hive/db/PacksHiveDb.dart +++ b/lib/hive/db/PacksHiveDb.dart @@ -16,13 +16,13 @@ class PacksHiveDb { final String _expireTimeKey = 'pack_set:list_last_fetch_date'; Future set(List data) { - return MyHive.box2.put(_key, data); + return MyHive.box.put(_key, data); } Future?>? get() async { List? list; try { - final data = await MyHive.box2.get(_key) as List?; + final data = await MyHive.box.get(_key) as List?; list = data?.map((e) => e as PackSet).toList(); } catch (e) { log('转换失败 $e'); @@ -32,13 +32,13 @@ class PacksHiveDb { } Future setExpireTime(DateTime date) { - return MyHive.box2.put(_expireTimeKey, date); + return MyHive.box.put(_expireTimeKey, date); } Future? getExpireTime() async { DateTime? time; try { - time = await MyHive.box2.get(_expireTimeKey) as DateTime?; + time = await MyHive.box.get(_expireTimeKey) as DateTime?; } catch (e) { log('转换失败 $e'); } diff --git a/lib/hive/db/PowerRankingsHiveDb.dart b/lib/hive/db/PowerRankingsHiveDb.dart index 903bb89..a02456a 100644 --- a/lib/hive/db/PowerRankingsHiveDb.dart +++ b/lib/hive/db/PowerRankingsHiveDb.dart @@ -23,7 +23,7 @@ class PowerRankingsHiveDb { final key = _getKey(isRush); List? list; try { - final data = await MyHive.box2.get(key) as List?; + final data = await MyHive.box.get(key) as List?; list = data?.map((e) => e as TierList_PowerRanking).toList(); } catch(e){ log('转换失败 $e'); @@ -34,14 +34,14 @@ class PowerRankingsHiveDb { Future set(List list, {required bool isRush}) { final key = _getKey(isRush); - return MyHive.box2.put(key, list); + return MyHive.box.put(key, list); } Future? getExpireTime({required bool isRush}) async{ final key = _getExpireTimeKey(isRush); DateTime? time; try { - time = await MyHive.box2.get(key) as DateTime?; + time = await MyHive.box.get(key) as DateTime?; } catch(e){ log('转换失败 $e'); } @@ -52,7 +52,7 @@ class PowerRankingsHiveDb { Future setExpireTime(DateTime time, {required bool isRush}) { final key = _getExpireTimeKey(isRush); - return MyHive.box2.put(key, time); + return MyHive.box.put(key, time); } } \ No newline at end of file diff --git a/lib/hive/db/SettingHiveDb.dart b/lib/hive/db/SettingHiveDb.dart new file mode 100644 index 0000000..fa8b4c7 --- /dev/null +++ b/lib/hive/db/SettingHiveDb.dart @@ -0,0 +1,30 @@ +import 'package:duel_links_meta/hive/MyHive.dart'; + +class SettingHiveDb { + factory SettingHiveDb() => _instance; + + SettingHiveDb._constructor(); + + static final _instance = SettingHiveDb._constructor(); + + final String _showWebviewNavsKey = 'setting:show_webview_navs'; + final String _themeColorIndexKey = 'setting:theme_color_index'; + + Future setShowWebviewNavs({required bool show}) { + return MyHive.box.put(_showWebviewNavsKey, show); + } + + Future getShowWebviewNavs() async { + final value = await MyHive.box.get(_showWebviewNavsKey) as bool?; + + return value; + } + + Future setThemeColorIndex(int index) { + return MyHive.box.put(_themeColorIndexKey, index); + } + + Future getThemeColorIndex() async{ + return (await MyHive.box.get(_themeColorIndexKey)) as int?; + } +} diff --git a/lib/hive/db/SkillHiveDb.dart b/lib/hive/db/SkillHiveDb.dart index 2c56979..49be2fe 100644 --- a/lib/hive/db/SkillHiveDb.dart +++ b/lib/hive/db/SkillHiveDb.dart @@ -24,7 +24,7 @@ class SkillHiveDb { Skill? skill; try { - skill = await MyHive.box2.get(key) as Skill?; + skill = await MyHive.box.get(key) as Skill?; } catch (e) { log('转换失败 $e'); return null; @@ -37,7 +37,7 @@ class SkillHiveDb { final key = _getExpireTimeKey(skillName); DateTime? time; try { - time = await MyHive.box2.get(key) as DateTime?; + time = await MyHive.box.get(key) as DateTime?; } catch (e) { log('转换失败 $e'); return null; @@ -48,12 +48,12 @@ class SkillHiveDb { Future set(Skill skill) { final key = _getKey(skill.name); - return MyHive.box2.put(key, skill); + return MyHive.box.put(key, skill); } Future setExpireTime(String skillName, DateTime time) { final key = _getExpireTimeKey(skillName); - return MyHive.box2.put(key, time); + return MyHive.box.put(key, time); } } diff --git a/lib/hive/db/TierListHiveDb.dart b/lib/hive/db/TierListHiveDb.dart index d61c1c9..8e1abfa 100644 --- a/lib/hive/db/TierListHiveDb.dart +++ b/lib/hive/db/TierListHiveDb.dart @@ -18,7 +18,7 @@ class TierListHiveDb { List? list; try { - final data = await MyHive.box2.get(_key) as List?; + final data = await MyHive.box.get(_key) as List?; list = data?.map((e) => e as TierList_TopTier).toList(); } catch (e) { log('转换失败 $e'); @@ -31,7 +31,7 @@ class TierListHiveDb { DateTime? time; try { - time = await MyHive.box2.get(_expireTimeKey) as DateTime?; + time = await MyHive.box.get(_expireTimeKey) as DateTime?; } catch (e) { log('转换失败 $e'); } @@ -40,10 +40,10 @@ class TierListHiveDb { } Future set(List list) { - return MyHive.box2.put(_key, list); + return MyHive.box.put(_key, list); } Future setExpireTime(DateTime time) { - return MyHive.box2.put(_expireTimeKey, time); + return MyHive.box.put(_expireTimeKey, time); } } diff --git a/lib/hive/db/TopDeckHiveDb.dart b/lib/hive/db/TopDeckHiveDb.dart index dcc1da0..3808f95 100644 --- a/lib/hive/db/TopDeckHiveDb.dart +++ b/lib/hive/db/TopDeckHiveDb.dart @@ -26,7 +26,7 @@ class TopDeckHiveDb { List? list; try { - final data = await MyHive.box2.get(key) as List?; + final data = await MyHive.box.get(key) as List?; list = data?.map((e) => e as TopDeck).toList(); }catch(e) { log('转换失败 $e'); @@ -39,7 +39,7 @@ class TopDeckHiveDb { final key = _getExpireTimeKey(isRush); DateTime? time; try { - time = await MyHive.box2.get(key) as DateTime?; + time = await MyHive.box.get(key) as DateTime?; } catch(e) { log('转换失败 $e'); } @@ -50,12 +50,12 @@ class TopDeckHiveDb { Future set(List list, {required bool isRush}) { final key = _getKey(isRush); - return MyHive.box2.put(key, list); + return MyHive.box.put(key, list); } Future setExpireTime(DateTime time, {required bool isRush}) { final key = _getExpireTimeKey(isRush); - return MyHive.box2.put(key, time); + return MyHive.box.put(key, time); } } \ No newline at end of file diff --git a/lib/http/CharacterApi.dart b/lib/http/CharacterApi.dart deleted file mode 100644 index 8e5d96d..0000000 --- a/lib/http/CharacterApi.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:get/get.dart'; - -class CharacterApi extends Net { - Future> list() => httpClient.get('/api/v1/characters?npc[\$ne]=true&limit=0&sort=-linkedArticle.date'); -} diff --git a/lib/http/DeckTypeApi.dart b/lib/http/DeckTypeApi.dart deleted file mode 100644 index 5cfe775..0000000 --- a/lib/http/DeckTypeApi.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:duel_links_meta/type/deck_type/DeckType.dart'; -import 'package:get/get_connect/http/src/response/response.dart'; - -class DeckTypeApi extends Net { - Future> getDetailByName(String name) => - httpClient.get('/api/v1/deck-types?name=$name&limit=1&aggregate=aboveThresh', decoder: DeckType.fromJson); -} diff --git a/lib/http/SkillApi.dart b/lib/http/SkillApi.dart deleted file mode 100644 index b66fc18..0000000 --- a/lib/http/SkillApi.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:duel_links_meta/type/skill/Skill.dart'; -import 'package:get/get_connect/http/src/response/response.dart'; - -class SkillApi extends Net { - // 获取技能列表 - Future> getByName(String name) => httpClient.get( - '/api/v1/skills?name[\$in]=$name&limit=1', - decoder: Skill.fromJson, - ); - - Future> getByCharacterId(String characterId) => - httpClient.get('/api/v1/skills?characters.character[\$or]=$characterId&archive[\$or]=true&rush[\$ne]=true&sort=name'); -} diff --git a/lib/http/SkillStatsApi.dart b/lib/http/SkillStatsApi.dart deleted file mode 100644 index 825e8c1..0000000 --- a/lib/http/SkillStatsApi.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:get/get_connect/connect.dart'; - -class SkillStatsApi extends Net { - Future> getByName(String name) => httpClient.get('/api/v1/skills/stats?name=$name'); -} diff --git a/lib/http/TierListApi.dart b/lib/http/TierListApi.dart deleted file mode 100644 index c924a54..0000000 --- a/lib/http/TierListApi.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking.dart'; -import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier.dart'; -import 'package:get/get_connect/http/src/response/response.dart'; - -class TierListApi extends Net { - Future>> getTopTiers() => - httpClient.get('/api/v1/deck-types?tier[\$in]=0,1,2,3,4&limit=0&sort=name&fields=name,tier', - decoder: (data) => (data as List).map(TierList_TopTier.fromJson).toList()); - - Future>> getPowerRankings() => httpClient.get( - '/api/v1/deck-types?rush[\$ne]=true&tournamentPower[\$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend', - decoder: (data) => (data as List).map(TierList_PowerRanking.fromJson).toList()); - - Future>> getRushRankings() => httpClient.get( - '/api/v1/deck-types?rush=true&tournamentPower[\$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend,rush', - decoder: (data) => (data as List).map(TierList_PowerRanking.fromJson).toList()); -} diff --git a/lib/http/TopDeckApi.dart b/lib/http/TopDeckApi.dart deleted file mode 100644 index 4a27597..0000000 --- a/lib/http/TopDeckApi.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; -import 'package:duel_links_meta/type/top_deck/TopDeckSimple.dart'; -import 'package:get/get_connect/http/src/response/response.dart'; - -class TopDeckApi extends Net { - Future> getBreakdownSample(Map params) => - httpClient.get('/api/v1/top-decks', query: params, decoder: TopDeck.fromJson); - - // Future> list(Map params) => - // httpClient.get('/api/v1/top-decks', query: params); - Future>> list(Map params) => httpClient.get('/api/v1/top-decks', query: params, decoder: (data) { - // return (data as List).map(TopDeckSimple.fromJson).toList(); - return (data as List).map(TopDeck.fromJson).toList(); - }); -} diff --git a/lib/http/WorldApi.dart b/lib/http/WorldApi.dart deleted file mode 100644 index 5fdfdcf..0000000 --- a/lib/http/WorldApi.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:duel_links_meta/http/http.dart'; -import 'package:get/get.dart'; - -class WorldApi extends Net { - Future> list() => httpClient.get('/api/v1/worlds'); -} diff --git a/lib/main.dart b/lib/main.dart index 0e0f98c..52ce508 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/pages/open_source_licenses/index.dart'; import 'package:duel_links_meta/pages/splash/index.dart'; +import 'package:duel_links_meta/store/AppStore.dart'; import 'package:duel_links_meta/widget/toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -27,110 +28,64 @@ void main() async { await initializeDateFormatting(); - runApp(const MyApp()); + final appStore = Get.put(AppStore()); + + runApp(MyApp(appStore: appStore)); } class MyApp extends StatelessWidget { - const MyApp({super.key}); + const MyApp({required this.appStore, super.key}); + + final AppStore appStore; @override Widget build(BuildContext context) { - return GetMaterialApp( - title: 'Duel Links Meta', - themeMode: ThemeMode.light, - // themeMode: ThemeMode.dark, - darkTheme: ThemeData.dark(useMaterial3: true).copyWith( - colorScheme: const ColorScheme.dark( - // seedColor: BaColors.main, - // onPrimary: Colors.yellow, - // onSecondary: Colors.tealAccent, - // background: Colors.yellow, - primary: Colors.pink, // tab bar indicator / navigation bar indicator - secondary: Colors.pinkAccent, - - // tertiary: Colors.orange, - // brightness: Brightness.dark, - // background: BaColors.main - ), - navigationBarTheme: NavigationBarTheme.of(context).copyWith( - backgroundColor: Colors.black, - // iconTheme: const MaterialStatePropertyAll( - // IconThemeData( - // size: 20, - // ), - // ), - iconTheme: MaterialStateProperty.resolveWith((Set states) { - // 这里可以根据不同的状态返回不同的 IconThemeData - // 如果没有特别的状态要求,可以返回一个默认的 IconThemeData - // 例如,在所有状态下使用相同的图标颜色和大小 - return const IconThemeData( - // color: Colors.white, // 设置为白色,以便在黑色背景上清晰可见 - // size: 20, // 设置图标大小 + return Obx(() => GetMaterialApp( + title: 'Duel Links Meta', + themeMode: ThemeMode.light, + darkTheme: ThemeData.dark(useMaterial3: true).copyWith( + colorScheme: ColorScheme.dark( + primary: appStore.themeColor, + ), + navigationBarTheme: NavigationBarTheme.of(context).copyWith( + iconTheme: MaterialStateProperty.resolveWith((Set states) { + // 这里可以根据不同的状态返回不同的 IconThemeData + // 如果没有特别的状态要求,可以返回一个默认的 IconThemeData + // 例如,在所有状态下使用相同的图标颜色和大小 + return const IconThemeData(); + }), + ), + // tabBarTheme: const TabBarTheme(labelColor: BaColors.theme, indicatorColor: BaColors.theme), + appBarTheme: const AppBarTheme( + elevation: 2, + centerTitle: true, + ), + ), + theme: ThemeData.light(useMaterial3: true).copyWith( + colorScheme: ColorScheme.light( + primary: appStore.themeColor, + ), + navigationBarTheme: NavigationBarTheme.of(context).copyWith( + iconTheme: MaterialStateProperty.resolveWith((Set states) { + // 这里可以根据不同的状态返回不同的 IconThemeData + // 如果没有特别的状态要求,可以返回一个默认的 IconThemeData + // 例如,在所有状态下使用相同的图标颜色和大小 + return IconThemeData( + color: states.contains(MaterialState.selected) ? Colors.white : Colors.black54, // 设置为白色,以便在黑色背景上清晰可见 + // size: 20, // 设置图标大小 ); - }), - ), - primaryColor: Colors.deepOrangeAccent, - // primarySwatch: Colors.yellow, - // scaffoldBackgroundColor: BaColors.theme, - // textTheme: TextTheme( - // ), - // useMaterial3: true, - - // tabBarTheme: const TabBarTheme(labelColor: BaColors.theme, indicatorColor: BaColors.theme), - appBarTheme: const AppBarTheme( - elevation: 2, - shadowColor: Color(0xff121212), - centerTitle: true, - surfaceTintColor: Colors.transparent, - // backgroundColor: BaColors.main - ), - ), - - theme: ThemeData.light(useMaterial3: true).copyWith( - colorScheme: const ColorScheme.light( - // seedColor: Colors.pink, - primary: Colors.blue, - secondary: Colors.blueAccent, - tertiary: Colors.deepOrange, - inversePrimary: Colors.deepPurple, - // onSecondary: Colors.green, - onSurface: Colors.black54, - // tertiary: Colors.orange, - // brightness: Brightness.dark, - ), - navigationBarTheme: NavigationBarTheme.of(context).copyWith( - backgroundColor: Colors.white, - surfaceTintColor: Colors.transparent, - iconTheme: MaterialStateProperty.resolveWith((Set states) { - // 这里可以根据不同的状态返回不同的 IconThemeData - // 如果没有特别的状态要求,可以返回一个默认的 IconThemeData - // 例如,在所有状态下使用相同的图标颜色和大小 - return IconThemeData( - color: states.contains(MaterialState.selected) ? Colors.white : Colors.black54, // 设置为白色,以便在黑色背景上清晰可见 - // size: 20, // 设置图标大小 - ); - }), - ), - primaryColor: Colors.pink, - // primaryColorDark: Colors.blue, - // scaffoldBackgroundColor: Colors.blueAccent, - // textTheme: TextTheme( - // ), - // useMaterial3: true, - // tabBarTheme: const TabBarTheme(labelColor: BaColors.theme, indicatorColor: BaColors.theme), - appBarTheme: const AppBarTheme( - elevation: 2, - shadowColor: Colors.white24, - centerTitle: true, - surfaceTintColor: Colors.transparent, - ), - ), - navigatorObservers: [FlutterSmartDialog.observer], - builder: FlutterSmartDialog.init( - toastBuilder: (msg) => ToastWidget(msg: msg), - ), - home: const SplashPage(), - // home: const OpenSourceLicensePage(), - ); + }), + ), + appBarTheme: const AppBarTheme( + elevation: 2, + centerTitle: true, + ), + ), + navigatorObservers: [FlutterSmartDialog.observer], + builder: FlutterSmartDialog.init( + toastBuilder: (msg) => ToastWidget(msg: msg), + ), + home: const SplashPage(), + )); } } diff --git a/lib/pages/articles/index.dart b/lib/pages/articles/index.dart index e7f0768..65d2afb 100644 --- a/lib/pages/articles/index.dart +++ b/lib/pages/articles/index.dart @@ -1,9 +1,9 @@ import 'dart:developer'; +import 'package:duel_links_meta/api/ArticleApi.dart'; import 'package:duel_links_meta/components/ListFooter.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/ArticleHiveDb.dart'; -import 'package:duel_links_meta/http/ArticleApi.dart'; import 'package:duel_links_meta/pages/articles/components/ArticleItem.dart'; import 'package:duel_links_meta/pages/webview/index.dart'; import 'package:duel_links_meta/type/Article.dart'; @@ -52,8 +52,12 @@ class _ArticlesPageState extends State with AutomaticKeepAliveClie 'limit': _listViewData.size.toString(), 'page': _listViewData.page.toString(), }; + params['field'] = '-markdown'; + params['sort'] = '-featured,-date'; + params[r'hidden[$ne]'] = 'true'; + params[r'category[$ne]'] = 'quick-news'; - final (err, list) = await ArticleApi().articleList(params).toCatch; + final (err, list) = await ArticleApi().articleList(params).toCatch; if (err != null || list == null) { if (isLoadMore) { setState(() { diff --git a/lib/pages/ban_list_change/components/BanListChangePickerView.dart b/lib/pages/ban_list_change/components/BanListChangePickerView.dart index 2392593..0c45904 100644 --- a/lib/pages/ban_list_change/components/BanListChangePickerView.dart +++ b/lib/pages/ban_list_change/components/BanListChangePickerView.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'dart:developer'; +import 'package:duel_links_meta/components/ModalBottomSheetWrap.dart'; import 'package:duel_links_meta/pages/ban_list_change/type/DataGroup.dart'; import 'package:duel_links_meta/type/ban_list_change/BanListChange.dart'; import 'package:flutter/cupertino.dart'; @@ -47,12 +47,10 @@ class _BanListChangePickerState extends State { @override Widget build(BuildContext context) { - return ClipRRect( - borderRadius: const BorderRadius.only(topLeft: Radius.circular(18), topRight: Radius.circular(18)), + return ModalBottomSheetWrap( child: Container( height: 240, padding: const EdgeInsets.only(top: 8), - color: Theme.of(context).colorScheme.onPrimary, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -118,7 +116,6 @@ class _BanListChangePickerState extends State { padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: ElevatedButton( onPressed: () { - log('confirm: $selectedYearKey, $selectedItemKey'); _defaultItemIndex = selectedItemKey; _defaultYearIndex = selectedYearKey; widget.onConfirm(selectedYearKey, selectedItemKey); diff --git a/lib/pages/ban_list_change/components/BanListChangeView.dart b/lib/pages/ban_list_change/components/BanListChangeView.dart index aa46cbe..e596d90 100644 --- a/lib/pages/ban_list_change/components/BanListChangeView.dart +++ b/lib/pages/ban_list_change/components/BanListChangeView.dart @@ -1,17 +1,15 @@ -import 'dart:developer'; - +import 'package:duel_links_meta/api/BanListChangeApi.dart'; +import 'package:duel_links_meta/components/cards_viewpager/index.dart'; +import 'package:duel_links_meta/extension/DateTime.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/BanListChangeHiveDb.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; -import 'package:duel_links_meta/http/BanListChangeApi.dart'; import 'package:duel_links_meta/pages/ban_list_change/components/BanListChangeCardView.dart'; import 'package:duel_links_meta/pages/ban_list_change/components/BanListChangePickerView.dart'; import 'package:duel_links_meta/pages/ban_list_change/type/DataGroup.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/ban_list_change/BanListChange.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; -import 'package:duel_links_meta/util/time_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:intl/intl.dart'; @@ -29,7 +27,6 @@ class _BanListChangeViewState extends State with AutomaticKee List> _banListChangeGroup = []; BanListChange? currentBanListChange; final formatter = DateFormat('MM-dd'); - bool _isInit = false; // Future fetchData({bool force = false}) async { @@ -127,6 +124,7 @@ class _BanListChangeViewState extends State with AutomaticKee void showUpdatesDatePicker() { showModalBottomSheet( context: context, + backgroundColor: Colors.transparent, builder: (context) => BanListChangePicker( data: _banListChangeGroup, onConfirm: handlePickerConfirm, @@ -137,7 +135,7 @@ class _BanListChangeViewState extends State with AutomaticKee Future _handleRefresh() async { final shouldRefresh = await fetchData(); - _isInit = true; + if (shouldRefresh) { await fetchData(force: true); } @@ -181,7 +179,7 @@ class _BanListChangeViewState extends State with AutomaticKee onTap: showUpdatesDatePicker, child: Row( children: [ - Text(TimeUtil.format(currentBanListChange?.date ?? currentBanListChange?.announced)), + Text((currentBanListChange?.date ?? currentBanListChange?.announced)?.format ?? ''), const Icon(Icons.keyboard_arrow_down, size: 16), ], ), @@ -207,7 +205,7 @@ class _BanListChangeViewState extends State with AutomaticKee if (_pageStatus == PageStatus.fail) const Center( child: Text('Loading failed'), - ) + ), ], ), ); diff --git a/lib/pages/ban_list_change/components/BanStatusCardView.dart b/lib/pages/ban_list_change/components/BanStatusCardView.dart index 6265071..5bc5ca4 100644 --- a/lib/pages/ban_list_change/components/BanStatusCardView.dart +++ b/lib/pages/ban_list_change/components/BanStatusCardView.dart @@ -1,6 +1,6 @@ import 'package:duel_links_meta/components/Loading.dart'; import 'package:duel_links_meta/components/MdCardItemView2.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; +import 'package:duel_links_meta/components/cards_viewpager/index.dart'; import 'package:duel_links_meta/pages/top_decks/type/Group.dart'; import 'package:duel_links_meta/store/BanCardStore.dart'; import 'package:duel_links_meta/type/MdCard.dart'; @@ -40,7 +40,7 @@ class _BanStatusCardViewState extends State with AutomaticKee } Future init() async { - await Future.delayed(const Duration(milliseconds: 200)); + await Future.delayed(const Duration(milliseconds: 200)); setState(() { _initFlag = true; }); @@ -141,16 +141,20 @@ class _BanStatusCardViewState extends State with AutomaticKee : const SizedBox(); }, ), - Obx(() => banCardsStore.pageStatus.value == PageStatus.fail - ? const SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - ) - : const SizedBox()), - Obx(() => banCardsStore.pageStatus.value == PageStatus.fail - ? const Center( - child: Text('Loading failed'), - ) - : const SizedBox(),), + Obx( + () => banCardsStore.pageStatus.value == PageStatus.fail + ? const SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + ) + : const SizedBox(), + ), + Obx( + () => banCardsStore.pageStatus.value == PageStatus.fail + ? const Center( + child: Text('Loading failed'), + ) + : const SizedBox(), + ), ], ), ); diff --git a/lib/pages/cards_viewpager/CardView2.dart b/lib/pages/cards_viewpager/CardView2.dart deleted file mode 100644 index 09c2d3d..0000000 --- a/lib/pages/cards_viewpager/CardView2.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:duel_links_meta/constant/colors.dart'; -import 'package:duel_links_meta/type/MdCard.dart'; -import 'package:duel_links_meta/util/time_util.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -import '../../components/IfElseBox.dart'; - -class CardView extends StatelessWidget { - const CardView({super.key, required this.card}); - - final MdCard card; - - @override - Widget build(BuildContext context) { - return ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(14)), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8), - decoration: BoxDecoration( - image: DecorationImage(image: AssetImage('assets/images/modal_bg.webp'), fit: BoxFit.fitWidth), - color: Theme.of(context).colorScheme.background, - // color: BaColors.main, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 200, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [Image.asset('assets/images/rarity_${card.rarity.toLowerCase()}.webp', height: 20)], - ), - Container( - width: 200, - height: 200 * 1.46, - child: Stack( - children: [ - CachedNetworkImage( - fit: BoxFit.cover, - width: double.infinity, - imageUrl: 'https://s3.duellinksmeta.com/cards/${card.oid}_w100.webp', - ), - Positioned( - top: 0, - left: 0, - right: 0, - bottom: 0, - child: CachedNetworkImage( - fit: BoxFit.cover, - imageUrl: 'https://s3.duellinksmeta.com/cards/${card.oid}_w420.webp', - )) - ], - ), - ) - ], - ), - ), - Container( - padding: const EdgeInsets.only(top: 12), - height: 160, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - card.name, - overflow: TextOverflow.ellipsis, - maxLines: 2, - style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600), - ), - ), - Row( - children: [ - if (card.type == 'Monster') - Row( - children: [ - Image.asset('assets/images/icon_attribute_${card.attribute!.toLowerCase()}.png', width: 12, height: 12), - const SizedBox(width: 2), - Text(card.attribute!, style: const TextStyle( fontSize: 10)), - if (card.level != null) - Row( - children: [ - const SizedBox(width: 4), - if (card.monsterType.contains('Xyz')) - Image.asset('assets/images/icon_normal_rank.png', width: 12, height: 12) - else - Image.asset('assets/images/icon_normal_level.png', width: 12, height: 12), - const SizedBox(width: 2), - Text(card.level?.toString() ?? '', style: const TextStyle( fontSize: 10)) - ], - ) - ], - ), - if (card.type == 'Spell' || card.type == 'Trap') - Row( - children: [ - Image.asset('assets/images/icon_card_type_${card.type.toLowerCase()}.png', width: 12, height: 12), - const SizedBox(width: 2), - Text(card.type, style: const TextStyle( fontSize: 10)), - const SizedBox(width: 4), - Image.asset('assets/images/icon_card_race_${card.race.toLowerCase()}.png', width: 12, height: 12), - // Image.asset('assets/images/icon_card_race_ritual.png', width: 12, height: 12), - const SizedBox(width: 2), - - Text(card.race.toLowerCase(), style: const TextStyle( fontSize: 10)) - ], - ), - ], - ) - ], - ), - if (card.monsterType.isNotEmpty) Row( - children: [ - const SizedBox(height: 4), - Text('[${card.monsterType.join('/')}]', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w500)), - ], - ), - const SizedBox(height: 4), - Text(card.description, style: const TextStyle( fontSize: 12)), - const SizedBox(height: 4), - - if (card.atk != null) - Row( - children: [ - const Text('ATK', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600)), - Text('/${card.atk}', style: const TextStyle( fontSize: 12)), - const SizedBox(width: 4), - IfElseBox( - condition: card.monsterType.contains('Link'), - ifTure: Row( - children: [ - const Text('LINK', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600)), - Text('/${card.linkRating ?? 0}', style: const TextStyle( fontSize: 12)), - ], - ), - elseTrue: Row(children: [ - const Text('DEF', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600)), - Text('/${card.def ?? 0}', style: const TextStyle( fontSize: 12)), - ]), - ), - ], - ), - const Text( - 'How to Obtain', - style: TextStyle( - fontSize: 12, - decoration: TextDecoration.underline, - decorationStyle: TextDecorationStyle.solid), - ), - const SizedBox(height: 10), - if (card.release != null) - Text( - 'Released on ${TimeUtil.format(card.release)}', - style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 12), - ) - ], - ), - )) - ], - ), - ), - ); - } -} diff --git a/lib/pages/character/index.dart b/lib/pages/character/index.dart index f994baa..c55c7dd 100644 --- a/lib/pages/character/index.dart +++ b/lib/pages/character/index.dart @@ -1,23 +1,19 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/api/CardApi.dart'; +import 'package:duel_links_meta/api/SkillApi.dart'; import 'package:duel_links_meta/components/IfElseBox.dart'; -import 'package:duel_links_meta/components/MdCardItemView.dart'; import 'package:duel_links_meta/components/MdCardsBoxLayout.dart'; -import 'package:duel_links_meta/constant/colors.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; -import 'package:duel_links_meta/http/SkillApi.dart'; +import 'package:duel_links_meta/components/SkillModalView.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/character/Character.dart'; +import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/skill/Skill.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; -import '../../components/SkillModalView.dart'; -import '../../type/enum/PageStatus.dart'; -import '../cards_viewpager/index.dart'; +import '../../components/cards_viewpager/index.dart'; class CharacterPage extends StatefulWidget { const CharacterPage({super.key, required this.character}); @@ -60,10 +56,10 @@ class _CharacterPageState extends State { ); } - handleTapSkillItem(int index) { - var skill = _skills[index]; + void handleTapSkillItem(int index) { + final skill = _skills[index]; - showDialog( + showDialog( context: context, builder: (context) => Dialog.fullscreen( backgroundColor: Colors.black.withOpacity(0.2), @@ -76,7 +72,7 @@ class _CharacterPageState extends State { } - Future _handleRefresh() async { + Future _handleRefresh() async { if (_pageStatus == PageStatus.success) return; var [skillRes, cardsRes] = await Future.wait([ @@ -113,7 +109,6 @@ class _CharacterPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: BaColors.theme, body: RefreshIndicator( key: _refreshIndicatorKey, onRefresh: _handleRefresh, @@ -131,7 +126,7 @@ class _CharacterPageState extends State { Positioned.fill( child: Container( decoration: BoxDecoration( - gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [BaColors.theme, BaColors.theme.withOpacity(0)]), + // gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [BaColors.theme, BaColors.theme.withOpacity(0)]), ), ), ), @@ -215,10 +210,10 @@ class _CharacterPageState extends State { itemCount: _dropedCards.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5, crossAxisSpacing: 6, childAspectRatio: 0.55), itemBuilder: (context, index) { - return MdCardItemView( - mdCard: _dropedCards[index], - onTap: (card) => handleTapCardItem(_dropedCards, index), - ); + // return MdCardItemView( + // mdCard: _dropedCards[index], + // onTap: (card) => handleTapCardItem(_dropedCards, index), + // ); }), ), const Padding( @@ -233,10 +228,10 @@ class _CharacterPageState extends State { itemCount: _levelUpCards.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5, crossAxisSpacing: 6, childAspectRatio: 0.55), itemBuilder: (context, index) { - return MdCardItemView( - mdCard: _levelUpCards[index], - onTap: (card) => handleTapCardItem(_levelUpCards, index), - ); + // return MdCardItemView( + // mdCard: _levelUpCards[index], + // onTap: (card) => handleTapCardItem(_levelUpCards, index), + // ); }), ), ], diff --git a/lib/pages/characters/index.dart b/lib/pages/characters/index.dart index 1c14633..bc20a10 100644 --- a/lib/pages/characters/index.dart +++ b/lib/pages/characters/index.dart @@ -1,18 +1,14 @@ -import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/api/CharacterApi.dart'; +import 'package:duel_links_meta/api/WorldApi.dart'; import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/constant/colors.dart'; -import 'package:duel_links_meta/http/CharacterApi.dart'; import 'package:duel_links_meta/pages/character/index.dart'; import 'package:duel_links_meta/type/character/Character.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:duel_links_meta/type/enum/PageStatus.dart'; +import 'package:duel_links_meta/type/world/World.dart'; import 'package:flutter/material.dart'; -import '../../http/WorldApi.dart'; -import '../../type/enum/PageStatus.dart'; -import '../../type/world/World.dart'; - class CharactersPage extends StatefulWidget { const CharactersPage({super.key}); @@ -52,10 +48,10 @@ class _CharactersPageState extends State { _characters = []; }); var worldsRes = await WorldApi().list(); - var worlds = worldsRes.body!.map((e) => World.fromJson(e)).toList(); + var worlds = worldsRes.body!.map(World.fromJson).toList(); var res = await CharacterApi().list(); - var list = res.body!.map((e) => Character.fromJson(e)).toList(); + var list = res.body!.map(Character.fromJson).toList(); setState(() { _characters = list; @@ -74,9 +70,7 @@ class _CharactersPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: BaColors.theme, appBar: AppBar( - backgroundColor: BaColors.main, title: Row( children: [ if (_index != null) @@ -127,7 +121,6 @@ class _CharactersPageState extends State { itemCount: _visibleCharacters.length, itemBuilder: (context, index) { return Card( - color: BaColors.main, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), diff --git a/lib/pages/deck_detail/components/DeckInfo.dart b/lib/pages/deck_detail/components/DeckInfo.dart index 3575056..4b7cedf 100644 --- a/lib/pages/deck_detail/components/DeckInfo.dart +++ b/lib/pages/deck_detail/components/DeckInfo.dart @@ -1,17 +1,16 @@ import 'dart:developer'; +import 'package:duel_links_meta/api/CardApi.dart'; +import 'package:duel_links_meta/api/TopDeckApi.dart'; import 'package:duel_links_meta/components/MdCardItemView2.dart'; +import 'package:duel_links_meta/components/cards_viewpager/index.dart'; +import 'package:duel_links_meta/extension/DateTime.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; -import 'package:duel_links_meta/http/TopDeckApi.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; -import 'package:duel_links_meta/util/time_util.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class DeckInfo extends StatefulWidget { @@ -20,6 +19,7 @@ class DeckInfo extends StatefulWidget { final TopDeck? topDeck; final String? deckTypeId; final bool? loadingVisible; + // final void Function(PageStatus pageStatus)? onPageStatusUpdate; @override @@ -112,15 +112,12 @@ class _DeckInfoState extends State { final extraCardIds = topDeck.extra.map((e) => e.card.oid).toList(); mainCardIds.addAll(extraCardIds); - log('mainCardIds length: ${mainCardIds.length}'); - final needFetchCardIds = []; final cards = []; for (var i = 0; i < mainCardIds.length; i++) { final hiveData = await CardHiveDb().get(mainCardIds[i]); if (hiveData == null) { - log('hiveData为null, ${mainCardIds[i]}'); needFetchCardIds.add(mainCardIds[i]); } else { cards.add(hiveData); @@ -128,8 +125,7 @@ class _DeckInfoState extends State { } if (needFetchCardIds.isNotEmpty) { - log('需要请求获取card $needFetchCardIds, length: ${needFetchCardIds.length}'); - final (cardsErr, cardsRes) = await CardApi().getByIds(needFetchCardIds.join(',')).toCatch; + final (_, cardsRes) = await CardApi().getByIds(needFetchCardIds.join(',')).toCatch; if (cardsRes != null) { cards.addAll(cardsRes); cardsRes.forEach((element) { @@ -208,7 +204,7 @@ class _DeckInfoState extends State { const Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: Text('—', style: TextStyle(fontSize: 12))), Text(sampleDeckTournamentName, style: const TextStyle(color: Color(0xff0a87bb), fontSize: 12)), const Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: Text('—', style: TextStyle(fontSize: 12))), - if (_topDeck != null) Text(TimeUtil.format(_topDeck?.created), style: const TextStyle(fontSize: 12)), + if (_topDeck != null) Text(_topDeck?.created?.format ?? '', style: const TextStyle(fontSize: 12)), const Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: Text('—', style: TextStyle(fontSize: 12))), if (_topDeck != null) Text(_topDeck!.author is String ? _topDeck!.author.toString() : '', style: const TextStyle(fontSize: 12)), @@ -224,8 +220,10 @@ class _DeckInfoState extends State { Assets.images.iconGem.image(width: 15, height: 15), const SizedBox(width: 4), Text(formatToK(_topDeck?.gemsPrice ?? 0), style: const TextStyle(fontSize: 11)), - if ((_topDeck?.dollarsPrice ?? 0) > 0) const Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: Text('+', style: TextStyle(fontSize: 11))), - if ((_topDeck?.dollarsPrice ?? 0) > 0) Text('\$${_topDeck?.dollarsPrice.toString() ?? '0'}', style: const TextStyle(fontSize: 11)), + if ((_topDeck?.dollarsPrice ?? 0) > 0) + const Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: Text('+', style: TextStyle(fontSize: 11))), + if ((_topDeck?.dollarsPrice ?? 0) > 0) + Text('\$${_topDeck?.dollarsPrice.toString() ?? '0'}', style: const TextStyle(fontSize: 11)), ], ), ), @@ -261,9 +259,7 @@ class _DeckInfoState extends State { ), ], ), - const SizedBox(height: 10), - Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), margin: EdgeInsets.zero, @@ -271,7 +267,10 @@ class _DeckInfoState extends State { padding: const EdgeInsets.only(left: 8, right: 8, top: 8), child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 5, mainAxisSpacing: 0, crossAxisSpacing: 8, childAspectRatio: 0.57), + crossAxisCount: 5, + crossAxisSpacing: 8, + childAspectRatio: 0.57, + ), padding: EdgeInsets.zero, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -295,7 +294,10 @@ class _DeckInfoState extends State { padding: const EdgeInsets.only(left: 8, right: 8, top: 8), child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 8, mainAxisSpacing: 0, crossAxisSpacing: 8, childAspectRatio: 0.53), + crossAxisCount: 8, + crossAxisSpacing: 8, + childAspectRatio: 0.53, + ), padding: EdgeInsets.zero, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -309,18 +311,17 @@ class _DeckInfoState extends State { }, ), ), - ) + ), ], ), ), - if (_loadingVisible && _pageStatus != PageStatus.success) const SizedBox( height: 200, child: Center( child: CircularProgressIndicator(), ), - ) + ), ], ); } diff --git a/lib/pages/deck_detail/index.dart b/lib/pages/deck_detail/index.dart index a18a94a..a86cd7d 100644 --- a/lib/pages/deck_detail/index.dart +++ b/lib/pages/deck_detail/index.dart @@ -1,5 +1,4 @@ -import 'dart:developer'; - +import 'package:duel_links_meta/api/TopDeckApi.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/pages/deck_detail/components/DeckInfo.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; @@ -7,8 +6,6 @@ import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import '../../http/TopDeckApi.dart'; - class DeckDetailPage extends StatefulWidget { const DeckDetailPage({required this.topDeck, super.key}); diff --git a/lib/pages/deck_type_detail/components/DeckTypeBreakdownGridView.dart b/lib/pages/deck_type_detail/components/DeckTypeBreakdownGridView.dart index ef1f5e9..ad02238 100644 --- a/lib/pages/deck_type_detail/components/DeckTypeBreakdownGridView.dart +++ b/lib/pages/deck_type_detail/components/DeckTypeBreakdownGridView.dart @@ -1,15 +1,11 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:duel_links_meta/components/MdCardItemView.dart'; -import 'package:duel_links_meta/components/MdCardsBoxLayout.dart'; -import 'package:duel_links_meta/constant/colors.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; +import 'package:duel_links_meta/components/MdCardItemView2.dart'; +import 'package:duel_links_meta/components/cards_viewpager/index.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/deck_type/DeckType.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class DeckTypeBreakdownGridView extends StatefulWidget { - const DeckTypeBreakdownGridView({super.key, required this.cards, cross, required this.crossAxisCount}); + const DeckTypeBreakdownGridView({required this.cards, required this.crossAxisCount, super.key}); final List cards; final int crossAxisCount; @@ -23,7 +19,7 @@ class _DeckTypeBreakdownGridViewState extends State { int get crossAxisCount => widget.crossAxisCount; - List get _cards { + List get _cards { return cards.map((e) => e.card).toList(); } @@ -32,7 +28,7 @@ class _DeckTypeBreakdownGridViewState extends State { context: context, builder: (context) => Dialog.fullscreen( backgroundColor: Colors.black87.withOpacity(0.3), - child: CardsViewpagerPage(cards: _cards, index: index) + child: CardsViewpagerPage(cards: _cards, index: index), ), ); } @@ -40,9 +36,7 @@ class _DeckTypeBreakdownGridViewState extends State { @override Widget build(BuildContext context) { return Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6) - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.only(left: 8, right: 8, top: 8), @@ -51,9 +45,10 @@ class _DeckTypeBreakdownGridViewState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: cards.length, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: crossAxisCount, mainAxisSpacing: 0, crossAxisSpacing: 6, childAspectRatio: 0.55), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, mainAxisSpacing: 0, crossAxisSpacing: 6, childAspectRatio: 0.55), itemBuilder: (BuildContext context, int index) { - return MdCardItemView( + return MdCardItemView2( mdCard: cards[index].card, trend: cards[index].trend, onTap: (card) => handleTapCardItem(index), @@ -67,6 +62,7 @@ class _DeckTypeBreakdownGridViewState extends State { ), ), ), + id: cards[index].card.oid, ); }, ), diff --git a/lib/pages/deck_type_detail/index.dart b/lib/pages/deck_type_detail/index.dart index 8569e2b..2f26b07 100644 --- a/lib/pages/deck_type_detail/index.dart +++ b/lib/pages/deck_type_detail/index.dart @@ -1,18 +1,15 @@ -import 'dart:developer'; import 'dart:ui'; -import 'package:animations/animations.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/api/DeckTypeApi.dart'; import 'package:duel_links_meta/components/SkillModalView.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/DeckTypeDetailHiveDb.dart'; -import 'package:duel_links_meta/http/DeckTypeApi.dart'; import 'package:duel_links_meta/pages/deck_detail/components/DeckInfo.dart'; import 'package:duel_links_meta/pages/deck_type_detail/components/DeckTypeBreakdownGridView.dart'; import 'package:duel_links_meta/store/AppStore.dart'; import 'package:duel_links_meta/type/deck_type/DeckType.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:get/get.dart'; @@ -187,42 +184,14 @@ class _DeckTypeDetailPageState extends State { gradient: LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, - // colors: [Theme.of(context).colorScheme.background, BaColors.theme.withOpacity(0)]), colors: [ Theme.of(context).scaffoldBackgroundColor, - Theme.of(context).scaffoldBackgroundColor.withOpacity(0) + Theme.of(context).scaffoldBackgroundColor.withOpacity(0), ], ), ), ), ), - // Positioned( - // bottom: 200, - // left: 0, - // right: 0, - // child: Padding( - // padding: const EdgeInsets.only(left: 8, bottom: 18), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text(_deckTypeName, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w500)), - // AnimatedOpacity( - // opacity: _pageStatus == PageStatus.success ? 1 : 0, - // duration: const Duration(milliseconds: 300), - // child: Row( - // children: [ - // const Text('Average size: ', style: TextStyle(fontSize: 12)), - // Text(_deckType?.deckBreakdown.avgMainSize.toString() ?? '', - // style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500)), - // const SizedBox(width: 3), - // const Text('cards', style: TextStyle(fontSize: 12)), - // ], - // ), - // ), - // ], - // ), - // ), - // ) ], ), AnimatedOpacity( @@ -268,27 +237,30 @@ class _DeckTypeDetailPageState extends State { Column( children: _deckType?.deckBreakdown.skills .where((item) => ((item.count) / _deckType!.deckBreakdown.total).round() > 0) - .map((skill) => InkWell( - onTap: () => handleTapSkill(skill), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 2), - child: Row( - children: [ - Expanded( - child: Text( - skill.name + skill.name, - style: const TextStyle(color: Color(0xff0a87bb)), - overflow: TextOverflow.ellipsis, - ), + .map( + (skill) => InkWell( + onTap: () => handleTapSkill(skill), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 2), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Text( + skill.name + skill.name, + style: const TextStyle(color: Color(0xff0a87bb)), + overflow: TextOverflow.ellipsis, ), - const SizedBox(width: 4), - Text( - ': ${(skill.count * 100 / _deckType!.deckBreakdown.total).toStringAsFixed(0)}%', - style: const TextStyle(fontSize: 12)) - ], - ), + ), + const SizedBox(width: 4), + Text( + ': ${(skill.count * 100 / _deckType!.deckBreakdown.total).toStringAsFixed(0)}%', + style: const TextStyle(fontSize: 12)), + ], ), - )) + ), + ), + ) .toList() ?? [], ), diff --git a/lib/pages/farming_and_event/components/EventListView.dart b/lib/pages/farming_and_event/components/EventListView.dart index 7c51912..36789e4 100644 --- a/lib/pages/farming_and_event/components/EventListView.dart +++ b/lib/pages/farming_and_event/components/EventListView.dart @@ -1,9 +1,7 @@ -import 'dart:developer'; - +import 'package:duel_links_meta/api/ArticleApi.dart'; import 'package:duel_links_meta/components/ListFooter.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/ArticleHiveDb.dart'; -import 'package:duel_links_meta/http/ArticleApi.dart'; import 'package:duel_links_meta/pages/articles/components/ArticleItem.dart'; import 'package:duel_links_meta/pages/farming_and_event/type/TabType.dart'; import 'package:duel_links_meta/pages/webview/index.dart'; diff --git a/lib/pages/home/index.dart b/lib/pages/home/index.dart index 0f09d33..78c6ada 100644 --- a/lib/pages/home/index.dart +++ b/lib/pages/home/index.dart @@ -1,8 +1,8 @@ +import 'package:duel_links_meta/api/NavTabApi.dart'; import 'package:duel_links_meta/components/SettingModalView.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/hive/db/NavHiveDb.dart'; -import 'package:duel_links_meta/http/NavTabApi.dart'; import 'package:duel_links_meta/pages/farming_and_event/index.dart'; import 'package:duel_links_meta/pages/home/components/NavItemCard.dart'; import 'package:duel_links_meta/pages/home/type/NavTabType.dart'; @@ -35,13 +35,19 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin NavTab(id: NavTabType.topDecksRush.value, title: 'TOP DECKS: RUSH'), NavTab(id: NavTabType.farmingAndEvents.value, title: 'FARMING & EVENTS'), NavTab(id: NavTabType.leaksAndUpdates.value, title: 'LEAKS & UPDATES', url: 'https://www.duellinksmeta.com/leaks-and-updates'), - NavTab(id: NavTabType.genGuide.value, title: 'GEM GUIDE'), + NavTab(id: NavTabType.genGuide.value, title: 'GEM GUIDE', url: 'https://www.duellinksmeta.com/gem-guide'), NavTab(id: NavTabType.deckBuilder.value, title: 'DECK BUILDER', url: 'https://www.duellinksmeta.com/deck-tester/'), NavTab(id: NavTabType.tournaments.value, title: 'TOURNAMENTS', url: 'https://www.duellinksmeta.com/tournaments'), NavTab(id: NavTabType.duelAssist.value, title: 'DUEL ASSIST', url: 'https://www.duellinksmeta.com/duel-assist'), NavTab(id: NavTabType.packOpener.value, title: 'PACK OPENER', url: 'https://www.duellinksmeta.com/pack-opening-simulator'), ]; + List get _showNavTabs { + if (appStore.showWebviewNavs.value) return _navTabs; + + return _navTabs.where((element) => element.url == null).toList(); + } + // void handleTapNav(NavTab nav) { if (nav.id == NavTabType.tierList.value) { @@ -79,10 +85,10 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin Future toggleDarkMode() async { if (Get.isDarkMode) { Get.changeThemeMode(ThemeMode.light); - MyHive.box2.put('dark_mode', 'light').ignore(); + MyHive.box.put('dark_mode', 'light').ignore(); } else { Get.changeThemeMode(ThemeMode.dark); - MyHive.box2.put('dark_mode', 'dark').ignore(); + MyHive.box.put('dark_mode', 'dark').ignore(); } } @@ -141,6 +147,7 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin Future handleOpenSettingDialog() async { await showModalBottomSheet( context: context, + backgroundColor: Colors.transparent, builder: (context) { return const SettingModalView(); }, @@ -165,6 +172,9 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin appBar: AppBar( automaticallyImplyLeading: false, title: const Text('Duel Links Meta'), + // foregroundColor: Colors.pink, + // backgroundColor: Colors.pink, + // surfaceTintColor: Colors.pink, actions: [ IconButton(onPressed: handleOpenSettingDialog, icon: const Icon(Icons.settings_rounded)), ], @@ -172,7 +182,7 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin body: RefreshIndicator( onRefresh: handleRefresh, key: _refreshIndicatorKey, - child: GridView.builder( + child: Obx(() => GridView.builder( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, @@ -180,14 +190,14 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin crossAxisSpacing: 8, childAspectRatio: 2, ), - itemCount: _navTabs.length, + itemCount: _showNavTabs.length, itemBuilder: (BuildContext context, int index) { return GestureDetector( - onTap: () => handleTapNav(_navTabs[index]), - child: NavItemCard(navTab: _navTabs[index]), + onTap: () => handleTapNav(_showNavTabs[index]), + child: NavItemCard(navTab: _showNavTabs[index]), ); }, - ), + )), ), ); } diff --git a/lib/pages/main/index.dart b/lib/pages/main/index.dart index b08a40e..5eac318 100644 --- a/lib/pages/main/index.dart +++ b/lib/pages/main/index.dart @@ -50,7 +50,7 @@ class _MainPageState extends State { selectedIndex: _selectedIndex, onDestinationSelected: _onItemTapped, height: 70, - indicatorColor: Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.3), + indicatorColor: Theme.of(context).colorScheme.primary.withOpacity(0.4), // surfaceTintColor: Colors.tealAccent, // surfaceTintColor: Colors.transparent, // backgroundColor: Theme.of(context).colorScheme.primary, diff --git a/lib/pages/pack_detail/index.dart b/lib/pages/pack_detail/index.dart index f5ca9e9..868a692 100644 --- a/lib/pages/pack_detail/index.dart +++ b/lib/pages/pack_detail/index.dart @@ -1,17 +1,13 @@ -import 'dart:developer'; - import 'package:cached_network_image/cached_network_image.dart'; -import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/components/MdCardItemView.dart'; +import 'package:duel_links_meta/api/CardApi.dart'; +import 'package:duel_links_meta/components/MdCardItemView2.dart'; +import 'package:duel_links_meta/components/cards_viewpager/index.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; import 'package:duel_links_meta/hive/db/PackHiveDb.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/pack_set/PackSet.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -179,9 +175,9 @@ class _PackDetailPageState extends State { crossAxisSpacing: 6, ), itemBuilder: (context, index) { - return MdCardItemView( + return MdCardItemView2( mdCard: rarity2CardsGroup[key]![index], - onTap: (card) => handleTapCardItem(rarity2CardsGroup[key]!, index), + onTap: (card) => handleTapCardItem(rarity2CardsGroup[key]!, index), id: rarity2CardsGroup[key]![index].oid, ); // return Container(color: Colors.white,); }, diff --git a/lib/pages/packs/index.dart b/lib/pages/packs/index.dart index 6d622a2..cf5a58a 100644 --- a/lib/pages/packs/index.dart +++ b/lib/pages/packs/index.dart @@ -1,8 +1,7 @@ -import 'dart:developer'; +import 'package:duel_links_meta/api/PackSetApi.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/PacksHiveDb.dart'; -import 'package:duel_links_meta/http/PackSetApi.dart'; import 'package:duel_links_meta/pages/packs/components/PackListView.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/pack_set/PackSet.dart'; diff --git a/lib/pages/skill_stats/index.dart b/lib/pages/skill_stats/index.dart index 202dce4..a3faa6a 100644 --- a/lib/pages/skill_stats/index.dart +++ b/lib/pages/skill_stats/index.dart @@ -1,6 +1,5 @@ +import 'package:duel_links_meta/api/SkillStatsApi.dart'; import 'package:duel_links_meta/components/SkillModalView.dart'; -import 'package:duel_links_meta/constant/colors.dart'; -import 'package:duel_links_meta/http/SkillStatsApi.dart'; import 'package:duel_links_meta/type/skill_stats/SkillStats.dart'; import 'package:flutter/material.dart'; @@ -17,13 +16,13 @@ class _SkillStatsState extends State { String get name => widget.name; List _skillStats = []; - fetchData() async { + Future fetchData() async { setState(() { _skillStats = []; }); - var res = await SkillStatsApi().getByName(name); - var list = res.body!.map((e) => SkillStats.fromJson(e)).toList(); + final res = await SkillStatsApi().getByName(name); + final list = res.body!.map(SkillStats.fromJson).toList(); setState(() { _skillStats = list; @@ -39,9 +38,7 @@ class _SkillStatsState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: BaColors.theme, appBar: AppBar( - backgroundColor: BaColors.main, title: Text(name, style: const TextStyle(color: Colors.white)), actions: [ IconButton(onPressed: fetchData, icon: const Icon(Icons.refresh)), diff --git a/lib/pages/splash/BanStatusCardHiveDb.dart b/lib/pages/splash/BanStatusCardHiveDb.dart index d230152..d9f8f5d 100644 --- a/lib/pages/splash/BanStatusCardHiveDb.dart +++ b/lib/pages/splash/BanStatusCardHiveDb.dart @@ -9,7 +9,7 @@ class BanStatusCardHiveDb { static Future?> getCardIds() async { List? cardIds; try { - cardIds = await MyHive.box2.get(_banStatusCardIdsKey) as List?; + cardIds = await MyHive.box.get(_banStatusCardIdsKey) as List?; } catch (e) { log('转换失败 $e'); @@ -22,7 +22,7 @@ class BanStatusCardHiveDb { static Future getExpireTime() async { DateTime? expireTime; try { - expireTime = await MyHive.box2.get(_banStatusCardIdsFetchDateKey) as DateTime?; + expireTime = await MyHive.box.get(_banStatusCardIdsFetchDateKey) as DateTime?; } catch (e) { log('转换失败 $e'); return null; @@ -32,10 +32,10 @@ class BanStatusCardHiveDb { } static Future setCardIds(List ids) { - return MyHive.box2.put(_banStatusCardIdsKey, ids); + return MyHive.box.put(_banStatusCardIdsKey, ids); } static Future setExpireTime(DateTime expireTime) { - return MyHive.box2.put(_banStatusCardIdsFetchDateKey, expireTime); + return MyHive.box.put(_banStatusCardIdsFetchDateKey, expireTime); } } diff --git a/lib/pages/splash/index.dart b/lib/pages/splash/index.dart index 0f7dbd0..3bc65f5 100644 --- a/lib/pages/splash/index.dart +++ b/lib/pages/splash/index.dart @@ -1,15 +1,10 @@ import 'dart:async'; -import 'dart:developer'; - -import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; +import 'dart:math' as math; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:duel_links_meta/gen/assets.gen.dart'; import 'package:duel_links_meta/hive/db/DarkModeHiveDb.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; import 'package:duel_links_meta/pages/main/index.dart'; -import 'package:duel_links_meta/pages/splash/BanStatusCardHiveDb.dart'; import 'package:duel_links_meta/store/BanCardStore.dart'; -import 'package:duel_links_meta/type/MdCard.dart'; -import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -20,10 +15,29 @@ class SplashPage extends StatefulWidget { State createState() => _SplashPageState(); } -class _SplashPageState extends State { - int _count = 1; +class BgItem { + BgItem({required this.url, required this.scale}); + + String url; + double scale; +} + +class _SplashPageState extends State with TickerProviderStateMixin { + int _count = 5; late Timer _timer; final banCardStore = Get.put(BanCardStore()); + late Animation _scaleAnimation; + late AnimationController _animationController; + late Animation _slideAnimation; + int index = 0; + double positionTop = 0; + String placeholder = + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/91c64743-70df-48d8-bc46-032dcb781164/deyge89-7a17c4e8-bea6-420e-93fa-2133104a0fd1.png/v1/fill/w_1120,h_713/blue_eyes_alternative_white_dragon_render_by_d_evil6661_deyge89-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTMwNCIsInBhdGgiOiJcL2ZcLzkxYzY0NzQzLTcwZGYtNDhkOC1iYzQ2LTAzMmRjYjc4MTE2NFwvZGV5Z2U4OS03YTE3YzRlOC1iZWE2LTQyMGUtOTNmYS0yMTMzMTA0YTBmZDEucG5nIiwid2lkdGgiOiI8PTIwNDgifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.qLwRAIvBxtGlcd9K1aL9Hyrwudo5zDvq3skuSfE9N3A'; + + String bgBlueFlexUrl = 'https://img.konami.com/yugioh/masterduel/images/background-blueflex.png'; + String bgRb = 'https://img.konami.com/yugioh/masterduel/images/background-fixed-rb.png'; + String bgTl = 'https://img.konami.com/yugioh/masterduel/images/background-fixed-tl.png'; + String bgUrl = 'https://img.konami.com/yugioh/masterduel/images/background-sparks.jpg'; @override void dispose() { @@ -32,17 +46,24 @@ class _SplashPageState extends State { _timer.cancel(); } + void navigateToHomePage() { + _animationController.dispose(); + + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) => const MainPage()), + (route) => false, //if you want to disable back feature set to false + ); + } + // void startCounterDown() { _timer = Timer.periodic(const Duration(seconds: 1), (timer) async { if (_count <= 0) { + timer.cancel(); - await Navigator.pushAndRemoveUntil( - context, - MaterialPageRoute(builder: (context) => const MainPage()), - (route) => false, //if you want to disable back feature set to false - ); + navigateToHomePage(); return; } @@ -61,9 +82,206 @@ class _SplashPageState extends State { } } + List bgList = [ + // 防火墙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/627fe721-846f-4f75-ac61-111ca00b27dd/dcut8b7-dd903b85-4837-4b40-96b3-ef9052858781.png/v1/fit/w_800,h_742/firewall_xceed_dragon__full_render___by_alanmac95_dcut8b7-414w-2x.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NzQyIiwicGF0aCI6IlwvZlwvNjI3ZmU3MjEtODQ2Zi00Zjc1LWFjNjEtMTExY2EwMGIyN2RkXC9kY3V0OGI3LWRkOTAzYjg1LTQ4MzctNGI0MC05NmIzLWVmOTA1Mjg1ODc4MS5wbmciLCJ3aWR0aCI6Ijw9ODAwIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmltYWdlLm9wZXJhdGlvbnMiXX0.AZTGdxh0r0_Lli_vb8wuOYFmZkFlT_HmOZ23QuklI7U', + scale: 1, + ), + + // 时空龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/64d82561-3383-4f02-ba5d-0cd37c6f9227/dbi97ff-6e40406b-b417-4976-a2d0-d7071512917b.png/v1/fill/w_1024,h_609/number_107__galaxy_eyes_tachyon_dragon_by_koga92_by_dkpromangas92_dbi97ff-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NjA5IiwicGF0aCI6IlwvZlwvNjRkODI1NjEtMzM4My00ZjAyLWJhNWQtMGNkMzdjNmY5MjI3XC9kYmk5N2ZmLTZlNDA0MDZiLWI0MTctNDk3Ni1hMmQwLWQ3MDcxNTEyOTE3Yi5wbmciLCJ3aWR0aCI6Ijw9MTAyNCJ9XV0sImF1ZCI6WyJ1cm46c2VydmljZTppbWFnZS5vcGVyYXRpb25zIl19.111LBrBGntP4T3txETGvMb6jgzRoWJ2tpdp7l-xkW4A', + scale: 1.5, + ), + + // 水晶大蛇 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/64d82561-3383-4f02-ba5d-0cd37c6f9227/dbi98er-3fae1bca-178d-49e6-abd4-583833a88418.png/v1/fill/w_853,h_937/crystron_quariongandrax_full_render_by_koga92_by_dkpromangas92_dbi98er-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTEyMiIsInBhdGgiOiJcL2ZcLzY0ZDgyNTYxLTMzODMtNGYwMi1iYTVkLTBjZDM3YzZmOTIyN1wvZGJpOThlci0zZmFlMWJjYS0xNzhkLTQ5ZTYtYWJkNC01ODM4MzNhODg0MTgucG5nIiwid2lkdGgiOiI8PTEwMjIifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.HJHntv3YAsFYTKILPkDa2xmvSTfvrgN3-CYKBjCK9HM', + scale: 1.2, + ), + + // 电子龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dguocwe-68ece52b-ecfd-4def-806f-eb4b6ebc68b7.png/v1/fill/w_1257,h_636/cyber_dragon_infinity___yugioh_master_duel_by_matteste_dguocwe-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjE1MCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGd1b2N3ZS02OGVjZTUyYi1lY2ZkLTRkZWYtODA2Zi1lYjRiNmViYzY4YjcucG5nIiwid2lkdGgiOiI8PTQyNTAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.sABtGfCCtpj92YNOt_HbKO8eZ6vT5TzVm_YOnGidYh8', + scale: 1.4, + ), + // 黑羽龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/53dc9ba2-f20c-4c3e-ac0b-b71317b922b2/d8tu7c4-079aef05-647a-4393-a1dd-227df9394547.png/v1/fill/w_875,h_600/black_winged_dragon_by_bright32302_d8tu7c4-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NjAwIiwicGF0aCI6IlwvZlwvNTNkYzliYTItZjIwYy00YzNlLWFjMGItYjcxMzE3YjkyMmIyXC9kOHR1N2M0LTA3OWFlZjA1LTY0N2EtNDM5My1hMWRkLTIyN2RmOTM5NDU0Ny5wbmciLCJ3aWR0aCI6Ijw9ODc1In1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmltYWdlLm9wZXJhdGlvbnMiXX0.tupRakYBC9-ZgGcTw2axvGURhQ1FP4SZS-KhVOfbFL8', + scale: 1, + ), + // 流星类天龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/53dc9ba2-f20c-4c3e-ac0b-b71317b922b2/dabn8zk-f85cebe6-7845-4e7c-b1e8-a1ac297cd860.png/v1/fill/w_835,h_768/shooting_quasar_dragon_by_bright32302_dabn8zk-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NzY4IiwicGF0aCI6IlwvZlwvNTNkYzliYTItZjIwYy00YzNlLWFjMGItYjcxMzE3YjkyMmIyXC9kYWJuOHprLWY4NWNlYmU2LTc4NDUtNGU3Yy1iMWU4LWExYWMyOTdjZDg2MC5wbmciLCJ3aWR0aCI6Ijw9ODM1In1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmltYWdlLm9wZXJhdGlvbnMiXX0.uHalzH8hMuX7WuzZSm8O7m2npGPxdPfapPW_dAS9vXc', + scale: 1, + ), + // 防火墙龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/59d970dd-eb09-447d-9a65-27f2c4fd248c/dbes4gw-5036056b-922d-4527-ad94-3c5ce3190fbb.png/v1/fill/w_889,h_899/firewall_dragon___full_artwork_by_xrosm_dbes4gw-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTAzMCIsInBhdGgiOiJcL2ZcLzU5ZDk3MGRkLWViMDktNDQ3ZC05YTY1LTI3ZjJjNGZkMjQ4Y1wvZGJlczRndy01MDM2MDU2Yi05MjJkLTQ1MjctYWQ5NC0zYzVjZTMxOTBmYmIucG5nIiwid2lkdGgiOiI8PTEwMTkifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.2_ctrGM9olCaoWsNHUYDvbJBk2si994BiEG4Bi0IcoY', + scale: 1, + ), + // 电子龙无限 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/53dc9ba2-f20c-4c3e-ac0b-b71317b922b2/d9xejwh-9196d980-3598-4cd8-90bf-cd06905fefca.png/v1/fill/w_1024,h_683/cyber_dragon_infinity_by_bright32302_d9xejwh-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NjgzIiwicGF0aCI6IlwvZlwvNTNkYzliYTItZjIwYy00YzNlLWFjMGItYjcxMzE3YjkyMmIyXC9kOXhlandoLTkxOTZkOTgwLTM1OTgtNGNkOC05MGJmLWNkMDY5MDVmZWZjYS5wbmciLCJ3aWR0aCI6Ijw9MTAyNCJ9XV0sImF1ZCI6WyJ1cm46c2VydmljZTppbWFnZS5vcGVyYXRpb25zIl19.lDdxdMVFOGxx7qGEIxfpeI88rYwwXjvVkzcVuUjpg1M', + scale: 1.4, + ), + + // 希望皇 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/5a6af839-076e-448b-b7e8-47dcfb1f1af3/df4yoiz-5994ee21-0c1b-4b6e-940a-0643c907f033.png/v1/fill/w_1040,h_769/master_duel_login_screen_render_by_henukim_df4yoiz-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTQ5MSIsInBhdGgiOiJcL2ZcLzVhNmFmODM5LTA3NmUtNDQ4Yi1iN2U4LTQ3ZGNmYjFmMWFmM1wvZGY0eW9pei01OTk0ZWUyMS0wYzFiLTRiNmUtOTQwYS0wNjQzYzkwN2YwMzMucG5nIiwid2lkdGgiOiI8PTIwMTYifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.d7PJXO0cAwu1WPsDlaQU0wtTEBTgj5hrBTTqiUq5gpA', + scale: 1, + ), + + // 龙辉巧 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/5a6af839-076e-448b-b7e8-47dcfb1f1af3/df2sg34-fed01781-72fd-4ead-a1bc-4ac63fc1156a.png/v1/fill/w_879,h_909/drytron_meteonis_draconids_render_by_henukim_df2sg34-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjExOSIsInBhdGgiOiJcL2ZcLzVhNmFmODM5LTA3NmUtNDQ4Yi1iN2U4LTQ3ZGNmYjFmMWFmM1wvZGYyc2czNC1mZWQwMTc4MS03MmZkLTRlYWQtYTFiYy00YWM2M2ZjMTE1NmEucG5nIiwid2lkdGgiOiI8PTIwNDkifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.tWDvQtjSpY71-8eZ0gkzM32mT7W8IpyihoE_wAycj_o', + scale: 1, + ), + + // 刺刀 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/df40rj8-eb92ad9d-8ca8-49c7-9b4b-5f1b4b3eeb99.png/v1/fill/w_878,h_910/borrelsword_dragon___yugioh_master_duel_by_matteste_df40rj8-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjgwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGY0MHJqOC1lYjkyYWQ5ZC04Y2E4LTQ5YzctOWI0Yi01ZjFiNGIzZWViOTkucG5nIiwid2lkdGgiOiI8PTI3MDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.JSankSgEKwzQReGylza5rxQEnKzogdKzsWL0TtvHwf0', + scale: 1, + ), + + // 蔷薇龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/b0d85773-a954-4ea3-8a6c-111cf5714f93/df4wysl-f15eb81f-e22f-470a-88ec-cb61771ce96a.png/v1/fill/w_895,h_893/ruddy_rose_dragon_by_faultzon3_df4wysl-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTI3NiIsInBhdGgiOiJcL2ZcL2IwZDg1NzczLWE5NTQtNGVhMy04YTZjLTExMWNmNTcxNGY5M1wvZGY0d3lzbC1mMTVlYjgxZi1lMjJmLTQ3MGEtODhlYy1jYjYxNzcxY2U5NmEucG5nIiwid2lkdGgiOiI8PTEyODAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.DN9jvTDr831Gl2GgV15YXzvKpUEW57img-rFCaIHwZM', + scale: 1, + ), + + // 冰龙剑 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dfd1w89-a61f4b86-7899-443c-8272-b9c6b56a55bb.png/v1/fill/w_940,h_850/mirrorjade___yugioh_master_duel_by_matteste_dfd1w89-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjM1MCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGZkMXc4OS1hNjFmNGI4Ni03ODk5LTQ0M2MtODI3Mi1iOWM2YjU2YTU1YmIucG5nIiwid2lkdGgiOiI8PTI2MDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.nJ63HHnEItCHdA6BYXkgYCXNq5Dw9xu44BFifafY4QA', + scale: 1, + ), + + // 法法 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dguos3c-c70f5142-774e-45d3-b182-d12e11c90ad7.png/v1/fill/w_951,h_840/true_king_of_all_calamities___yugioh_master_duel_by_matteste_dguos3c-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjY1MCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGd1b3MzYy1jNzBmNTE0Mi03NzRlLTQ1ZDMtYjE4Mi1kMTJlMTFjOTBhZDcucG5nIiwid2lkdGgiOiI8PTMwMDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.0_5-pTiovRRDYc8vpXspbqjtwLvBLuWOQRKTKySTANs', + scale: 1, + ), + // 天庭号 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dguoef8-2dc6ebf4-05cf-44d2-b8af-dc55d8cfd016.png/v1/fill/w_1103,h_724/divine_arsenal_aa_zeus___yugioh_master_duel_by_matteste_dguoef8-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjIwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGd1b2VmOC0yZGM2ZWJmNC0wNWNmLTQ0ZDItYjhhZi1kYzU1ZDhjZmQwMTYucG5nIiwid2lkdGgiOiI8PTMzNTAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.JJBLh_TKh6aF4zIprIL8OQh1rv0QdRDZeJjSH3BEo74', + scale: 1, + ), + // 拿非利 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dguofth-190988bb-9c15-4150-9939-4c70b9f4d90a.png/v1/fill/w_1017,h_786/el_shaddoll_construct___yugioh_master_duel_by_matteste_dguofth-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTcwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGd1b2Z0aC0xOTA5ODhiYi05YzE1LTQxNTAtOTkzOS00YzcwYjlmNGQ5MGEucG5nIiwid2lkdGgiOiI8PTIyMDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.DmbjoUE5MSQBPgNt-KcY4PEqReXnzxRnVZCMxfTebLI', + scale: 1, + ), + + // 天童 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dfrau8v-5f219236-3b72-481d-9ac2-8d55bf596628.png/v1/fit/w_828,h_1082/kurikara_divincarnate___yugioh_master_duel_by_matteste_dfrau8v-414w-2x.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MzIwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGZyYXU4di01ZjIxOTIzNi0zYjcyLTQ4MWQtOWFjMi04ZDU1YmY1OTY2MjgucG5nIiwid2lkdGgiOiI8PTI0NTAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.aOIDuS9mVlz3fimh080wAb1zRjnhRdKw-uEdZXxg3hc', + scale: 1, + ), + + // + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dfp01tv-a4fd3118-83a3-4715-a47f-3c6344958553.png/v1/fill/w_902,h_886/ultimate_slayer___yugioh_master_duel_by_matteste_dfp01tv-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjgwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGZwMDF0di1hNGZkMzExOC04M2EzLTQ3MTUtYTQ3Zi0zYzYzNDQ5NTg1NTMucG5nIiwid2lkdGgiOiI8PTI4NTAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.ZO8atLE65xUuv7pS60RwGTwQXWtoqUMUah8YCFUOOlA', + scale: 1, + ), + + // 银河眼1 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/df3yr5s-58164e67-d65f-455d-bde7-a80cf725807f.png/v1/fill/w_970,h_824/galaxy_eyes_cipher_x_dragon___yugioh_master_duel_by_matteste_df3yr5s-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MzQwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGYzeXI1cy01ODE2NGU2Ny1kNjVmLTQ1NWQtYmRlNy1hODBjZjcyNTgwN2YucG5nIiwid2lkdGgiOiI8PTQwMDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.uuBQiItwym2MJM8Dzz8Vgqh0bN7S88TMCZnw92WpVp0', + scale: 1, + ), + + // 究极龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/03b5a34c-d066-4223-ae94-21c906adb0bc/dbjqir2-11577bfa-e050-4c9b-8f66-b3fba78649f2.png/v1/fill/w_1024,h_500/neo_blue_eyes_ultimate_dragon_full_render_by_holycrapwhitedragon_dbjqir2-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NTAwIiwicGF0aCI6IlwvZlwvMDNiNWEzNGMtZDA2Ni00MjIzLWFlOTQtMjFjOTA2YWRiMGJjXC9kYmpxaXIyLTExNTc3YmZhLWUwNTAtNGM5Yi04ZjY2LWIzZmJhNzg2NDlmMi5wbmciLCJ3aWR0aCI6Ijw9MTAyNCJ9XV0sImF1ZCI6WyJ1cm46c2VydmljZTppbWFnZS5vcGVyYXRpb25zIl19.LFY8xAIptbencZ9hVhBNBYdURzJvZx5y8oETBNG03eI', + scale: 1, + ), + // 青眼1 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/627fe721-846f-4f75-ac61-111ca00b27dd/ddmykl2-bf7bb3c2-0201-444e-b30e-63f366ca548b.png/v1/fill/w_908,h_589/blue_eyes_white_dragon__render__by_alanmac95_ddmykl2-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NTg5IiwicGF0aCI6IlwvZlwvNjI3ZmU3MjEtODQ2Zi00Zjc1LWFjNjEtMTExY2EwMGIyN2RkXC9kZG15a2wyLWJmN2JiM2MyLTAyMDEtNDQ0ZS1iMzBlLTYzZjM2NmNhNTQ4Yi5wbmciLCJ3aWR0aCI6Ijw9OTA4In1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmltYWdlLm9wZXJhdGlvbnMiXX0.mfBxuZ78dqwd4SeJPfCAey0dZk6eEFJAOOoUNc9KdS4', + scale: 1, + ), + + // 驱魔姐妹 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/df51h22-8dd52191-184f-4b71-be58-89e3fd72759a.png/v1/fill/w_922,h_867/exosister_mikailis___yugioh_master_duel_by_matteste_df51h22-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjM1MCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGY1MWgyMi04ZGQ1MjE5MS0xODRmLTRiNzEtYmU1OC04OWUzZmQ3Mjc1OWEucG5nIiwid2lkdGgiOiI8PTI1MDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.BWsVqbUI-xiOEDxHwMHlwf7trkB1Y-4tvPUiLhSDV-4', + scale: 1, + ), + // 仪式凤凰 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/96551409-eddd-4b72-92da-7fd74699b762/dco5tvi-98636c24-8e72-48c6-9ead-a280bd74f229.png/v1/fill/w_1024,h_725/sacred_blue_phoenix_of_nephthys_by_coccvo_dco5tvi-fullview.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9NzI1IiwicGF0aCI6IlwvZlwvOTY1NTE0MDktZWRkZC00YjcyLTkyZGEtN2ZkNzQ2OTliNzYyXC9kY281dHZpLTk4NjM2YzI0LThlNzItNDhjNi05ZWFkLWEyODBiZDc0ZjIyOS5wbmciLCJ3aWR0aCI6Ijw9MTAyNCJ9XV0sImF1ZCI6WyJ1cm46c2VydmljZTppbWFnZS5vcGVyYXRpb25zIl19.qkpXN7HsyJo78gENU9KVYNIqI9X0nya4s574374DaLM', + scale: 1, + ), + + // 巨大喷流 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/b0d85773-a954-4ea3-8a6c-111cf5714f93/dfozeao-b29ecc75-4082-4b2b-acc9-4e6489bacb64.png/v1/fit/w_828,h_968/gigantic_spright_by_faultzon3_dfozeao-414w-2x.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MTQ5NSIsInBhdGgiOiJcL2ZcL2IwZDg1NzczLWE5NTQtNGVhMy04YTZjLTExMWNmNTcxNGY5M1wvZGZvemVhby1iMjllY2M3NS00MDgyLTRiMmItYWNjOS00ZTY0ODliYWNiNjQucG5nIiwid2lkdGgiOiI8PTEyODAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.J0bwIjEjXIWkFBwwAlr8pXhYSKbH5KLf71Whb5ajfUQ', + scale: 1, + ), + + // 红莲龙 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dhxlm3h-f570b54e-d728-4941-8830-9189a90b49d7.png/v1/fill/w_985,h_811/crimson_dragon___yugioh_master_duel_by_matteste_dhxlm3h-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjgwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGh4bG0zaC1mNTcwYjU0ZS1kNzI4LTQ5NDEtODgzMC05MTg5YTkwYjQ5ZDcucG5nIiwid2lkdGgiOiI8PTM0MDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.OS_Ae_xkHAKC4JdoG3F9fIHjsL3Psx6xSoNTjoXFaQU', + scale: 1, + ), + + // 闪刀- kakari + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dhjqbcz-0dd60ee9-f56e-4296-8c91-08a426c1a9b5.png/v1/fill/w_863,h_926/sky_striker___kagari__alt____yugioh_master_duel_by_matteste_dhjqbcz-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjkwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGhqcWJjei0wZGQ2MGVlOS1mNTZlLTQyOTYtOGM5MS0wOGE0MjZjMWE5YjUucG5nIiwid2lkdGgiOiI8PTI3MDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.LVOcGOOhEqKjNdHWEwMfH2PASKtC8gplUDrPgZlq8FI', + scale: 1, + ), + + // 陨石 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dguoltn-bca520a4-51c8-4e25-9b2a-1409c8d482f0.png/v1/fill/w_894,h_894/nibiru_the_primal_being___yugioh_master_duel_by_matteste_dguoltn-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjA0OCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGd1b2x0bi1iY2E1MjBhNC01MWM4LTRlMjUtOWIyYS0xNDA5YzhkNDgyZjAucG5nIiwid2lkdGgiOiI8PTIwNDgifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.vEk9jVASbPoi2mfMYGzXJnCihHWEgpKO_6pha8lLqDY', + scale: 1, + ), + + // 小米 + BgItem( + url: + 'https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/fffbb7f5-78d1-47b5-a59d-19225f518420/dguofxb-e0938f9f-cf1f-4e38-82db-59141c48ed5e.png/v1/fill/w_920,h_868/el_shaddoll_winda___yugioh_master_duel_by_matteste_dguofxb-pre.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9MjUwMCIsInBhdGgiOiJcL2ZcL2ZmZmJiN2Y1LTc4ZDEtNDdiNS1hNTlkLTE5MjI1ZjUxODQyMFwvZGd1b2Z4Yi1lMDkzOGY5Zi1jZjFmLTRlMzgtODJkYi01OTE0MWM0OGVkNWUucG5nIiwid2lkdGgiOiI8PTI2NTAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.LuAeeHlg52MXBPnEz-yFIihY9rWuKqV2CRYSne4BFsw', + scale: 1, + ), + ]; + + void initSlideBg() { + setState(() { + positionTop -= _count * 40; + }); + } + Future init() async { await initDarkMode(); + initSlideBg(); startCounterDown(); banCardStore.setupLocalData().ignore(); @@ -71,7 +289,15 @@ class _SplashPageState extends State { @override void initState() { + setState(() { + index = math.Random().nextInt(bgList.length); + }); super.initState(); + _animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 5000))..repeat(reverse: true); + + _slideAnimation = Tween(begin: Offset.zero, end: const Offset(0, -0.02)).animate(_animationController); + + _scaleAnimation = Tween(begin: 1, end: 1.02).chain(CurveTween(curve: Curves.easeOutCirc)).animate(_animationController); init(); } @@ -79,8 +305,100 @@ class _SplashPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Center( - child: Text('splash page, $_count'), + backgroundColor: Colors.black54, + body: InkResponse( + highlightShape: BoxShape.rectangle, + containedInkWell: true, + onTap: navigateToHomePage, + child: Stack( + fit: StackFit.expand, + children: [ + AnimatedPositioned( + duration: const Duration(seconds: 10), + top: positionTop, + left: 0, + right: 0, + bottom: 0, + child: SizedBox( + height: MediaQuery.of(context).size.height * 2, + child: Transform.scale( + scale: 1.8, + child: CachedNetworkImage( + fit: BoxFit.fitWidth, + imageUrl: bgUrl, + repeat: ImageRepeat.repeatY, + ), + ), + ), + ), + Positioned.fill( + child: CachedNetworkImage( + imageUrl: bgBlueFlexUrl, + fit: BoxFit.cover, + ), + ), + Positioned( + bottom: -200, + right: 0, + child: CachedNetworkImage(imageUrl: bgRb), + ), + Positioned( + top: 0, + child: CachedNetworkImage(imageUrl: bgTl), + ), + Center( + child: ScaleTransition( + scale: _scaleAnimation, + child: SlideTransition( + position: _slideAnimation, + child: AspectRatio( + aspectRatio: 1, + child: Transform.translate( + offset: const Offset(0, -60), + child: Transform.scale( + scale: bgList[index].scale, + child: CachedNetworkImage( + fit: BoxFit.contain, + placeholder: (context, url) => CachedNetworkImage(imageUrl: placeholder), + imageUrl: bgList[index].url, + ), + ), + ), + ), + ), + ), + ), + Center( + child: Transform.translate( + offset: const Offset(0, 40), + child: GestureDetector( + onTap: () { + setState(() { + index = math.Random().nextInt(bgList.length); + }); + }, + child: Assets.images.siteLogoDlmPng.image(height: 80), + ), + ), + ), + Positioned( + bottom: 100, + left: 0, + right: 0, + child: Center( + child: RichText( + text: TextSpan( + text: 'Tap the screen (', + children: [ + TextSpan(text: '$_count', style: TextStyle(color: Theme.of(context).colorScheme.primary)), + const TextSpan(text: 's)'), + ], + ), + ), + ), + ) + ], + ), ), ); } diff --git a/lib/pages/tier_list/components/TierListView.dart b/lib/pages/tier_list/components/TierListView.dart index 4176fd2..b32f914 100644 --- a/lib/pages/tier_list/components/TierListView.dart +++ b/lib/pages/tier_list/components/TierListView.dart @@ -1,9 +1,9 @@ import 'dart:developer'; +import 'package:duel_links_meta/api/TierListApi.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/PowerRankingsHiveDb.dart'; import 'package:duel_links_meta/hive/db/TierListHiveDb.dart'; -import 'package:duel_links_meta/http/TierListApi.dart'; import 'package:duel_links_meta/pages/deck_type_detail/index.dart'; import 'package:duel_links_meta/pages/tier_list/components/TierListItemView.dart'; import 'package:duel_links_meta/pages/tier_list/type/TierListGroup.dart'; @@ -92,9 +92,6 @@ class _TierListViewState extends State with AutomaticKeepAliveClie }); _list.sort((a, b) => a.tier.compareTo(b.tier)); - _list.forEach((element) { - log(element.desc); - }); setState(() { _tierListGroup = _list; diff --git a/lib/pages/top_decks/index.dart b/lib/pages/top_decks/index.dart index 5af3f58..37a2614 100644 --- a/lib/pages/top_decks/index.dart +++ b/lib/pages/top_decks/index.dart @@ -1,7 +1,7 @@ +import 'package:duel_links_meta/api/TopDeckApi.dart'; import 'package:duel_links_meta/components/TopDeckItem.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/TopDeckHiveDb.dart'; -import 'package:duel_links_meta/http/TopDeckApi.dart'; import 'package:duel_links_meta/pages/deck_detail/index.dart'; import 'package:duel_links_meta/pages/top_decks/components/TopDeckListView.dart'; import 'package:duel_links_meta/pages/top_decks/type/Group.dart'; diff --git a/lib/pages/webview/index.dart b/lib/pages/webview/index.dart index 1a9a73e..4568862 100644 --- a/lib/pages/webview/index.dart +++ b/lib/pages/webview/index.dart @@ -1,6 +1,8 @@ +import 'dart:developer'; + import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/constant/colors.dart'; import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:webview_flutter/webview_flutter.dart'; class WebviewPage extends StatefulWidget { @@ -19,27 +21,38 @@ class _WebviewPageState extends State { String get url => super.widget.url; late final WebViewController _webViewController; - var loadingPercentage = 0; + int loadingPercentage = 0; - initWebViewController() { + void initWebViewController() { _webViewController = WebViewController() ..loadRequest(Uri.parse(url)) - ..setNavigationDelegate(NavigationDelegate(onPageStarted: (url) { - setState(() { - if (loadingPercentage == 100) return; - loadingPercentage = 0; - }); - }, onProgress: (progress) { - setState(() { - if (loadingPercentage == 100) return; - loadingPercentage = progress; - }); - }, onPageFinished: (url) { - setState(() { - if (loadingPercentage == 100) return; - loadingPercentage = 100; - }); - })); + ..setNavigationDelegate( + NavigationDelegate( + onPageStarted: (url) { + setState(() { + if (loadingPercentage == 100) return; + loadingPercentage = 0; + }); + }, + onProgress: (progress) { + setState(() { + if (loadingPercentage == 100) return; + loadingPercentage = progress; + }); + }, + onPageFinished: (url) { + setState(() { + if (loadingPercentage == 100) return; + loadingPercentage = 100; + }); + }, + ), + ); + } + + void openByBrowser() { + final uri = Uri.parse(url); + launchUrl(uri).ignore(); } @override @@ -61,48 +74,24 @@ class _WebviewPageState extends State { actions: [ Row( children: [ + IconButton( + onPressed: openByBrowser, + icon: const Icon(Icons.open_in_browser_rounded), + ), // IconButton( - // icon: const Icon(Icons.arrow_back_ios), - // onPressed: () async { - // final messenger = ScaffoldMessenger.of(context); - // if (await _webViewController.canGoBack()) { - // await _webViewController.goBack(); - // } else { - // messenger.showSnackBar( - // const SnackBar(content: Text('No back history item')), - // ); - // return; - // } - // }, - // ), - // IconButton( - // icon: const Icon(Icons.arrow_forward_ios), - // onPressed: () async { - // final messenger = ScaffoldMessenger.of(context); - // if (await _webViewController.canGoForward()) { - // await _webViewController.goForward(); - // } else { - // messenger.showSnackBar( - // const SnackBar(content: Text('No forward history item')), - // ); - // return; - // } + // icon: const Icon(Icons.replay), + // onPressed: () { + // _webViewController.reload(); // }, // ), - IconButton( - icon: const Icon(Icons.replay), - onPressed: () { - _webViewController.reload(); - }, - ), ], - ) + ), ], ), body: Stack( children: [ AnimatedOpacity( - duration: const Duration(milliseconds: 0), + duration: Duration.zero, opacity: loadingPercentage == 100 ? 1 : 0, child: WebViewWidget( controller: _webViewController, diff --git a/lib/store/AppStore.dart b/lib/store/AppStore.dart index 2dc26a4..4504ba8 100644 --- a/lib/store/AppStore.dart +++ b/lib/store/AppStore.dart @@ -1,5 +1,8 @@ import 'dart:developer'; +import 'dart:ui'; +import 'package:duel_links_meta/hive/db/SettingHiveDb.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -7,6 +10,48 @@ class AppStore extends GetxController{ PackageInfo? packageInfo; + RxBool showWebviewNavs = false.obs; + RxInt themeColorIndex = 0.obs; + Color get themeColor => themeColorList[themeColorIndex.value]; + + List themeColorList = [ + Colors.red, + Colors.redAccent, + Colors.deepOrange, + Colors.orangeAccent, + Colors.orange, + Colors.yellow, + Colors.yellow, + Colors.pink, + Colors.pinkAccent, + Colors.deepPurple, + Colors.deepPurpleAccent, + Colors.purple, + Colors.purpleAccent, + Colors.teal, + Colors.tealAccent, + Colors.green, + Colors.greenAccent, + Colors.lightGreen, + Colors.lightGreenAccent, + Colors.blue, + Colors.blueAccent, + Colors.blueGrey, + Colors.lightBlue, + Colors.lightBlueAccent, + ]; + + void toggleShowWebviewNavs() { + showWebviewNavs.value = !showWebviewNavs.value; + + SettingHiveDb().setShowWebviewNavs(show: showWebviewNavs.value); + } + + Future changeThemeColorIndex(int index) async { + themeColorIndex.value = index; + SettingHiveDb().setThemeColorIndex(index).ignore(); + } + @override void onClose() { super.onClose(); @@ -18,7 +63,10 @@ class AppStore extends GetxController{ super.onInit(); log('AppStore on init'); + themeColorIndex.value = (await SettingHiveDb().getThemeColorIndex()) ?? 0; + showWebviewNavs.value = (await SettingHiveDb().getShowWebviewNavs()) ?? false; final info = await PackageInfo.fromPlatform(); + packageInfo = info; } } \ No newline at end of file diff --git a/lib/store/BanCardStore.dart b/lib/store/BanCardStore.dart index 48ad449..343da54 100644 --- a/lib/store/BanCardStore.dart +++ b/lib/store/BanCardStore.dart @@ -1,8 +1,8 @@ import 'dart:developer'; +import 'package:duel_links_meta/api/CardApi.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; import 'package:duel_links_meta/pages/splash/BanStatusCardHiveDb.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; diff --git a/lib/type/NavTab.g.dart b/lib/type/NavTab.g.dart index 5523bf9..d772ba2 100644 --- a/lib/type/NavTab.g.dart +++ b/lib/type/NavTab.g.dart @@ -49,6 +49,7 @@ class NavTabAdapter extends TypeAdapter { NavTab _$NavTabFromJson(Map json) => NavTab( id: json['id'] as int, title: json['title'] as String? ?? '', + url: json['url'] as String?, ) ..oid = json['_id'] as String ..image = json['image'] as String; @@ -58,4 +59,5 @@ Map _$NavTabToJson(NavTab instance) => { 'image': instance.image, 'id': instance.id, 'title': instance.title, + 'url': instance.url, }; diff --git a/lib/util/index.dart b/lib/util/index.dart deleted file mode 100644 index f5e3bba..0000000 --- a/lib/util/index.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:developer'; -import 'dart:io'; - -import 'package:duel_links_meta/extension/String.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:get/get_connect/http/src/response/response.dart'; - -class Util { - static Future<(Exception?, T?)> toCatch(Future> fn) async { - try { - var res = await fn; - log('[toCatch] code: ${res.statusCode}, body is null: ${res.body == null}, statusText: ${res.statusText}'); - - if (res.statusCode != 200) { - return (HttpException('status: ${res.statusCode}, msg: ${res.statusText}'), null); - } - - if (res.body == null) { - return (const HttpException('response body is null'), res.body); - } - - return (null, res.body); - } catch (err) { - return (HttpException(err.toString()), null); - } - } - - - static bool isReachBottom(ScrollController controller, {int threshold = 200}) { - return controller.position.maxScrollExtent - controller.position.pixels <= threshold; - } - - static List? decoderListCatch(dynamic data, T Function(dynamic _data) decoder) { - if (data == null) { - return null; - } - - try { - return (data as List).map(decoder).toList(); - } catch (err) { - if (kDebugMode) { - final msg = '[decoderListCatch] 解析json失败: $err'; - log(msg); - - msg.toast(); - } - - return null; - } - } - -} diff --git a/lib/util/time_util.dart b/lib/util/time_util.dart deleted file mode 100644 index eb5a580..0000000 --- a/lib/util/time_util.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:intl/intl.dart'; - -class TimeUtil { - static final _formatter = DateFormat.yMMMMd(); - - static String format(DateTime? time) { - if (time == null) return ''; - - return _formatter.format(time); - } -} - diff --git a/lib/widget/background.dart b/lib/widget/background.dart deleted file mode 100644 index 6f6cb2d..0000000 --- a/lib/widget/background.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class Particle { - Particle({ - required this.x, - required this.y, - required this.radius, - required this.speedX, - required this.speedY, - required this.color, - }); - - double x; - double y; - final double radius; - double speedX; - double speedY; - final Color color; -} - -class BubbleBackground extends StatefulWidget { - const BubbleBackground({ - super.key, - required this.child, - required this.colors, - }); - - final Widget child; - final List colors; - - @override - BubbleBackgroundState createState() => BubbleBackgroundState(); -} - -class BubbleBackgroundState extends State - with TickerProviderStateMixin { - final List particles = []; - - final _notifier = ValueNotifier(0); - - @override - void initState() { - super.initState(); - tick(); - } - - Future tick() async { - while (mounted) { - await Future.delayed(const Duration(milliseconds: 16)); - if (!mounted) { - return; - } - if (particles.isEmpty) { - createParticles(context.size!); - } - for (final particle in particles) { - particle.x += particle.speedX; - particle.y += particle.speedY; - - if (particle.x < 0 || particle.x > MediaQuery.of(context).size.width) { - particle.speedX *= -1; - } - if (particle.y < 0 || particle.y > MediaQuery.of(context).size.height) { - particle.speedY *= -1; - } - } - _notifier.value = _notifier.value + 1; - } - } - - void createParticles(Size size) { - final random = Random(); - for (final color in widget.colors) { - final x = random.nextDouble() * size.width; - final y = random.nextDouble() * size.height; - final radius = random.nextDouble() * 100 + 50; - final speedX = random.nextDouble() * 2 - 1; - final speedY = random.nextDouble() * 2 - 1; - final particle = Particle( - x: x, - y: y, - radius: radius, - speedX: speedX, - speedY: speedY, - color: color, - ); - particles.add(particle); - } - } - - @override - Widget build(BuildContext context) { - return CustomPaint( - painter: ParticlePainter(particles, _notifier), - child: widget.child, - ); - } -} - -class ParticlePainter extends CustomPainter { - ParticlePainter(this.particles, Listenable listenable) - : super(repaint: listenable); - - final List particles; - - @override - void paint(Canvas canvas, Size size) { - for (final particle in particles) { - canvas.drawCircle( - Offset(particle.x, particle.y), - particle.radius, - Paint()..color = particle.color, - ); - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/widget/ripple_tap.dart b/lib/widget/ripple_tap.dart deleted file mode 100644 index e0693d1..0000000 --- a/lib/widget/ripple_tap.dart +++ /dev/null @@ -1,441 +0,0 @@ -import 'package:flutter/material.dart'; - -class RippleTap extends StatelessWidget { - const RippleTap({ - super.key, - required this.child, - this.onTap, - this.onTapDown, - this.onTapUp, - this.onTapCancel, - this.onDoubleTap, - this.onLongPress, - this.type = MaterialType.canvas, - this.elevation = 0.0, - this.color = Colors.transparent, - this.shadowColor, - this.surfaceTintColor, - this.textStyle, - this.borderRadius, - this.shape, - this.borderOnForeground = true, - this.clipBehavior = Clip.antiAlias, - this.animationDuration = kThemeChangeDuration, - }); - - final Widget child; - - /// Called when the user taps this part of the material. - final GestureTapCallback? onTap; - - /// Called when the user taps down this part of the material. - final GestureTapDownCallback? onTapDown; - - /// Called when the user releases a tap that was started on this part of the - /// material. [onTap] is called immediately after. - final GestureTapUpCallback? onTapUp; - - /// Called when the user cancels a tap that was started on this part of the - /// material. - final GestureTapCallback? onTapCancel; - - /// Called when the user double taps this part of the material. - final GestureTapCallback? onDoubleTap; - - /// Called when the user long-presses on this part of the material. - final GestureLongPressCallback? onLongPress; - - /// The kind of material to show (e.g., card or canvas). This - /// affects the shape of the widget, the roundness of its corners if - /// the shape is rectangular, and the default color. - final MaterialType type; - - /// {@template flutter.material.material.elevation} - /// The z-coordinate at which to place this material relative to its parent. - /// - /// This controls the size of the shadow below the material and the opacity - /// of the elevation overlay color if it is applied. - /// - /// If this is non-zero, the contents of the material are clipped, because the - /// widget conceptually defines an independent printed piece of material. - /// - /// Defaults to 0. Changing this value will cause the shadow and the elevation - /// overlay or surface tint to animate over [Material.animationDuration]. - /// - /// The value is non-negative. - /// - /// See also: - /// - /// * [ThemeData.useMaterial3] which defines whether a surface tint or - /// elevation overlay is used to indicate elevation. - /// * [ThemeData.applyElevationOverlayColor] which controls the whether - /// an overlay color will be applied to indicate elevation. - /// * [Material.color] which may have an elevation overlay applied. - /// * [Material.shadowColor] which will be used for the color of a drop shadow. - /// * [Material.surfaceTintColor] which will be used as the overlay tint to - /// show elevation. - /// {@endtemplate} - final double elevation; - - /// The color to paint the material. - /// - /// Must be opaque. To create a transparent piece of material, use - /// [MaterialType.transparency]. - /// - /// If [ThemeData.useMaterial3] is true then an optional [surfaceTintColor] - /// overlay may be applied on top of this color to indicate elevation. - /// - /// If [ThemeData.useMaterial3] is false and [ThemeData.applyElevationOverlayColor] - /// is true and [ThemeData.brightness] is [Brightness.dark] then a - /// semi-transparent overlay color will be composited on top of this - /// color to indicate the elevation. This is no longer needed for Material - /// Design 3, which uses [surfaceTintColor]. - /// - /// By default, the color is derived from the [type] of material. - final Color? color; - - /// The color to paint the shadow below the material. - /// - /// When [ThemeData.useMaterial3] is true, and this is null, then no drop - /// shadow will be rendered for this material. If it is non-null, then this - /// color will be used to render a drop shadow below the material. - /// - /// When [ThemeData.useMaterial3] is false, and this is null, then - /// [ThemeData.shadowColor] is used, which defaults to fully opaque black. - /// - /// See also: - /// * [ThemeData.useMaterial3], which determines the default value for this - /// property if it is null. - /// * [ThemeData.applyElevationOverlayColor], which turns elevation overlay - /// on or off for dark themes. - final Color? shadowColor; - - /// The color of the surface tint overlay applied to the material color - /// to indicate elevation. - /// - /// Material Design 3 introduced a new way for some components to indicate - /// their elevation by using a surface tint color overlay on top of the - /// base material [color]. This overlay is painted with an opacity that is - /// related to the [elevation] of the material. - /// - /// If [ThemeData.useMaterial3] is false, then this property is not used. - /// - /// If [ThemeData.useMaterial3] is true and [surfaceTintColor] is not null, - /// then it will be used to overlay the base [color] with an opacity based - /// on the [elevation]. - /// - /// Otherwise, no surface tint will be applied. - /// - /// See also: - /// - /// * [ThemeData.useMaterial3], which turns this feature on. - /// * [ElevationOverlay.applySurfaceTint], which is used to implement the - /// tint. - /// * https://m3.material.io/styles/color/the-color-system/color-roles - /// which specifies how the overlay is applied. - final Color? surfaceTintColor; - - /// The typographical style to use for text within this material. - final TextStyle? textStyle; - - /// Defines the material's shape as well its shadow. - /// - /// If shape is non null, the [borderRadius] is ignored and the material's - /// clip boundary and shadow are defined by the shape. - /// - /// A shadow is only displayed if the [elevation] is greater than - /// zero. - final ShapeBorder? shape; - - /// Whether to paint the [shape] border in front of the [child]. - /// - /// The default value is true. - /// If false, the border will be painted behind the [child]. - final bool borderOnForeground; - - /// {@template flutter.material.Material.clipBehavior} - /// The content will be clipped (or not) according to this option. - /// - /// See the enum [Clip] for details of all possible options and their common - /// use cases. - /// {@endtemplate} - /// - /// Defaults to [Clip.none], and must not be null. - final Clip clipBehavior; - - /// Defines the duration of animated changes for [shape], [elevation], - /// [shadowColor], [surfaceTintColor] and the elevation overlay if it is applied. - /// - /// The default value is [kThemeChangeDuration]. - final Duration animationDuration; - - /// If non-null, the corners of this box are rounded by this - /// [BorderRadiusGeometry] value. - /// - /// Otherwise, the corners specified for the current [type] of material are - /// used. - /// - /// If [shape] is non null then the border radius is ignored. - /// - /// Must be null if [type] is [MaterialType.circle]. - final BorderRadiusGeometry? borderRadius; - - @override - Widget build(BuildContext context) { - return Material( - type: type, - elevation: elevation, - color: color, - shadowColor: shadowColor, - surfaceTintColor: surfaceTintColor, - textStyle: textStyle, - shape: shape, - borderOnForeground: borderOnForeground, - clipBehavior: clipBehavior, - animationDuration: animationDuration, - borderRadius: borderRadius, - child: InkResponse( - highlightShape: BoxShape.rectangle, - containedInkWell: true, - onTap: onTap, - onTapDown: onTapDown, - onTapCancel: onTapCancel, - onTapUp: onTapUp, - onLongPress: onLongPress, - onDoubleTap: onDoubleTap, - child: child, - ), - ); - } -} - -class ScalableRippleTap extends StatefulWidget { - const ScalableRippleTap({ - super.key, - required this.child, - this.onTap, - this.type = MaterialType.canvas, - this.elevation = 0.0, - this.color = Colors.transparent, - this.shadowColor, - this.surfaceTintColor, - this.textStyle, - this.borderRadius, - this.shape, - this.borderOnForeground = true, - this.clipBehavior = Clip.antiAlias, - this.animationDuration = kThemeChangeDuration, - this.transform, - this.onDoubleTap, - this.onLongPress, - }); - - final Widget child; - - /// Called when the user taps this part of the material. - final GestureTapCallback? onTap; - - /// Called when the user double taps this part of the material. - final GestureTapCallback? onDoubleTap; - - /// Called when the user long-presses on this part of the material. - final GestureLongPressCallback? onLongPress; - - final Matrix4? transform; - - /// The kind of material to show (e.g., card or canvas). This - /// affects the shape of the widget, the roundness of its corners if - /// the shape is rectangular, and the default color. - final MaterialType type; - - /// {@template flutter.material.material.elevation} - /// The z-coordinate at which to place this material relative to its parent. - /// - /// This controls the size of the shadow below the material and the opacity - /// of the elevation overlay color if it is applied. - /// - /// If this is non-zero, the contents of the material are clipped, because the - /// widget conceptually defines an independent printed piece of material. - /// - /// Defaults to 0. Changing this value will cause the shadow and the elevation - /// overlay or surface tint to animate over [Material.animationDuration]. - /// - /// The value is non-negative. - /// - /// See also: - /// - /// * [ThemeData.useMaterial3] which defines whether a surface tint or - /// elevation overlay is used to indicate elevation. - /// * [ThemeData.applyElevationOverlayColor] which controls the whether - /// an overlay color will be applied to indicate elevation. - /// * [Material.color] which may have an elevation overlay applied. - /// * [Material.shadowColor] which will be used for the color of a drop shadow. - /// * [Material.surfaceTintColor] which will be used as the overlay tint to - /// show elevation. - /// {@endtemplate} - final double elevation; - - /// The color to paint the material. - /// - /// Must be opaque. To create a transparent piece of material, use - /// [MaterialType.transparency]. - /// - /// If [ThemeData.useMaterial3] is true then an optional [surfaceTintColor] - /// overlay may be applied on top of this color to indicate elevation. - /// - /// If [ThemeData.useMaterial3] is false and [ThemeData.applyElevationOverlayColor] - /// is true and [ThemeData.brightness] is [Brightness.dark] then a - /// semi-transparent overlay color will be composited on top of this - /// color to indicate the elevation. This is no longer needed for Material - /// Design 3, which uses [surfaceTintColor]. - /// - /// By default, the color is derived from the [type] of material. - final Color? color; - - /// The color to paint the shadow below the material. - /// - /// When [ThemeData.useMaterial3] is true, and this is null, then no drop - /// shadow will be rendered for this material. If it is non-null, then this - /// color will be used to render a drop shadow below the material. - /// - /// When [ThemeData.useMaterial3] is false, and this is null, then - /// [ThemeData.shadowColor] is used, which defaults to fully opaque black. - /// - /// See also: - /// * [ThemeData.useMaterial3], which determines the default value for this - /// property if it is null. - /// * [ThemeData.applyElevationOverlayColor], which turns elevation overlay - /// on or off for dark themes. - final Color? shadowColor; - - /// The color of the surface tint overlay applied to the material color - /// to indicate elevation. - /// - /// Material Design 3 introduced a new way for some components to indicate - /// their elevation by using a surface tint color overlay on top of the - /// base material [color]. This overlay is painted with an opacity that is - /// related to the [elevation] of the material. - /// - /// If [ThemeData.useMaterial3] is false, then this property is not used. - /// - /// If [ThemeData.useMaterial3] is true and [surfaceTintColor] is not null, - /// then it will be used to overlay the base [color] with an opacity based - /// on the [elevation]. - /// - /// Otherwise, no surface tint will be applied. - /// - /// See also: - /// - /// * [ThemeData.useMaterial3], which turns this feature on. - /// * [ElevationOverlay.applySurfaceTint], which is used to implement the - /// tint. - /// * https://m3.material.io/styles/color/the-color-system/color-roles - /// which specifies how the overlay is applied. - final Color? surfaceTintColor; - - /// The typographical style to use for text within this material. - final TextStyle? textStyle; - - /// Defines the material's shape as well its shadow. - /// - /// If shape is non null, the [borderRadius] is ignored and the material's - /// clip boundary and shadow are defined by the shape. - /// - /// A shadow is only displayed if the [elevation] is greater than - /// zero. - final ShapeBorder? shape; - - /// Whether to paint the [shape] border in front of the [child]. - /// - /// The default value is true. - /// If false, the border will be painted behind the [child]. - final bool borderOnForeground; - - /// {@template flutter.material.Material.clipBehavior} - /// The content will be clipped (or not) according to this option. - /// - /// See the enum [Clip] for details of all possible options and their common - /// use cases. - /// {@endtemplate} - /// - /// Defaults to [Clip.none], and must not be null. - final Clip clipBehavior; - - /// Defines the duration of animated changes for [shape], [elevation], - /// [shadowColor], [surfaceTintColor] and the elevation overlay if it is applied. - /// - /// The default value is [kThemeChangeDuration]. - final Duration animationDuration; - - /// If non-null, the corners of this box are rounded by this - /// [BorderRadiusGeometry] value. - /// - /// Otherwise, the corners specified for the current [type] of material are - /// used. - /// - /// If [shape] is non null then the border radius is ignored. - /// - /// Must be null if [type] is [MaterialType.circle]. - final BorderRadiusGeometry? borderRadius; - - @override - State createState() => _ScalableRippleTapState(); -} - -class _ScalableRippleTapState extends State { - late DateTime _clickTime; - - final _originalTransform = Matrix4.identity(); - - late Matrix4 _transform = _originalTransform; - - @override - Widget build(BuildContext context) { - return AnimatedContainer( - duration: widget.animationDuration, - transformAlignment: Alignment.center, - transform: _transform, - child: RippleTap( - type: widget.type, - elevation: widget.elevation, - color: widget.color, - shadowColor: widget.shadowColor, - surfaceTintColor: widget.surfaceTintColor, - textStyle: widget.textStyle, - shape: widget.shape, - borderOnForeground: widget.borderOnForeground, - clipBehavior: widget.clipBehavior, - animationDuration: widget.animationDuration, - borderRadius: widget.borderRadius, - onTap: widget.onTap, - onTapDown: (_) => _scaleStart(), - onTapCancel: _scaleEnd, - onTapUp: (_) => _scaleEnd(), - onLongPress: widget.onLongPress, - onDoubleTap: widget.onDoubleTap, - child: widget.child, - ), - ); - } - - Future _scaleEnd() async { - final diff = DateTime.now().difference(_clickTime); - if (diff < widget.animationDuration) { - await Future.delayed(widget.animationDuration - diff); - } - if (mounted) { - setState(() { - _transform = _originalTransform; - }); - } - } - - void _scaleStart() { - _clickTime = DateTime.now(); - if (mounted) { - setState(() { - _transform = widget.transform ?? Matrix4.diagonal3Values(0.96, 0.96, 1); - }); - } - } -}