logo和名称替换

This commit is contained in:
谢宇宁
2026-10-14 11:23:36 +07:00
parent 1cb514cd02
commit c5c04a7967
478 changed files with 8990 additions and 4948 deletions
+61 -31
View File
@@ -1,12 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgmj/assets_tool/app_colors.dart';
import 'package:hgmj/assets_tool/images.dart';
import 'package:hgmj/hj_utils/image_util.dart';
import 'package:hgmj/tools_base/cache/image_cache_manager.dart';
import 'package:hgmj/tools_base/loading/loading_alert_widget.dart';
import 'package:hgmj/tools_base/toast.dart';
import 'package:hgmj/tools_base/widget/net_image_widget.dart';
import 'package:wydj/assets_tool/app_colors.dart';
import 'package:wydj/assets_tool/images.dart';
import 'package:wydj/hj_utils/image_util.dart';
import 'package:wydj/tools_base/cache/image_cache_manager.dart';
import 'package:wydj/tools_base/loading/loading_alert_widget.dart';
import 'package:wydj/tools_base/toast.dart';
import 'package:wydj/tools_base/widget/net_image_widget.dart';
/// 微信风格图片浏览器
/// 横滑切图 + 双指/双击缩放 + 上下滑拖拽退出(图片跟手、背景随拖动渐隐露出下层页面) + 长按保存
@@ -20,18 +20,26 @@ class ImageBrowserPage extends StatefulWidget {
final int initialIndex;
final bool showSaveButton; // 右上角显式"保存到相册"按钮(AI 生成图等场景);默认只支持长按保存
const ImageBrowserPage({super.key, required this.images, this.initialIndex = 0, this.showSaveButton = false});
const ImageBrowserPage(
{super.key,
required this.images,
this.initialIndex = 0,
this.showSaveButton = false});
/// 打开浏览器(透明路由 + 淡入)
/// [showSaveButton] 显示右上角"保存到相册"按钮
static void open(List<String> images, {int index = 0, bool showSaveButton = false}) {
static void open(List<String> images,
{int index = 0, bool showSaveButton = false}) {
final valid = images.where((e) => e.isNotEmpty).toList();
if (valid.isEmpty) return;
// fullscreenDialog:true → GetX canTransitionTo 返回 false,下层页面不做外出转场,
// 保持完整渲染 → 拖拽偷看时看到的是整页而非"pop 一半"。
// 瞬时 fadeIn:打开无残影。仍是 Get.to → Get.back 能正常 pop,下滑退出正常
Get.to(
() => ImageBrowserPage(images: valid, initialIndex: index.clamp(0, valid.length - 1), showSaveButton: showSaveButton),
() => ImageBrowserPage(
images: valid,
initialIndex: index.clamp(0, valid.length - 1),
showSaveButton: showSaveButton),
opaque: false,
transition: Transition.fadeIn,
duration: Duration.zero,
@@ -43,7 +51,8 @@ class ImageBrowserPage extends StatefulWidget {
State<ImageBrowserPage> createState() => _ImageBrowserPageState();
}
class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProviderStateMixin {
class _ImageBrowserPageState extends State<ImageBrowserPage>
with TickerProviderStateMixin {
// ========== 分页 ==========
late final PageController _pageCtr;
late int _curIndex;
@@ -72,11 +81,17 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
super.initState();
_curIndex = widget.initialIndex;
_pageCtr = PageController(initialPage: _curIndex);
_transCtrs = List.generate(widget.images.length, (_) => TransformationController());
_resetCtr = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
_zoomCtr = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
_enterCtr = AnimationController(vsync: this, duration: const Duration(milliseconds: 220))..forward();
_exitCtr = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
_transCtrs =
List.generate(widget.images.length, (_) => TransformationController());
_resetCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 200));
_zoomCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 200));
_enterCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 220))
..forward();
_exitCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 200));
}
@override
@@ -97,10 +112,12 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
bool get _isZoomed => _transCtrs[_curIndex].value.getMaxScaleOnAxis() > 1.05;
// 背景不透明度:随竖直拖动距离渐隐(下拉偷看下层)
double get _bgOpacity => (1 - _dragY.abs() / (Get.height * 0.6)).clamp(0.0, 1.0);
double get _bgOpacity =>
(1 - _dragY.abs() / (Get.height * 0.6)).clamp(0.0, 1.0);
// 拖动时图片轻微缩小
double get _dragScale => (1 - _dragY.abs() / (Get.height * 2)).clamp(0.85, 1.0);
double get _dragScale =>
(1 - _dragY.abs() / (Get.height * 2)).clamp(0.85, 1.0);
// 退出淡出系数:1→0
double get _exitFactor => 1 - _exitCtr.value;
@@ -119,8 +136,8 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
_exit();
return;
}
final anim =
Tween<double>(begin: _dragY, end: 0).animate(CurvedAnimation(parent: _resetCtr, curve: Curves.easeOut));
final anim = Tween<double>(begin: _dragY, end: 0)
.animate(CurvedAnimation(parent: _resetCtr, curve: Curves.easeOut));
void listener() => setState(() => _dragY = anim.value);
anim.addListener(listener);
// whenComplete 在 dispose 取消 ticker 时也会触发,一律先判 mounted
@@ -156,8 +173,8 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
-pos.dx * (scale - 1), -pos.dy * (scale - 1), 0, 1, //
);
}
final anim =
Matrix4Tween(begin: ctr.value, end: target).animate(CurvedAnimation(parent: _zoomCtr, curve: Curves.easeOut));
final anim = Matrix4Tween(begin: ctr.value, end: target)
.animate(CurvedAnimation(parent: _zoomCtr, curve: Curves.easeOut));
void listener() => ctr.value = anim.value;
anim.addListener(listener);
_zoomCtr.forward(from: 0).whenComplete(() {
@@ -195,7 +212,10 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
builder: (_, __) => Stack(
children: [
// 黑色背景,随拖动/退出渐隐
Positioned.fill(child: ColoredBox(color: Colors.black.withValues(alpha: _bgOpacity * _exitFactor))),
Positioned.fill(
child: ColoredBox(
color: Colors.black
.withValues(alpha: _bgOpacity * _exitFactor))),
GestureDetector(
onLongPress: _saveCurrent, // 长按保存
onDoubleTapDown: (d) => _tapPos = d.localPosition,
@@ -209,7 +229,8 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
opacity: _enterCtr,
child: Transform.translate(
offset: Offset(0, _dragY),
child: Transform.scale(scale: _dragScale, child: _pageView()),
child:
Transform.scale(scale: _dragScale, child: _pageView()),
),
),
),
@@ -226,7 +247,9 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
if (_isSaving)
const Positioned.fill(
child: AbsorbPointer(
child: ColoredBox(color: Color(0x80000000), child: LoadingAlertWidget(title: '保存中...')),
child: ColoredBox(
color: Color(0x80000000),
child: LoadingAlertWidget(title: '保存中...')),
),
),
],
@@ -272,7 +295,8 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
right: 20,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 2),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20)),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(20)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
@@ -287,7 +311,8 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
: (isIncoming ? const Offset(0, -1) : const Offset(0, 1));
return ClipRect(
child: SlideTransition(
position: Tween<Offset>(begin: begin, end: Offset.zero).animate(anim),
position: Tween<Offset>(begin: begin, end: Offset.zero)
.animate(anim),
child: child,
),
);
@@ -295,10 +320,12 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
child: Text(
'${_curIndex + 1}',
key: ValueKey(_curIndex),
style: const TextStyle(color: AppColors.actionRed, fontSize: 12),
style:
const TextStyle(color: AppColors.actionRed, fontSize: 12),
),
),
Text('/${widget.images.length}', style: const TextStyle(color: Color(0xff3D3D3D), fontSize: 12)),
Text('/${widget.images.length}',
style: const TextStyle(color: Color(0xff3D3D3D), fontSize: 12)),
],
),
),
@@ -331,8 +358,11 @@ class _ImageBrowserPageState extends State<ImageBrowserPage> with TickerProvider
onTap: _saveCurrent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4), color: const Color(0xFFE57310)),
child: const Text('保存到相册', style: TextStyle(color: Colors.white, fontSize: 14)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: const Color(0xFFE57310)),
child: const Text('保存到相册',
style: TextStyle(color: Colors.white, fontSize: 14)),
),
),
),