feat:更新组件UI

This commit is contained in:
2025-11-19 19:53:05 +08:00
parent 8acad9c63a
commit 07180965cc
13 changed files with 360 additions and 237 deletions

View File

@@ -0,0 +1,65 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
Widget buildEasyRefresh({
required EasyRefreshController freshController,
required Future<void> Function()? onRefresh,
required Future<void> Function()? onLoad,
required Widget body,
}) {
return EasyRefresh(
controller: freshController,
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '准备刷新',
processingText: '刷新中...',
processedText: '刷新完成',
failedText: '刷新失败',
noMoreText: '没有更多数据',
showText: true,
messageText: '更新于 %T',
showMessage: true,
),
footer: ClassicFooter(
dragText: '上拉加载',
armedText: '释放加载',
readyText: '准备加载',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败',
noMoreText: '没有更多数据',
showText: true,
messageText: '更新于 %T',
showMessage: true,
),
onRefresh: onRefresh,
onLoad: onLoad,
child: body,
);
}
Widget buildScrollToTop({
required BuildContext context,
required VoidCallback scrollToTop,
}) {
return Positioned(
right: 0,
bottom: 20,
child: FloatingActionButton(
onPressed: scrollToTop,
backgroundColor: Theme.of(context).colorScheme.primary,
elevation: 2,
mini: true,
child: const Icon(Icons.arrow_upward, color: Colors.white),
),
);
}
void scrollToTopAnimateTo(ScrollController controller) {
controller.animateTo(
0,
duration: const Duration(milliseconds: 500), // 滚动动画时长
curve: Curves.easeInOut, // 滚动动画曲线
);
}

View File

@@ -75,7 +75,7 @@ class _ImagePreviewPageState extends State<ImagePreviewPage> {
pageOptions:
widget.images.map((url) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(url),
imageProvider: NetworkImage('${AppConfig.imageBaseUrl}$url'),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
// 点击空白处关闭预览
@@ -91,42 +91,27 @@ class _ImagePreviewPageState extends State<ImagePreviewPage> {
}
}
Widget buildFileImage(BuildContext context, File imageFile) {
return GestureDetector(
onTap: () => showFullScreenImage(context, FileImage(imageFile)),
child: Image(
image: FileImage(imageFile),
width: 120,
height: 120,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return buildImageLoadingIndicator(loadingProgress);
},
),
);
}
Widget buildNetworkImage(BuildContext context, String url) {
Widget buildNetworkImage(
BuildContext context,
List<String> imageUrls,
int index,
) {
return AspectRatio(
aspectRatio: 1.5,
aspectRatio: 4 / 3,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: GestureDetector(
onTap: () {
showFullScreenImage(
context,
NetworkImage('${AppConfig.imageBaseUrl}$url'),
);
showFullScreenImage(context, imageUrls, index);
},
child: Image.network(
'${AppConfig.imageBaseUrl}$url',
'${AppConfig.imageBaseUrl}${imageUrls[index]}',
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return buildImageLoadingIndicator(loadingProgress);
},
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
errorBuilder: (context, error, stackTrace) => buildErrorImage(),
),
),
),
@@ -135,7 +120,8 @@ Widget buildNetworkImage(BuildContext context, String url) {
Widget buildImagePreviewItem({
required BuildContext context,
required String imageUrl,
required List<String> imageUrls,
required int index,
required VoidCallback onRemoveImage,
}) {
return Container(
@@ -144,7 +130,7 @@ Widget buildImagePreviewItem({
borderRadius: BorderRadius.circular(8),
child: Stack(
children: [
buildNetworkImage(context, imageUrl),
buildNetworkImage(context, imageUrls, index),
buildDeleteImage(onRemoveImage: onRemoveImage),
],
),
@@ -168,7 +154,7 @@ Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) {
);
}
Widget _buildErrorImage() {
Widget buildErrorImage() {
return Container(
color: Colors.grey[200],
child: const Icon(Icons.image, color: Colors.grey, size: 30),
@@ -183,7 +169,7 @@ Widget _buildPhotoView(ImageProvider imageProvider) {
maxScale: PhotoViewComputedScale.covered * 2,
initialScale: PhotoViewComputedScale.contained,
loadingBuilder: (context, event) => buildImageLoadingIndicator(event),
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
errorBuilder: (context, error, stackTrace) => buildErrorImage(),
);
}
@@ -198,29 +184,40 @@ Widget _buildCloseImage(BuildContext context) {
);
}
void showFullScreenImage(BuildContext context, ImageProvider imageProvider) {
Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return Scaffold(
backgroundColor: Colors.black.withAlpha(200),
body: Stack(
children: [
// 可缩放图片
Positioned.fill(child: _buildPhotoView(imageProvider)),
// 关闭按钮
_buildCloseImage(context),
],
),
);
},
void showFullScreenImage(
BuildContext context,
List<String> imageUrls,
int index,
) {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => ImagePreviewPage(images: imageUrls, initialIndex: index),
),
);
// Navigator.of(context).push(
// PageRouteBuilder(
// opaque: false,
// pageBuilder: (
// BuildContext context,
// Animation<double> animation,
// Animation<double> secondaryAnimation,
// ) {
// return Scaffold(
// backgroundColor: Colors.black.withAlpha(200),
// body: Stack(
// children: [
// // 可缩放图片
// Positioned.fill(child: _buildPhotoView(imageProvider)),
// // 关闭按钮
// _buildCloseImage(context),
// ],
// ),
// );
// },
// ),
// );
}
Widget buildImageUploadButton({required VoidCallback onPickImage}) {

View File

@@ -29,20 +29,18 @@ InputDecoration formInputDecoration({
}
Widget buildCard({required BuildContext context, required Widget child}) {
final colors = Theme
.of(context)
.colorScheme;
final colors = Theme.of(context).colorScheme;
return Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: colors.surface,
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withAlpha(50), width: 1),
border: Border.all(color: colors.outline.withAlpha(50)),
),
child: Padding(padding: EdgeInsets.all(8), child: child),
child: Padding(padding: EdgeInsets.all(10 ), child: child),
),
);
}
@@ -66,11 +64,7 @@ Widget buildToggleSwitch<T extends Enum>({
initialLabelIndex: tabValues.indexOf(currentTab),
totalSwitches: tabValues.length,
labels: labels,
activeBgColor: [Theme
.of(context)
.colorScheme
.primary
],
activeBgColor: [Theme.of(context).colorScheme.primary],
activeFgColor: Colors.white,
inactiveBgColor: Colors.grey.shade200,
inactiveFgColor: Colors.grey.shade700,
@@ -99,10 +93,7 @@ Widget circleIconButton({
fixedSize: Size(size, size),
shape: CircleBorder(),
elevation: 0,
backgroundColor: Theme
.of(context)
.colorScheme
.primary,
backgroundColor: Theme.of(context).colorScheme.primary,
padding: EdgeInsets.zero,
minimumSize: const Size(0, 0),
),
@@ -111,9 +102,7 @@ Widget circleIconButton({
}
Widget buildTag(BuildContext context, String title) {
final colors = Theme
.of(context)
.colorScheme;
final colors = Theme.of(context).colorScheme;
return Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),
@@ -195,11 +184,7 @@ Widget buildLoadingIndicator(BuildContext context) {
color: colors.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 8,
offset: Offset(0, 2),
),
BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 2)),
],
),
child: Column(