feat: 逻辑与页面布局分离
This commit is contained in:
1 parent
b1f6ce4bf9
commit
1e61ae7a0b
11 files changed
+1233
-907
No files matched your search
@@ -1,7 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../core/config/locale_controller.dart';
|
||||
import '../../core/data/models/drama.dart';
|
||||
import '../../core/data/repositories/watch_history_repository.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
|
||||
class MineLogic {
|
||||
enum MyPageAction { home, discover, resume }
|
||||
|
||||
class MineLogic extends ChangeNotifier {
|
||||
MineLogic({
|
||||
required this.history,
|
||||
required this.drama,
|
||||
@@ -15,6 +22,11 @@ class MineLogic {
|
||||
late Future<Duration?> position;
|
||||
int section;
|
||||
int filter = 0;
|
||||
bool _disposed = false;
|
||||
|
||||
void notify() {
|
||||
if (!_disposed) notifyListeners();
|
||||
}
|
||||
|
||||
void refreshPosition() {
|
||||
position = history.positionFor(
|
||||
@@ -23,4 +35,82 @@ class MineLogic {
|
||||
drama.episodes.first.id,
|
||||
);
|
||||
}
|
||||
|
||||
void selectSection(int index) {
|
||||
section = index;
|
||||
notify();
|
||||
}
|
||||
|
||||
void selectFilter(int index) {
|
||||
filter = index;
|
||||
notify();
|
||||
}
|
||||
|
||||
void syncPage({
|
||||
required bool active,
|
||||
required bool wasActive,
|
||||
required Drama nextDrama,
|
||||
required bool dramaChanged,
|
||||
required int sectionRequest,
|
||||
required int previousSectionRequest,
|
||||
required int initialSection,
|
||||
}) {
|
||||
if (active && (!wasActive || dramaChanged)) {
|
||||
drama = nextDrama;
|
||||
refreshPosition();
|
||||
notify();
|
||||
}
|
||||
if (sectionRequest != previousSectionRequest) {
|
||||
section = initialSection;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
void act(
|
||||
BuildContext context,
|
||||
MyPageAction action, {
|
||||
required bool embedded,
|
||||
ValueChanged<MyPageAction>? onAction,
|
||||
}) {
|
||||
if (embedded) {
|
||||
onAction?.call(action);
|
||||
} else {
|
||||
Navigator.pop(context, action);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showLanguage(BuildContext context) async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
await showModalBottomSheet<void>(
|
||||
context: context,
|
||||
backgroundColor: const Color(0xFF1C1D22),
|
||||
showDragHandle: true,
|
||||
builder: (sheetContext) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(title: Text(l10n.language)),
|
||||
for (final (code, label) in [
|
||||
('en', l10n.english),
|
||||
('zh', l10n.simplifiedChinese),
|
||||
])
|
||||
ListTile(
|
||||
title: Text(label),
|
||||
onTap: () {
|
||||
context.read<LocaleController>().select(code);
|
||||
Navigator.pop(sheetContext);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../core/config/locale_controller.dart';
|
||||
import '../../core/formatting/localized_content.dart';
|
||||
import '../../core/data/models/drama.dart';
|
||||
import '../../core/data/repositories/watch_history_repository.dart';
|
||||
@@ -10,8 +9,6 @@ import '../../l10n/app_localizations.dart';
|
||||
import 'mine_binding.dart';
|
||||
import 'mine_logic.dart';
|
||||
|
||||
enum MyPageAction { home, discover, resume }
|
||||
|
||||
class MyPage extends StatefulWidget {
|
||||
const MyPage({
|
||||
super.key,
|
||||
@@ -58,55 +55,32 @@ class _MyPageState extends State<MyPage> {
|
||||
@override
|
||||
void didUpdateWidget(covariant MyPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.active &&
|
||||
(!oldWidget.active || oldWidget.drama.id != widget.drama.id)) {
|
||||
logic.drama = widget.drama;
|
||||
setState(logic.refreshPosition);
|
||||
}
|
||||
if (widget.sectionRequest != oldWidget.sectionRequest) {
|
||||
setState(() => logic.section = widget.initialSection);
|
||||
}
|
||||
}
|
||||
|
||||
void _act(MyPageAction action) {
|
||||
if (widget.embedded) {
|
||||
widget.onAction?.call(action);
|
||||
} else {
|
||||
Navigator.pop(context, action);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showLanguage() async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
await showModalBottomSheet<void>(
|
||||
context: context,
|
||||
backgroundColor: const Color(0xFF1C1D22),
|
||||
showDragHandle: true,
|
||||
builder: (sheetContext) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(title: Text(l10n.language)),
|
||||
for (final (code, label) in [
|
||||
('en', l10n.english),
|
||||
('zh', l10n.simplifiedChinese),
|
||||
])
|
||||
ListTile(
|
||||
title: Text(label),
|
||||
onTap: () {
|
||||
context.read<LocaleController>().select(code);
|
||||
Navigator.pop(sheetContext);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
logic.syncPage(
|
||||
active: widget.active,
|
||||
wasActive: oldWidget.active,
|
||||
nextDrama: widget.drama,
|
||||
dramaChanged: oldWidget.drama.id != widget.drama.id,
|
||||
sectionRequest: widget.sectionRequest,
|
||||
previousSectionRequest: oldWidget.sectionRequest,
|
||||
initialSection: widget.initialSection,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
logic.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListenableBuilder(
|
||||
listenable: logic,
|
||||
builder: (context, _) => _page(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _page(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final content = SafeArea(
|
||||
bottom: !widget.embedded,
|
||||
@@ -179,7 +153,12 @@ class _MyPageState extends State<MyPage> {
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
tooltip: l10n.search,
|
||||
onPressed: () => _act(MyPageAction.discover),
|
||||
onPressed: () => logic.act(
|
||||
context,
|
||||
MyPageAction.discover,
|
||||
embedded: widget.embedded,
|
||||
onAction: widget.onAction,
|
||||
),
|
||||
icon: const Icon(Icons.search_rounded, size: 23),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
@@ -324,7 +303,12 @@ class _MyPageState extends State<MyPage> {
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton.icon(
|
||||
onPressed: () => _act(MyPageAction.resume),
|
||||
onPressed: () => logic.act(
|
||||
context,
|
||||
MyPageAction.resume,
|
||||
embedded: widget.embedded,
|
||||
onAction: widget.onAction,
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: _orange,
|
||||
foregroundColor: Colors.white,
|
||||
@@ -368,7 +352,7 @@ class _MyPageState extends State<MyPage> {
|
||||
children: [
|
||||
for (var index = 0; index < tabs.length; index++) ...[
|
||||
InkWell(
|
||||
onTap: () => setState(() => logic.section = index),
|
||||
onTap: () => logic.selectSection(index),
|
||||
child: Text(
|
||||
tabs[index],
|
||||
style: TextStyle(
|
||||
@@ -397,7 +381,7 @@ class _MyPageState extends State<MyPage> {
|
||||
child: ChoiceChip(
|
||||
label: Text(filters[index]),
|
||||
selected: logic.filter == index,
|
||||
onSelected: (_) => setState(() => logic.filter = index),
|
||||
onSelected: (_) => logic.selectFilter(index),
|
||||
showCheckmark: false,
|
||||
visualDensity: VisualDensity.compact,
|
||||
backgroundColor: const Color(0xFF242328),
|
||||
@@ -418,7 +402,12 @@ class _MyPageState extends State<MyPage> {
|
||||
if (logic.section == 0) const SizedBox(height: 11),
|
||||
if (logic.section == 0 && hasHistory)
|
||||
InkWell(
|
||||
onTap: () => _act(MyPageAction.resume),
|
||||
onTap: () => logic.act(
|
||||
context,
|
||||
MyPageAction.resume,
|
||||
embedded: widget.embedded,
|
||||
onAction: widget.onAction,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(9),
|
||||
@@ -499,7 +488,12 @@ class _MyPageState extends State<MyPage> {
|
||||
if (widget.onDramaSelected != null) {
|
||||
widget.onDramaSelected!(drama);
|
||||
} else if (drama.id == widget.drama.id) {
|
||||
_act(MyPageAction.resume);
|
||||
logic.act(
|
||||
context,
|
||||
MyPageAction.resume,
|
||||
embedded: widget.embedded,
|
||||
onAction: widget.onAction,
|
||||
);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@@ -588,7 +582,7 @@ class _MyPageState extends State<MyPage> {
|
||||
const Icon(Icons.chevron_right_rounded, size: 18),
|
||||
],
|
||||
),
|
||||
onTap: _showLanguage,
|
||||
onTap: () => logic.showLanguage(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -606,12 +600,22 @@ class _MyPageState extends State<MyPage> {
|
||||
_NavItem(
|
||||
icon: Icons.play_circle_outline_rounded,
|
||||
label: l10n.homeTab,
|
||||
onTap: () => Navigator.pop(context, MyPageAction.home),
|
||||
onTap: () => logic.act(
|
||||
context,
|
||||
MyPageAction.home,
|
||||
embedded: widget.embedded,
|
||||
onAction: widget.onAction,
|
||||
),
|
||||
),
|
||||
_NavItem(
|
||||
icon: Icons.explore_outlined,
|
||||
label: l10n.discoverTab,
|
||||
onTap: () => Navigator.pop(context, MyPageAction.discover),
|
||||
onTap: () => logic.act(
|
||||
context,
|
||||
MyPageAction.discover,
|
||||
embedded: widget.embedded,
|
||||
onAction: widget.onAction,
|
||||
),
|
||||
),
|
||||
_NavItem(
|
||||
icon: Icons.video_library_outlined,
|
||||
|
||||
Reference in new issue
Block a user