mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 16:48:16 +08:00
21
lib/common/widgets/colored_box_transition.dart
Normal file
21
lib/common/widgets/colored_box_transition.dart
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ColoredBoxTransition extends AnimatedWidget {
|
||||||
|
const ColoredBoxTransition({
|
||||||
|
super.key,
|
||||||
|
required this.color,
|
||||||
|
this.child,
|
||||||
|
}) : super(listenable: color);
|
||||||
|
|
||||||
|
final Animation<Color?> color;
|
||||||
|
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ColoredBox(
|
||||||
|
color: color.value!,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import 'dart:io' show File, Platform;
|
import 'dart:io' show File, Platform;
|
||||||
|
|
||||||
|
import 'package:PiliPlus/common/widgets/colored_box_transition.dart';
|
||||||
import 'package:PiliPlus/common/widgets/flutter/page/page_view.dart';
|
import 'package:PiliPlus/common/widgets/flutter/page/page_view.dart';
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_tap_gesture_recognizer.dart';
|
||||||
@@ -52,7 +53,7 @@ class GalleryViewer extends StatefulWidget {
|
|||||||
this.maxScale = 8.0,
|
this.maxScale = 8.0,
|
||||||
required this.quality,
|
required this.quality,
|
||||||
required this.sources,
|
required this.sources,
|
||||||
this.initIndex = 1,
|
this.initIndex = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
final double minScale;
|
final double minScale;
|
||||||
@@ -87,14 +88,12 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
late final LongPressGestureRecognizer _longPressGestureRecognizer;
|
late final LongPressGestureRecognizer _longPressGestureRecognizer;
|
||||||
|
|
||||||
late final AnimationController _animateController;
|
late final AnimationController _animateController;
|
||||||
late final Animation<Decoration> _opacityAnimation;
|
late final Animation<Color?> _opacityAnimation;
|
||||||
double dx = 0, dy = 0;
|
double dx = 0, dy = 0;
|
||||||
|
|
||||||
Offset _offset = Offset.zero;
|
Offset _offset = Offset.zero;
|
||||||
bool _dragging = false;
|
bool _dragging = false;
|
||||||
|
|
||||||
bool get _isActive => _dragging || _animateController.isAnimating;
|
|
||||||
|
|
||||||
String _getActualUrl(String url) {
|
String _getActualUrl(String url) {
|
||||||
return _quality != 100
|
return _quality != 100
|
||||||
? ImageUtils.thumbnailUrl(url, _quality)
|
? ImageUtils.thumbnailUrl(url, _quality)
|
||||||
@@ -138,14 +137,16 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
});
|
});
|
||||||
|
|
||||||
_animateController = AnimationController(
|
_animateController = AnimationController(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(
|
||||||
|
milliseconds: 750,
|
||||||
|
), // reverse only if value <= 0.2
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
|
|
||||||
_opacityAnimation = _animateController.drive(
|
_opacityAnimation = _animateController.drive(
|
||||||
DecorationTween(
|
ColorTween(
|
||||||
begin: const BoxDecoration(color: Colors.black),
|
begin: Colors.black,
|
||||||
end: const BoxDecoration(color: Colors.transparent),
|
end: Colors.transparent,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -189,7 +190,7 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onDragUpdate(ScaleUpdateDetails details) {
|
void _onDragUpdate(ScaleUpdateDetails details) {
|
||||||
if (!_isActive || _animateController.isAnimating) {
|
if (!_dragging || _animateController.isAnimating) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,16 +203,12 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onDragEnd(ScaleEndDetails details) {
|
void _onDragEnd(ScaleEndDetails details) {
|
||||||
if (!_isActive || _animateController.isAnimating) {
|
if (!_dragging || _animateController.isAnimating) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dragging = false;
|
_dragging = false;
|
||||||
|
|
||||||
if (_animateController.isCompleted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_animateController.isDismissed) {
|
if (!_animateController.isDismissed) {
|
||||||
if (_animateController.value > 0.2) {
|
if (_animateController.value > 0.2) {
|
||||||
Get.back();
|
Get.back();
|
||||||
@@ -256,13 +253,12 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
return Listener(
|
return Listener(
|
||||||
behavior: .opaque,
|
behavior: .opaque,
|
||||||
onPointerDown: _onPointerDown,
|
onPointerDown: _onPointerDown,
|
||||||
child: DecoratedBoxTransition(
|
|
||||||
decoration: _opacityAnimation,
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: .expand,
|
fit: .expand,
|
||||||
alignment: .center,
|
alignment: .center,
|
||||||
clipBehavior: .none,
|
clipBehavior: .none,
|
||||||
children: [
|
children: [
|
||||||
|
ColoredBoxTransition(color: _opacityAnimation),
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
_containerSize = constraints.biggest;
|
_containerSize = constraints.biggest;
|
||||||
@@ -287,7 +283,6 @@ class _GalleryViewerState extends State<GalleryViewer>
|
|||||||
_buildIndicator,
|
_buildIndicator,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,10 +77,26 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
_GestureType? _gestureType;
|
_GestureType? _gestureType;
|
||||||
|
|
||||||
|
final Matrix4 _matrix = Matrix4.identity();
|
||||||
|
|
||||||
|
late double __scale;
|
||||||
|
double get _scale => __scale;
|
||||||
|
set _scale(double value) {
|
||||||
|
__scale = value;
|
||||||
|
_matrix[0] = _matrix[5] = _matrix[10] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
late Offset __position;
|
||||||
|
Offset get _position => __position;
|
||||||
|
set _position(Offset value) {
|
||||||
|
__position = value;
|
||||||
|
_matrix
|
||||||
|
..[12] = value.dx
|
||||||
|
..[13] = value.dy;
|
||||||
|
}
|
||||||
|
|
||||||
Offset? _scalePos;
|
Offset? _scalePos;
|
||||||
late double _scale;
|
|
||||||
double? _scaleStart;
|
double? _scaleStart;
|
||||||
late Offset _position;
|
|
||||||
Offset? _referenceFocalPoint;
|
Offset? _referenceFocalPoint;
|
||||||
|
|
||||||
late Size _imageSize;
|
late Size _imageSize;
|
||||||
@@ -97,10 +113,6 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
late double _scaleFrom, _scaleTo;
|
late double _scaleFrom, _scaleTo;
|
||||||
late Offset _positionFrom, _positionTo;
|
late Offset _positionFrom, _positionTo;
|
||||||
|
|
||||||
Matrix4 get _matrix =>
|
|
||||||
Matrix4.translationValues(_position.dx, _position.dy, 0.0)
|
|
||||||
..scaleByDouble(_scale, _scale, _scale, 1.0);
|
|
||||||
|
|
||||||
void _listener() {
|
void _listener() {
|
||||||
final t = Curves.easeOut.transform(_animationController.value);
|
final t = Curves.easeOut.transform(_animationController.value);
|
||||||
_scale = t.lerp(_scaleFrom, _scaleTo);
|
_scale = t.lerp(_scaleFrom, _scaleTo);
|
||||||
@@ -396,7 +408,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
onPointerDown: _onPointerDown,
|
onPointerDown: _onPointerDown,
|
||||||
onPointerPanZoomStart: _onPointerPanZoomStart,
|
onPointerPanZoomStart: _onPointerPanZoomStart,
|
||||||
onPointerSignal: _onPointerSignal,
|
onPointerSignal: _onPointerSignal,
|
||||||
child: ClipRRect(
|
child: ClipRect(
|
||||||
child: Transform(
|
child: Transform(
|
||||||
transform: _matrix,
|
transform: _matrix,
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import 'package:PiliPlus/pages/home/controller.dart';
|
|||||||
import 'package:PiliPlus/pages/main/controller.dart';
|
import 'package:PiliPlus/pages/main/controller.dart';
|
||||||
import 'package:PiliPlus/pages/setting/models/model.dart';
|
import 'package:PiliPlus/pages/setting/models/model.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
|
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart';
|
import 'package:PiliPlus/pages/setting/widgets/slider_dialog.dart';
|
||||||
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
|
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||||
import 'package:PiliPlus/services/download/download_service.dart';
|
import 'package:PiliPlus/services/download/download_service.dart';
|
||||||
@@ -952,7 +952,7 @@ Future<void> _showRefreshDragDialog(
|
|||||||
) async {
|
) async {
|
||||||
final res = await showDialog<double>(
|
final res = await showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
title: '刷新滑动距离',
|
title: '刷新滑动距离',
|
||||||
min: 0.1,
|
min: 0.1,
|
||||||
max: 0.5,
|
max: 0.5,
|
||||||
@@ -975,7 +975,7 @@ Future<void> _showRefreshDialog(
|
|||||||
) async {
|
) async {
|
||||||
final res = await showDialog<double>(
|
final res = await showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
title: '刷新指示器高度',
|
title: '刷新指示器高度',
|
||||||
min: 10.0,
|
min: 10.0,
|
||||||
max: 100.0,
|
max: 100.0,
|
||||||
@@ -1063,7 +1063,7 @@ Future<void> _showReplyCountDialog(
|
|||||||
) async {
|
) async {
|
||||||
final res = await showDialog<double>(
|
final res = await showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
title: '连接重试次数',
|
title: '连接重试次数',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 8,
|
max: 8,
|
||||||
@@ -1085,7 +1085,7 @@ Future<void> _showReplyDelayDialog(
|
|||||||
) async {
|
) async {
|
||||||
final res = await showDialog<double>(
|
final res = await showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
title: '连接重试间隔',
|
title: '连接重试间隔',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1000,
|
max: 1000,
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ import 'package:PiliPlus/pages/main/controller.dart';
|
|||||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||||
import 'package:PiliPlus/pages/setting/models/model.dart';
|
import 'package:PiliPlus/pages/setting/models/model.dart';
|
||||||
import 'package:PiliPlus/pages/setting/slide_color_picker.dart';
|
import 'package:PiliPlus/pages/setting/slide_color_picker.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/dual_slide_dialog.dart';
|
import 'package:PiliPlus/pages/setting/widgets/dual_slider_dialog.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart';
|
import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
|
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart';
|
import 'package:PiliPlus/pages/setting/widgets/slider_dialog.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
|
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
|
||||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||||
@@ -373,7 +373,7 @@ void _showQualityDialog({
|
|||||||
}) {
|
}) {
|
||||||
showDialog<double>(
|
showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
value: initValue.toDouble(),
|
value: initValue.toDouble(),
|
||||||
title: title,
|
title: title,
|
||||||
min: 10,
|
min: 10,
|
||||||
@@ -622,7 +622,7 @@ void _showSpringDialog(BuildContext context, _) {
|
|||||||
Future<void> _showFontWeightDialog(BuildContext context) async {
|
Future<void> _showFontWeightDialog(BuildContext context) async {
|
||||||
final res = await showDialog<double>(
|
final res = await showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
title: 'App字体字重',
|
title: 'App字体字重',
|
||||||
value: Pref.appFontWeight.toDouble() + 1,
|
value: Pref.appFontWeight.toDouble() + 1,
|
||||||
min: 1,
|
min: 1,
|
||||||
@@ -661,7 +661,7 @@ Future<void> _showCardWidthDialog(
|
|||||||
) async {
|
) async {
|
||||||
final res = await showDialog<(double, double)>(
|
final res = await showDialog<(double, double)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DualSlideDialog(
|
builder: (context) => DualSliderDialog(
|
||||||
title: '列表最大列宽度(默认240dp)',
|
title: '列表最大列宽度(默认240dp)',
|
||||||
value1: Pref.recommendCardWidth,
|
value1: Pref.recommendCardWidth,
|
||||||
value2: Pref.smallCardWidth,
|
value2: Pref.smallCardWidth,
|
||||||
@@ -879,7 +879,7 @@ Future<void> _showToastDialog(
|
|||||||
) async {
|
) async {
|
||||||
final res = await showDialog<double>(
|
final res = await showDialog<double>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SlideDialog(
|
builder: (context) => SliderDialog(
|
||||||
title: 'Toast不透明度',
|
title: 'Toast不透明度',
|
||||||
value: CustomToast.toastOpacity,
|
value: CustomToast.toastOpacity,
|
||||||
min: 0.0,
|
min: 0.0,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class DualSlideDialog extends StatefulWidget {
|
class DualSliderDialog extends StatefulWidget {
|
||||||
final double value1;
|
final double value1;
|
||||||
final double value2;
|
final double value2;
|
||||||
final String title;
|
final String title;
|
||||||
@@ -13,7 +13,7 @@ class DualSlideDialog extends StatefulWidget {
|
|||||||
final String suffix;
|
final String suffix;
|
||||||
final int precise;
|
final int precise;
|
||||||
|
|
||||||
const DualSlideDialog({
|
const DualSliderDialog({
|
||||||
super.key,
|
super.key,
|
||||||
required this.value1,
|
required this.value1,
|
||||||
required this.value2,
|
required this.value2,
|
||||||
@@ -28,10 +28,10 @@ class DualSlideDialog extends StatefulWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DualSlideDialog> createState() => _DualSlideDialogState();
|
State<DualSliderDialog> createState() => _DualSliderDialogState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DualSlideDialogState extends State<DualSlideDialog> {
|
class _DualSliderDialogState extends State<DualSliderDialog> {
|
||||||
late double _tempValue1;
|
late double _tempValue1;
|
||||||
late double _tempValue2;
|
late double _tempValue2;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SlideDialog extends StatefulWidget {
|
class SliderDialog extends StatefulWidget {
|
||||||
final double value;
|
final double value;
|
||||||
final String title;
|
final String title;
|
||||||
final double min;
|
final double min;
|
||||||
@@ -10,7 +10,7 @@ class SlideDialog extends StatefulWidget {
|
|||||||
final String suffix;
|
final String suffix;
|
||||||
final int precise;
|
final int precise;
|
||||||
|
|
||||||
const SlideDialog({
|
const SliderDialog({
|
||||||
super.key,
|
super.key,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.title,
|
required this.title,
|
||||||
@@ -22,10 +22,10 @@ class SlideDialog extends StatefulWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SlideDialog> createState() => _SlideDialogState();
|
State<SliderDialog> createState() => _SliderDialogState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SlideDialogState extends State<SlideDialog> {
|
class _SliderDialogState extends State<SliderDialog> {
|
||||||
late double _tempValue;
|
late double _tempValue;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/skeleton/video_reply.dart';
|
import 'package:PiliPlus/common/skeleton/video_reply.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/colored_box_transition.dart';
|
||||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||||
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
@@ -327,8 +328,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
|
|||||||
}
|
}
|
||||||
final child = _replyItem(context, response[index], index);
|
final child = _replyItem(context, response[index], index);
|
||||||
if (jumpIndex == index) {
|
if (jumpIndex == index) {
|
||||||
return AnimatedBuilder(
|
return ColoredBoxTransition(
|
||||||
animation: _colorAnimation ??= _controller.animController.drive(
|
color: _colorAnimation ??= _controller.animController.drive(
|
||||||
ColorTween(
|
ColorTween(
|
||||||
begin: theme.colorScheme.onInverseSurface,
|
begin: theme.colorScheme.onInverseSurface,
|
||||||
end: theme.colorScheme.surface,
|
end: theme.colorScheme.surface,
|
||||||
@@ -339,12 +340,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
builder: (context, child) {
|
|
||||||
return ColoredBox(
|
|
||||||
color: _colorAnimation!.value!,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
|
|||||||
Reference in New Issue
Block a user