chore(client): 归档原型阶段未提交改动(binding/logic/view 分层与各页迭代)
为什么:客户端原型在 14d7bd3 之后又做了一轮(页面分层、发现页、分类滑动、播放器控件等),
一直留在工作区没入库。下一批账号链路要改其中的 app.dart / home_view / player_view / mine_view,
先归档这批既存改动,本批的实际 diff 才说得清边界。
只归档,不改行为:当前工作区 flutter analyze 0 问题、flutter test 148 全过。
This commit is contained in:
1 parent
14d7bd39c7
commit
77d5748dbb
33 files changed
+1264
-1064
No files matched your search
@@ -1,30 +0,0 @@
|
||||
import '../../core/data/models/drama.dart';
|
||||
|
||||
enum HomeFeedCategory { following, animated, liveAction, recommended }
|
||||
|
||||
/// Maps catalog entries into home feeds without owning playback or UI state.
|
||||
class HomeFeedSections {
|
||||
HomeFeedSections(this.dramas, {this.followedDramaIds = const {}});
|
||||
|
||||
final List<Drama> dramas;
|
||||
final Set<String> followedDramaIds;
|
||||
|
||||
List<int> indicesFor(HomeFeedCategory category) => [
|
||||
for (var index = 0; index < dramas.length; index++)
|
||||
if (dramas[index].episodes.isNotEmpty &&
|
||||
switch (category) {
|
||||
HomeFeedCategory.following => followedDramaIds.contains(
|
||||
dramas[index].id,
|
||||
),
|
||||
HomeFeedCategory.animated =>
|
||||
dramas[index].format == DramaFormat.animated,
|
||||
HomeFeedCategory.liveAction =>
|
||||
dramas[index].format == DramaFormat.liveAction,
|
||||
HomeFeedCategory.recommended => true,
|
||||
})
|
||||
index,
|
||||
];
|
||||
|
||||
Drama dramaAt(HomeFeedCategory category, int categoryIndex) =>
|
||||
dramas[indicesFor(category)[categoryIndex]];
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Converts horizontal drags into a previous/next category request.
|
||||
/// Placement decides where the gesture is active; the seek bar sits outside it.
|
||||
class CategorySwipeRegion extends StatefulWidget {
|
||||
const CategorySwipeRegion({
|
||||
super.key,
|
||||
required this.onShift,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final ValueChanged<int> onShift;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
State<CategorySwipeRegion> createState() => _CategorySwipeRegionState();
|
||||
}
|
||||
|
||||
class _CategorySwipeRegionState extends State<CategorySwipeRegion> {
|
||||
double _distance = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onHorizontalDragStart: (_) => _distance = 0,
|
||||
onHorizontalDragUpdate: (details) => _distance += details.delta.dx,
|
||||
onHorizontalDragEnd: (details) {
|
||||
final velocity = details.primaryVelocity ?? 0;
|
||||
if (_distance.abs() < 44 && velocity.abs() < 300) return;
|
||||
final direction = _distance.abs() >= 44 ? _distance : velocity;
|
||||
widget.onShift(direction < 0 ? 1 : -1);
|
||||
},
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
import '../home_feed_sections.dart';
|
||||
import 'category_swipe_region.dart';
|
||||
|
||||
/// Reused by playable and empty home feeds.
|
||||
class HomeFeedHeader extends StatelessWidget {
|
||||
const HomeFeedHeader({
|
||||
super.key,
|
||||
required this.l10n,
|
||||
required this.category,
|
||||
required this.onCategorySelected,
|
||||
required this.onCategoryShift,
|
||||
required this.onSearch,
|
||||
required this.onMy,
|
||||
this.onMenu,
|
||||
this.onMute,
|
||||
this.muted = false,
|
||||
});
|
||||
|
||||
final AppLocalizations l10n;
|
||||
final HomeFeedCategory category;
|
||||
final ValueChanged<HomeFeedCategory> onCategorySelected;
|
||||
final ValueChanged<int> onCategoryShift;
|
||||
final VoidCallback onSearch;
|
||||
final VoidCallback onMy;
|
||||
final VoidCallback? onMenu;
|
||||
final VoidCallback? onMute;
|
||||
final bool muted;
|
||||
|
||||
String _label(HomeFeedCategory category) => switch (category) {
|
||||
HomeFeedCategory.following => l10n.followingTab,
|
||||
HomeFeedCategory.animated => l10n.animatedTab,
|
||||
HomeFeedCategory.liveAction => l10n.liveActionTab,
|
||||
HomeFeedCategory.recommended => l10n.recommendedTab,
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => IconTheme(
|
||||
data: const IconThemeData(color: Colors.white),
|
||||
child: DefaultTextStyle.merge(
|
||||
style: const TextStyle(color: Colors.white),
|
||||
child: _header(context),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _header(BuildContext context) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (onMenu != null)
|
||||
IconButton(
|
||||
key: const Key('home-open-menu'),
|
||||
tooltip: l10n.openQuickMenu,
|
||||
onPressed: onMenu,
|
||||
icon: const Icon(Icons.menu_rounded),
|
||||
),
|
||||
Container(
|
||||
width: 29,
|
||||
height: 29,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFF5C37),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(Icons.play_arrow_rounded, color: Colors.white),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
l10n.homeTab,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
tooltip: l10n.search,
|
||||
onPressed: onSearch,
|
||||
icon: const Icon(Icons.search_rounded),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: l10n.myTab,
|
||||
onPressed: onMy,
|
||||
icon: const CircleAvatar(
|
||||
radius: 15,
|
||||
backgroundColor: Color(0xFF3C3A3A),
|
||||
child: Icon(
|
||||
Icons.person_rounded,
|
||||
color: Colors.white,
|
||||
size: 19,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 4, 12, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CategorySwipeRegion(
|
||||
key: const Key('home-category-swipe'),
|
||||
onShift: onCategoryShift,
|
||||
child: Row(
|
||||
children: [
|
||||
for (final item in HomeFeedCategory.values)
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
key: Key('home-category-${item.name}'),
|
||||
onTap: () => onCategorySelected(item),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 9),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
_label(item),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: category == item
|
||||
? const Color(0xFFFFC2B0)
|
||||
: const Color(0xFFF7F4F2),
|
||||
shadows: const [
|
||||
Shadow(blurRadius: 8, color: Colors.black),
|
||||
],
|
||||
fontSize: 12,
|
||||
fontWeight: category == item
|
||||
? FontWeight.w700
|
||||
: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SizedBox(
|
||||
width: 18,
|
||||
height: 2,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: category == item
|
||||
? const Color(0xFFFF5C37)
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (onMute != null)
|
||||
IconButton(
|
||||
tooltip: muted ? l10n.unmute : l10n.mute,
|
||||
onPressed: onMute,
|
||||
icon: Icon(
|
||||
muted ? Icons.volume_off_rounded : Icons.volume_up_rounded,
|
||||
size: 21,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
||||
import '../../../core/config/locale_controller.dart';
|
||||
import '../../../core/data/models/drama.dart';
|
||||
import '../../../core/formatting/localized_content.dart';
|
||||
import '../../../core/widgets/remote_image.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
/// Shortcuts for the existing root tabs; this widget owns no navigation state.
|
||||
@@ -27,7 +28,7 @@ class HomeQuickDrawer extends StatelessWidget {
|
||||
|
||||
static const _panel = Color(0xFF222225);
|
||||
static const _card = Color(0xFF303033);
|
||||
static const _orange = Color(0xFFFF5C37);
|
||||
static const _orange = Color(0xFF2F7BFF);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -88,11 +89,10 @@ class HomeQuickDrawer extends StatelessWidget {
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.asset(
|
||||
drama.posterAsset,
|
||||
child: RemoteImage(
|
||||
drama.posterUrl,
|
||||
width: 74,
|
||||
height: 100,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
@@ -111,18 +111,9 @@ class HomeQuickDrawer extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
Text(
|
||||
l10n.episodeCount(drama.episodes.length),
|
||||
l10n.episodeCount(drama.episodeCount),
|
||||
style: const TextStyle(color: Color(0xFFE7E2E0)),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
l10n.freeTestMedia,
|
||||
maxLines: 2,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFFFB99E),
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user