From e013d40c5ba2bd73ef05daa4bb012659cabdccc9 Mon Sep 17 00:00:00 2001 From: hanami <1021210354@qq.com> Date: Sat, 7 Sep 2024 11:27:22 +0800 Subject: [PATCH] feat: setting modal --- assets/images/card_placeholder.webp | Bin 102162 -> 0 bytes lib/{http => api}/ArticleApi.dart | 31 +- lib/{http => api}/BanListChangeApi.dart | 14 +- lib/{http => api}/CardApi.dart | 20 +- lib/api/CharacterApi.dart | 15 + lib/api/DeckTypeApi.dart | 17 + lib/{http => api}/NavTabApi.dart | 6 +- lib/{http => api}/PackSetApi.dart | 13 +- lib/api/SkillApi.dart | 21 + lib/api/SkillStatsApi.dart | 14 + lib/api/TierListApi.dart | 29 ++ lib/api/TopDeckApi.dart | 24 + lib/api/WorldApi.dart | 15 + lib/{http => api}/http.dart | 0 lib/components/MdCardItemView.dart | 4 +- lib/components/MdCardItemView2.dart | 3 +- lib/components/MdCardsBoxLayout.dart | 24 +- lib/components/ModalBottomSheetWrap.dart | 34 ++ lib/components/SettingModalView.dart | 226 +++++---- lib/components/SkillModalView.dart | 46 +- .../ThemeColorSelectorPanelModalView.dart | 91 ++++ lib/components/TopDeckItem.dart | 28 +- .../cards_viewpager/CardView.dart | 19 +- .../cards_viewpager/index.dart | 4 +- lib/constant/colors.dart | 23 - .../exception/HttpCacheException.dart | 6 - lib/extension/DateTime.dart | 8 +- lib/extension/Function.dart | 59 --- lib/extension/Future.dart | 7 +- lib/extension/String.dart | 8 +- lib/gen/assets.gen.dart | 19 +- lib/hive/MyHive.dart | 16 +- lib/hive/db/ArticleHiveDb.dart | 8 +- lib/hive/db/BanListChangeHiveDb.dart | 8 +- lib/hive/db/CardHiveDb.dart | 4 +- lib/hive/db/DarkModeHiveDb.dart | 4 +- lib/hive/db/DeckTypeDetailHiveDb.dart | 8 +- lib/hive/db/NavHiveDb.dart | 12 +- lib/hive/db/PackHiveDb.dart | 4 +- lib/hive/db/PacksHiveDb.dart | 8 +- lib/hive/db/PowerRankingsHiveDb.dart | 8 +- lib/hive/db/SettingHiveDb.dart | 30 ++ lib/hive/db/SkillHiveDb.dart | 8 +- lib/hive/db/TierListHiveDb.dart | 8 +- lib/hive/db/TopDeckHiveDb.dart | 8 +- lib/http/CharacterApi.dart | 6 - lib/http/DeckTypeApi.dart | 8 - lib/http/SkillApi.dart | 14 - lib/http/SkillStatsApi.dart | 6 - lib/http/TierListApi.dart | 18 - lib/http/TopDeckApi.dart | 16 - lib/http/WorldApi.dart | 6 - lib/main.dart | 151 +++--- lib/pages/articles/index.dart | 8 +- .../components/BanListChangePickerView.dart | 7 +- .../components/BanListChangeView.dart | 16 +- .../components/BanStatusCardView.dart | 28 +- lib/pages/cards_viewpager/CardView2.dart | 172 ------- lib/pages/character/index.dart | 41 +- lib/pages/characters/index.dart | 19 +- .../deck_detail/components/DeckInfo.dart | 41 +- lib/pages/deck_detail/index.dart | 5 +- .../components/DeckTypeBreakdownGridView.dart | 24 +- lib/pages/deck_type_detail/index.dart | 76 +-- .../components/EventListView.dart | 4 +- lib/pages/home/index.dart | 28 +- lib/pages/main/index.dart | 2 +- lib/pages/pack_detail/index.dart | 14 +- lib/pages/packs/index.dart | 3 +- lib/pages/skill_stats/index.dart | 11 +- lib/pages/splash/BanStatusCardHiveDb.dart | 8 +- lib/pages/splash/index.dart | 352 +++++++++++++- .../tier_list/components/TierListView.dart | 5 +- lib/pages/top_decks/index.dart | 2 +- lib/pages/webview/index.dart | 93 ++-- lib/store/AppStore.dart | 48 ++ lib/store/BanCardStore.dart | 2 +- lib/type/NavTab.g.dart | 2 + lib/util/index.dart | 53 --- lib/util/time_util.dart | 12 - lib/widget/background.dart | 123 ----- lib/widget/ripple_tap.dart | 441 ------------------ 82 files changed, 1258 insertions(+), 1569 deletions(-) delete mode 100644 assets/images/card_placeholder.webp rename lib/{http => api}/ArticleApi.dart (62%) rename lib/{http => api}/BanListChangeApi.dart (58%) rename lib/{http => api}/CardApi.dart (67%) create mode 100644 lib/api/CharacterApi.dart create mode 100644 lib/api/DeckTypeApi.dart rename lib/{http => api}/NavTabApi.dart (72%) rename lib/{http => api}/PackSetApi.dart (50%) create mode 100644 lib/api/SkillApi.dart create mode 100644 lib/api/SkillStatsApi.dart create mode 100644 lib/api/TierListApi.dart create mode 100644 lib/api/TopDeckApi.dart create mode 100644 lib/api/WorldApi.dart rename lib/{http => api}/http.dart (100%) create mode 100644 lib/components/ModalBottomSheetWrap.dart create mode 100644 lib/components/ThemeColorSelectorPanelModalView.dart rename lib/{pages => components}/cards_viewpager/CardView.dart (94%) rename lib/{pages => components}/cards_viewpager/index.dart (95%) delete mode 100644 lib/constant/colors.dart delete mode 100644 lib/constant/exception/HttpCacheException.dart delete mode 100644 lib/extension/Function.dart create mode 100644 lib/hive/db/SettingHiveDb.dart delete mode 100644 lib/http/CharacterApi.dart delete mode 100644 lib/http/DeckTypeApi.dart delete mode 100644 lib/http/SkillApi.dart delete mode 100644 lib/http/SkillStatsApi.dart delete mode 100644 lib/http/TierListApi.dart delete mode 100644 lib/http/TopDeckApi.dart delete mode 100644 lib/http/WorldApi.dart delete mode 100644 lib/pages/cards_viewpager/CardView2.dart delete mode 100644 lib/util/index.dart delete mode 100644 lib/util/time_util.dart delete mode 100644 lib/widget/background.dart delete mode 100644 lib/widget/ripple_tap.dart diff --git a/assets/images/card_placeholder.webp b/assets/images/card_placeholder.webp deleted file mode 100644 index 98991696437c395ddd9cdee88ef26face8c5f885..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102162 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�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 e0uCkw=PMi*jNPA1Qms}X5Wc=Dimt%A5i zs!*W7FItVxWE&$h)&dVDE_m?iIKGAs`IhnbgXzj>3qEd+T{Aq02~0HQ+(Hb!e>}P( zPY$yBYox$j;pYtUl&|h=Ugi_0 z(nH{5ZXucnYHf}?i4rT>D!j+O;A)h~36>yx?h?kLe~qpKuZ8C8!pnqRmiCA8BfCvQ(KD_~8O65^HnhGBlZ48Ypl9Vb2c1K;c} zBAya+u&)a`Gq71)V9>14PD6A05KCz5J#yNnogG~*CGAbe_|kVYXb0b zk_6lCPnf&^f;l$LQVResme9VGUj!t5aXA64I&04Ji+URC3FMl@E3Zeq`WF#5{&)7u zD2e7`m?N1n-U&%Nm30E?A<7dy0K_s_^9yB=2m+QZ_jcr=61e|u=38F~q1Zc+brS+1 zM{1OmR=J^-TOg#|F|}lD>=-W6J!G}&R@9y3e!1@QWXDkDP^A_c4fHT=;xJyRZqg+d1N{y_@Tg$nSN!N=kLP+D) zgZwu83ADhrGR#KJjo|oI76PLQO##y|8V^Q>EVPJ#i*pUW&hvo)AHhb^kK<>O zky%4CQCY#-pvLVyjX}%awI(uwW6vQe!jQ!BB>w2T`HHP~N5K$Zl%+kngLSt-guf;H z^Ntw`4XT4bF%`!=ggKlxX0d07U{~-2AZ&s|IvZ3eF78HzZ+h_3wD|FbK)GN!p6Ko` znO3j*!zU0+os7Ll3iSGz_UnyS^YxYhyf?ig2TUEBDVcmK&IA2-B~1w<=F$PEd*?%z zWLj+=DNb}B9N~Y0i@vN5|MS*uVsr*0#luUQt>LY6jssd7F`Qzh(ePX!CZ)HQ9gV

;5bJ^PGS8!w*eV=ZyzqIP9S)i0 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 W+bcbl>asG~MmFP5>> 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); - }); - } - } -}