Files
2026-09-08 13:45:13 +08:00

217 lines
6.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:go_router/go_router.dart';
import 'package:new_learn/core/api/class/api.dart';
import 'package:new_learn/core/api/class/model.dart';
import 'package:new_learn/core/utils/img_url.dart';
import 'package:new_learn/features/class/widgets/post_list_body.dart';
/// ============================================================
/// 【MyInteractPage】「我的互动」页 —— 对标 Vue pages/classPackage/myInteract/myInteract.vue
///
/// 一、入口:从「我的」页等进入。
/// 二、结构:NavBar("我的互动") + 3 颗胶囊 tab + 内容区
/// 「我的动态 / 我的点赞 / 我的留言」
/// 三、子列表用通用 PostListBody:
/// 动态 getUserClassPost(queryType=0);点赞 getMyLikePage;留言 getMyCommentPage。
/// 点赞页取消点赞时从列表移除该项(removeOnUnlike);其余保留。
/// 四、懒加载:默认建第一个 tab,切到再建(对标 renderedTabs)。
/// ============================================================
class MyInteractPage extends StatefulWidget {
const MyInteractPage({super.key});
@override
State<MyInteractPage> createState() => _MyInteractPageState();
}
class _MyInteractPageState extends State<MyInteractPage> {
static const List<String> _tabs = ['我的动态', '我的点赞', '我的留言'];
int _currentTab = 0;
final List<bool> _loadedTabs = [true, false, false];
void _switchTab(int index) {
if (_currentTab == index) return;
setState(() {
_loadedTabs[index] = true;
_currentTab = index;
});
}
void _openDetail(UserPostDto post) {
context.push('/class/my-update-detail', extra: post);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F8FA),
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
elevation: 0,
title: const Text(
'我的互动',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Color(0xFF1D2B47),
),
),
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Color(0xFF1D2B47)),
onPressed: () =>
context.canPop() ? context.pop() : context.go('/class'),
),
),
// 对齐 Vue .activity-box:背景图 + #F5F8FA 底色
// background-image: url(/StaticResource/2026-04-07/myInteract-bg.png)
// background-size: 100% 540rpx → height: 270.h + BoxFit.fitWidth
body: Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: SizedBox(
height: 270.h, // Vue 540rpx
child: CachedNetworkImage(
imageUrl: ImgUrl.build(
'/StaticResource/2026-04-07/myInteract-bg.png',
),
fit: BoxFit.fitWidth,
placeholder: (_, __) => const SizedBox.shrink(),
errorWidget: (_, __, ___) => const SizedBox.shrink(),
),
),
),
// 内容区:顶 padding = 系统状态栏 + AppBar 高度,对齐 Vue NavBar 后的起点
Padding(
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + kToolbarHeight,
),
child: Column(
children: [
_buildTabs(),
Expanded(
child: IndexedStack(
index: _currentTab,
children: [
if (_loadedTabs[0]) _UpdateTab(onTapPost: _openDetail),
if (_loadedTabs[1]) _LikeTab(onTapPost: _openDetail),
if (_loadedTabs[2]) _CommentTab(onTapPost: _openDetail),
],
),
),
],
),
),
],
),
);
}
Widget _buildTabs() {
return Container(
width: double.infinity,
padding: EdgeInsets.fromLTRB(14.w, 12.h, 14.w, 22.h),
child: Row(
children: [
for (int i = 0; i < _tabs.length; i++) ...[
if (i > 0) SizedBox(width: 12.w),
_buildTabItem(i),
],
],
),
);
}
Widget _buildTabItem(int index) {
final active = _currentTab == index;
return GestureDetector(
onTap: () => _switchTab(index),
child: Container(
width: 84.w,
height: 37.h,
alignment: Alignment.center,
decoration: BoxDecoration(
color: active ? const Color(0xFF3D7DF5) : Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
_tabs[index],
style: TextStyle(
fontSize: 15,
color: active ? Colors.white : const Color(0xFF687FAD),
),
),
),
);
}
}
class _UpdateTab extends StatelessWidget {
const _UpdateTab({required this.onTapPost});
final ValueChanged<UserPostDto> onTapPost;
@override
Widget build(BuildContext context) {
return PostListBody(
fetchPage:
({required pageIndex, required pageSize, required prevPageId}) =>
ClassApi.instance.getUserClassPost(
queryType: 0,
pageIndex: pageIndex,
pageSize: pageSize,
prevPageId: prevPageId,
),
emptyText: '暂无动态',
onTapPost: onTapPost,
);
}
}
class _LikeTab extends StatelessWidget {
const _LikeTab({required this.onTapPost});
final ValueChanged<UserPostDto> onTapPost;
@override
Widget build(BuildContext context) {
return PostListBody(
fetchPage:
({required pageIndex, required pageSize, required prevPageId}) =>
ClassApi.instance.getMyLikePage(
pageIndex: pageIndex,
pageSize: pageSize,
prevPageId: prevPageId,
),
emptyText: '暂无点赞',
removeOnUnlike: true,
onTapPost: onTapPost,
);
}
}
class _CommentTab extends StatelessWidget {
const _CommentTab({required this.onTapPost});
final ValueChanged<UserPostDto> onTapPost;
@override
Widget build(BuildContext context) {
return PostListBody(
fetchPage:
({required pageIndex, required pageSize, required prevPageId}) =>
ClassApi.instance.getMyCommentPage(
pageIndex: pageIndex,
pageSize: pageSize,
prevPageId: prevPageId,
),
emptyText: '暂无留言',
onTapPost: onTapPost,
);
}
}