style(app): 格式化并拆分长函数
This commit is contained in:
1 parent
2b2b7e9913
commit
7850dfc746
31 files changed
+388
-318
No files matched your search
+2
-1
@@ -146,7 +146,8 @@ class _LumaDramaAppState extends State<LumaDramaApp> {
|
||||
);
|
||||
|
||||
late final CommerceRepository _commerceRepository =
|
||||
widget.commerceRepository ?? HttpCommerceRepository(CommerceApi(_apiClient));
|
||||
widget.commerceRepository ??
|
||||
HttpCommerceRepository(CommerceApi(_apiClient));
|
||||
|
||||
late final WalletController _walletController = WalletController(
|
||||
_commerceRepository,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'json_numbers.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'json_numbers.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'drama.dart';
|
||||
import 'json_numbers.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import '../models/order.dart';
|
||||
import '../models/product.dart';
|
||||
|
||||
@@ -59,7 +59,8 @@ class _CachedTicket {
|
||||
final PlayTicket ticket;
|
||||
final DateTime fetchedAt;
|
||||
|
||||
bool isExpired(DateTime now, Duration ttl) => now.difference(fetchedAt) >= ttl;
|
||||
bool isExpired(DateTime now, Duration ttl) =>
|
||||
now.difference(fetchedAt) >= ttl;
|
||||
}
|
||||
|
||||
/// 测试与离线场景用:按集返回固定票据,并记录被请求过几次。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import '../models/search_result.dart';
|
||||
import '../remote/catalog_api.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import '../models/order.dart';
|
||||
import '../models/product.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import '../../core/data/models/drama.dart';
|
||||
import '../../core/data/repositories/search_repository.dart';
|
||||
@@ -10,9 +10,5 @@ class DiscoverBinding {
|
||||
required Drama drama,
|
||||
required WatchHistoryRepository history,
|
||||
SearchRepository? search,
|
||||
}) => DiscoverLogic(
|
||||
drama: drama,
|
||||
history: history,
|
||||
searchRepository: search,
|
||||
);
|
||||
}) => DiscoverLogic(drama: drama, history: history, searchRepository: search);
|
||||
}
|
||||
@@ -134,7 +134,8 @@ class DiscoverLogic extends ChangeNotifier {
|
||||
|
||||
void _scheduleSearch(String query) {
|
||||
final alreadyRequested = query == _lastRequestedQuery;
|
||||
if (alreadyRequested && (searchLoading || searchResults.isNotEmpty || searchError != null)) {
|
||||
if (alreadyRequested &&
|
||||
(searchLoading || searchResults.isNotEmpty || searchError != null)) {
|
||||
return;
|
||||
}
|
||||
_lastRequestedQuery = query;
|
||||
|
||||
@@ -181,7 +181,8 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 12),
|
||||
child: _languages(l10n, allDramas),
|
||||
),
|
||||
), if (logic.resumeRecord != null && logic.resumeDrama(allDramas) != null)
|
||||
),
|
||||
if (logic.resumeRecord != null && logic.resumeDrama(allDramas) != null)
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 14),
|
||||
@@ -495,6 +496,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
||||
if (code == 'en') return l10n.languageEnglish;
|
||||
return code;
|
||||
}
|
||||
|
||||
Widget _continueCard(
|
||||
AppLocalizations l10n,
|
||||
Drama drama,
|
||||
|
||||
@@ -25,24 +25,40 @@ class _HomeDramaGrid extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loading = _loadingState();
|
||||
if (loading != null) return loading;
|
||||
final search = _searchState();
|
||||
if (search != null) return search;
|
||||
if (dramas.isEmpty) return _emptyState();
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: _masonry(dramas)),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _loadingState() {
|
||||
if (categories.loading && categories.feed.sections.isEmpty) {
|
||||
return const SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
|
||||
);
|
||||
}
|
||||
if (categories.error != null) {
|
||||
return _fill(
|
||||
EmptyState(
|
||||
key: const Key('home-categories-error'),
|
||||
icon: Icons.cloud_off_rounded,
|
||||
title: l10n.categoriesError,
|
||||
subtitle: l10n.emptyHint,
|
||||
actionLabel: l10n.retry,
|
||||
onAction: categories.load,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (categories.error == null) return null;
|
||||
return _fill(
|
||||
EmptyState(
|
||||
key: const Key('home-categories-error'),
|
||||
icon: Icons.cloud_off_rounded,
|
||||
title: l10n.categoriesError,
|
||||
subtitle: l10n.emptyHint,
|
||||
actionLabel: l10n.retry,
|
||||
onAction: categories.load,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _searchState() {
|
||||
if (searching && searchLoading) {
|
||||
return const SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
@@ -54,39 +70,33 @@ class _HomeDramaGrid extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (searching && searchError != null) {
|
||||
return _fill(
|
||||
EmptyState(
|
||||
key: const Key('discover-search-error'),
|
||||
icon: Icons.cloud_off_rounded,
|
||||
title: errorText(l10n, searchError, l10n.searchFailed),
|
||||
actionLabel: l10n.retry,
|
||||
actionKey: const Key('discover-search-retry'),
|
||||
onAction: onRetrySearch,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (dramas.isEmpty) {
|
||||
return _fill(
|
||||
EmptyState(
|
||||
key: const Key('home-all-dramas-empty'),
|
||||
icon: searching
|
||||
? Icons.search_off_rounded
|
||||
: Icons.movie_filter_outlined,
|
||||
title: searching
|
||||
? l10n.noSearchResults
|
||||
: categories.selectedSectionMissing
|
||||
? l10n.noCategoryDramas
|
||||
: l10n.noSearchResults,
|
||||
subtitle: l10n.emptyHint,
|
||||
),
|
||||
);
|
||||
}
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: _masonry(dramas)),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
],
|
||||
if (!searching || searchError == null) return null;
|
||||
return _fill(
|
||||
EmptyState(
|
||||
key: const Key('discover-search-error'),
|
||||
icon: Icons.cloud_off_rounded,
|
||||
title: errorText(l10n, searchError, l10n.searchFailed),
|
||||
actionLabel: l10n.retry,
|
||||
actionKey: const Key('discover-search-retry'),
|
||||
onAction: onRetrySearch,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _emptyState() {
|
||||
return _fill(
|
||||
EmptyState(
|
||||
key: const Key('home-all-dramas-empty'),
|
||||
icon: searching
|
||||
? Icons.search_off_rounded
|
||||
: Icons.movie_filter_outlined,
|
||||
title: searching
|
||||
? l10n.noSearchResults
|
||||
: categories.selectedSectionMissing
|
||||
? l10n.noCategoryDramas
|
||||
: l10n.noSearchResults,
|
||||
subtitle: l10n.emptyHint,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -283,10 +283,7 @@ class PlayerLogic extends ChangeNotifier {
|
||||
}
|
||||
final ready = await ensureTicket(index, forceRefresh: true);
|
||||
if (ready) {
|
||||
await session.select(
|
||||
index,
|
||||
preloadIndex: nextVisibleIndex(index),
|
||||
);
|
||||
await session.select(index, preloadIndex: nextVisibleIndex(index));
|
||||
}
|
||||
return ready;
|
||||
} finally {
|
||||
|
||||
@@ -991,7 +991,8 @@ class _PlaybackOverlayState extends State<_PlaybackOverlay> {
|
||||
Navigator.pop(sheetContext);
|
||||
widget.onQualityTap();
|
||||
},
|
||||
), ListTile(
|
||||
),
|
||||
ListTile(
|
||||
key: const Key('player-more-download'),
|
||||
leading: const Icon(
|
||||
Icons.download_outlined,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -26,54 +26,60 @@ class QualitySheet extends StatelessWidget {
|
||||
key: const Key('player-quality-sheet'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 2, 20, 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
l10n.playbackQuality,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final quality in qualities)
|
||||
ListTile(
|
||||
key: Key('player-quality-$quality'),
|
||||
dense: true,
|
||||
title: Text(
|
||||
_label(l10n, quality),
|
||||
style: TextStyle(
|
||||
color: quality == selected ? AppColors.blue : AppColors.cream,
|
||||
fontSize: 13,
|
||||
fontWeight: quality == selected
|
||||
? FontWeight.w700
|
||||
: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
trailing: quality == selected
|
||||
? const Icon(
|
||||
Icons.check_rounded,
|
||||
color: AppColors.blue,
|
||||
size: 18,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 6, 20, 14),
|
||||
child: Text(
|
||||
l10n.qualityAutoHint,
|
||||
style: const TextStyle(color: AppColors.muted, fontSize: 11),
|
||||
),
|
||||
),
|
||||
_header(l10n),
|
||||
for (final quality in qualities) _qualityTile(l10n, quality),
|
||||
_hint(l10n),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _header(AppLocalizations l10n) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 2, 20, 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
l10n.playbackQuality,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _qualityTile(AppLocalizations l10n, String quality) {
|
||||
final active = quality == selected;
|
||||
return ListTile(
|
||||
key: Key('player-quality-$quality'),
|
||||
dense: true,
|
||||
title: Text(
|
||||
_label(l10n, quality),
|
||||
style: TextStyle(
|
||||
color: active ? AppColors.blue : AppColors.cream,
|
||||
fontSize: 13,
|
||||
fontWeight: active ? FontWeight.w700 : FontWeight.w500,
|
||||
),
|
||||
),
|
||||
trailing: active
|
||||
? const Icon(Icons.check_rounded, color: AppColors.blue, size: 18)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _hint(AppLocalizations l10n) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 6, 20, 14),
|
||||
child: Text(
|
||||
l10n.qualityAutoHint,
|
||||
style: const TextStyle(color: AppColors.muted, fontSize: 11),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _label(AppLocalizations l10n, String quality) =>
|
||||
quality == autoQuality ? l10n.qualityAuto : quality;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
@@ -122,7 +122,9 @@ class _StorePageState extends State<StorePage> {
|
||||
controller.purchaseError,
|
||||
l10n.orderCreateFailed,
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
return;
|
||||
}
|
||||
widget.onPurchased?.call();
|
||||
@@ -162,80 +164,85 @@ class _ProductTile extends StatelessWidget {
|
||||
children: [
|
||||
_ProductBadge(vip: product.isVip),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
product.name,
|
||||
key: Key('store-product-name-${product.id}'),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_detail(l10n),
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: _info(l10n)),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
product.displayPrice,
|
||||
key: Key('store-product-price-${product.id}'),
|
||||
style: const TextStyle(
|
||||
color: AppColors.gold,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: 88,
|
||||
height: 32,
|
||||
child: FilledButton(
|
||||
key: Key('store-buy-${product.id}'),
|
||||
onPressed: purchasing ? null : onBuy,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: AppColors.blue,
|
||||
foregroundColor: AppColors.onPrimary,
|
||||
padding: EdgeInsets.zero,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
child: purchasing
|
||||
? const SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: AppColors.onPrimary,
|
||||
),
|
||||
)
|
||||
: Text(l10n.buy),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_price(l10n),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _info(AppLocalizations l10n) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
product.name,
|
||||
key: Key('store-product-name-${product.id}'),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onSurface,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_detail(l10n),
|
||||
style: const TextStyle(color: AppColors.muted, fontSize: 11),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _price(AppLocalizations l10n) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
product.displayPrice,
|
||||
key: Key('store-product-price-${product.id}'),
|
||||
style: const TextStyle(
|
||||
color: AppColors.gold,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: 88,
|
||||
height: 32,
|
||||
child: FilledButton(
|
||||
key: Key('store-buy-${product.id}'),
|
||||
onPressed: purchasing ? null : onBuy,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: AppColors.blue,
|
||||
foregroundColor: AppColors.onPrimary,
|
||||
padding: EdgeInsets.zero,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
child: purchasing ? _buyProgress() : Text(l10n.buy),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buyProgress() {
|
||||
return const SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: AppColors.onPrimary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _detail(AppLocalizations l10n) {
|
||||
if (product.isVip) return l10n.vipDuration(product.vipDays);
|
||||
final base = l10n.walletBalanceShort(product.coinAmount);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Author: fruit.com.cn
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -90,9 +90,8 @@ class _WalletPageState extends State<WalletPage> {
|
||||
void _openRecharge(WalletController wallet) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => RechargePage(
|
||||
onPurchased: () => unawaited(wallet.load()),
|
||||
),
|
||||
builder: (_) =>
|
||||
RechargePage(onPurchased: () => unawaited(wallet.load())),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -320,7 +319,9 @@ class _LedgerTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final positive = entry.changeAmount >= 0;
|
||||
final color = positive ? AppColors.success : AppColors.accent;
|
||||
final amount = positive ? '+${entry.changeAmount}' : '${entry.changeAmount}';
|
||||
final amount = positive
|
||||
? '+${entry.changeAmount}'
|
||||
: '${entry.changeAmount}';
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
Reference in new issue
Block a user