325 lines
12 KiB
Dart
325 lines
12 KiB
Dart
/// 时间: 2025-03-22
|
||
/// 功能: 诗词主页(参考微信小程序布局)
|
||
/// 介绍: 展示诗词内容,支持点赞、收藏、分享等功能,参考wxpm小程序的index页面布局
|
||
/// 最新变化: 2026-04-02 支持深色模式,添加底部内边距适配液态玻璃导航栏
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:get/get.dart';
|
||
|
||
import '../../constants/app_constants.dart';
|
||
import '../../config/app_config.dart';
|
||
import '../../../services/get/home_controller.dart';
|
||
import '../../../services/get/theme_controller.dart';
|
||
import 'home_part.dart';
|
||
import 'set/home_components.dart';
|
||
import 'set/home-load.dart';
|
||
import 'set/home-set.dart';
|
||
import '../../services/get/care_controller.dart';
|
||
import 'care/care_widgets.dart';
|
||
import 'care/care_poetry_page.dart';
|
||
|
||
class HomePage extends StatefulWidget {
|
||
const HomePage({super.key});
|
||
|
||
@override
|
||
State<HomePage> createState() => _HomePageState();
|
||
}
|
||
|
||
class _HomePageState extends State<HomePage>
|
||
with SingleTickerProviderStateMixin {
|
||
final GlobalKey repaintKey = GlobalKey();
|
||
final SecondaryButtonsManager _secondaryButtonsManager =
|
||
SecondaryButtonsManager();
|
||
final FloatingButtonsVisibilityManager _floatingButtonsVisibilityManager =
|
||
FloatingButtonsVisibilityManager();
|
||
final CareController _careController = Get.put(CareController());
|
||
late AnimationController _animationController;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_secondaryButtonsManager.init();
|
||
_floatingButtonsVisibilityManager.init();
|
||
_animationController = AnimationController(
|
||
duration: const Duration(milliseconds: 300),
|
||
vsync: this,
|
||
);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_floatingButtonsVisibilityManager.dispose();
|
||
_animationController.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
Get.lazyPut(() => HomeController());
|
||
final controller = Get.find<HomeController>();
|
||
final themeController = Get.find<ThemeController>();
|
||
final careController = Get.find<CareController>();
|
||
|
||
return Obx(() {
|
||
final isDark = themeController.isDarkModeRx.value;
|
||
|
||
// 触发动画
|
||
if (_animationController.status != AnimationStatus.forward) {
|
||
_animationController.forward(from: 0);
|
||
}
|
||
|
||
// 关怀模式开启时显示关怀页面,添加过渡动画
|
||
if (careController.isCareModeEnabled) {
|
||
return FadeTransition(
|
||
opacity: Tween<double>(begin: 0, end: 1).animate(
|
||
CurvedAnimation(
|
||
parent: _animationController,
|
||
curve: Curves.easeInOut,
|
||
),
|
||
),
|
||
child: const CarePoetryPage(),
|
||
);
|
||
}
|
||
|
||
return FadeTransition(
|
||
opacity: Tween<double>(begin: 0, end: 1).animate(
|
||
CurvedAnimation(
|
||
parent: _animationController,
|
||
curve: Curves.easeInOut,
|
||
),
|
||
),
|
||
child: Scaffold(
|
||
backgroundColor: isDark ? const Color(0xFF121212) : Colors.grey[50],
|
||
body: SafeArea(child: _buildBody(controller, isDark)),
|
||
),
|
||
);
|
||
});
|
||
}
|
||
|
||
Widget _buildBody(HomeController controller, bool isDark) {
|
||
if (controller.loading.value) {
|
||
return LoadingWidget(isDark: isDark);
|
||
}
|
||
|
||
if (controller.errorMessage.value.isNotEmpty) {
|
||
return CustomErrorWidget(
|
||
errorMessage: controller.errorMessage.value,
|
||
onRetry: controller.loadPoetry,
|
||
isDark: isDark,
|
||
);
|
||
}
|
||
|
||
if (!PoetryDataUtils.isValidPoetryData(controller.poetryData.value)) {
|
||
return EmptyWidget(isDark: isDark);
|
||
}
|
||
|
||
return _buildContent(controller, isDark);
|
||
}
|
||
|
||
Widget _buildContent(HomeController controller, bool isDark) {
|
||
return FutureBuilder<bool>(
|
||
future: OfflineDataManager().isOnline(),
|
||
builder: (context, snapshot) {
|
||
final isOnline = snapshot.data ?? true;
|
||
|
||
return Stack(
|
||
children: [
|
||
RefreshIndicator(
|
||
onRefresh: controller.refreshPoetry,
|
||
child: SingleChildScrollView(
|
||
physics: const AlwaysScrollableScrollPhysics(),
|
||
// 添加底部内边距,让内容延伸到导航栏下方,实现玻璃效果
|
||
padding: EdgeInsets.only(
|
||
top: 48,
|
||
bottom: AppConfig.liquidGlassTotalHeight + 16,
|
||
),
|
||
child: Column(
|
||
children: [
|
||
Obx(
|
||
() => PoetryCard(
|
||
poetryData: controller.poetryData.value!,
|
||
keywordList: List.from(controller.keywordList),
|
||
onTap: controller.loadNextPoetry,
|
||
sectionLoadingStates: Map.from(
|
||
controller.sectionLoadingStates,
|
||
),
|
||
repaintKey: repaintKey,
|
||
isDark: isDark,
|
||
),
|
||
),
|
||
const SizedBox(height: 160),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
// 关怀按钮 - 左上角
|
||
Positioned(
|
||
top: 8,
|
||
left: 16,
|
||
child: CareButton(
|
||
onTap: _careController.toggleCareButtonVisibility,
|
||
isDark: isDark,
|
||
),
|
||
),
|
||
// 关怀模式开关
|
||
Obx(
|
||
() => _careController.isCareButtonVisible.value
|
||
? Positioned(
|
||
top: 60,
|
||
left: 16,
|
||
right: 16,
|
||
child: CareModeToggle(
|
||
isEnabled: _careController.isCareModeEnabled,
|
||
onToggle: _careController.toggleCareMode,
|
||
isDark: isDark,
|
||
),
|
||
)
|
||
: const SizedBox.shrink(),
|
||
),
|
||
// 收起/恢复按钮 - 右上角(与下方分享按钮对齐)
|
||
Positioned(
|
||
top: 8,
|
||
right: 16,
|
||
child: FloatingButtonsToggleButton(
|
||
manager: _floatingButtonsVisibilityManager,
|
||
isDark: isDark,
|
||
),
|
||
),
|
||
// 左侧按钮组 - 固定位置,紧贴底栏bar
|
||
ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.flashingNotifier,
|
||
builder: (context, isFlashing, child) {
|
||
return ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.visibleNotifier,
|
||
builder: (context, isVisible, child) {
|
||
if (!isVisible && !isFlashing) {
|
||
return const SizedBox.shrink();
|
||
}
|
||
return ValueListenableBuilder<bool>(
|
||
valueListenable: _secondaryButtonsManager.hiddenNotifier,
|
||
builder: (context, isHidden, child) {
|
||
if (isHidden) return const SizedBox.shrink();
|
||
return Positioned(
|
||
left: 16,
|
||
bottom:
|
||
AppConfig.liquidGlassHeight +
|
||
AppConfig.liquidGlassBottomMargin +
|
||
68,
|
||
child: FloatingPreviousButton(
|
||
onPrevious: controller.loadPreviousPoetry,
|
||
isDark: isDark,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
},
|
||
);
|
||
},
|
||
),
|
||
ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.flashingNotifier,
|
||
builder: (context, isFlashing, child) {
|
||
return ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.visibleNotifier,
|
||
builder: (context, isVisible, child) {
|
||
if (!isVisible && !isFlashing) {
|
||
return const SizedBox.shrink();
|
||
}
|
||
return Positioned(
|
||
left: 16,
|
||
bottom:
|
||
AppConfig.liquidGlassHeight +
|
||
AppConfig.liquidGlassBottomMargin +
|
||
8,
|
||
child: FloatingNextButton(
|
||
onNext: controller.loadNextPoetry,
|
||
isDark: isDark,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
},
|
||
),
|
||
// 右侧按钮组 - 固定位置,紧贴底栏bar
|
||
ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.flashingNotifier,
|
||
builder: (context, isFlashing, child) {
|
||
return ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.visibleNotifier,
|
||
builder: (context, isVisible, child) {
|
||
if (!isVisible && !isFlashing) {
|
||
return const SizedBox.shrink();
|
||
}
|
||
return ValueListenableBuilder<bool>(
|
||
valueListenable: _secondaryButtonsManager.hiddenNotifier,
|
||
builder: (context, isHidden, child) {
|
||
if (isHidden) return const SizedBox.shrink();
|
||
return Positioned(
|
||
right: 16,
|
||
bottom:
|
||
AppConfig.liquidGlassHeight +
|
||
AppConfig.liquidGlassBottomMargin +
|
||
68,
|
||
child: FloatingShareButton(
|
||
onShare: () async {
|
||
if (controller.poetryData.value != null) {
|
||
await ShareImageUtils.captureAndShare(
|
||
context,
|
||
repaintKey,
|
||
subject: controller.poetryData.value!.name,
|
||
);
|
||
}
|
||
},
|
||
isDark: isDark,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
},
|
||
);
|
||
},
|
||
),
|
||
if (isOnline)
|
||
ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.flashingNotifier,
|
||
builder: (context, isFlashing, child) {
|
||
return ValueListenableBuilder<bool>(
|
||
valueListenable:
|
||
_floatingButtonsVisibilityManager.visibleNotifier,
|
||
builder: (context, isVisible, child) {
|
||
if (!isVisible && !isFlashing) {
|
||
return const SizedBox.shrink();
|
||
}
|
||
return Positioned(
|
||
right: 16,
|
||
bottom:
|
||
AppConfig.liquidGlassHeight +
|
||
AppConfig.liquidGlassBottomMargin +
|
||
8,
|
||
child: Obx(
|
||
() => FloatingLikeButton(
|
||
isLiked: controller.isLiked.value,
|
||
isLoadingLike: controller.isLoadingLike.value,
|
||
onToggleLike: controller.toggleLike,
|
||
isDark: isDark,
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
},
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
}
|