mod: 图片质量
This commit is contained in:
@ -43,7 +43,7 @@ class LiveCard extends StatelessWidget {
|
||||
Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: liveItem.cover + '@.webp',
|
||||
src: liveItem.cover,
|
||||
type: 'emote',
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
|
@ -11,6 +11,7 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
final String? type;
|
||||
final Duration? fadeOutDuration;
|
||||
final Duration? fadeInDuration;
|
||||
final int? quality;
|
||||
|
||||
const NetworkImgLayer({
|
||||
Key? key,
|
||||
@ -22,6 +23,8 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
this.type,
|
||||
this.fadeOutDuration,
|
||||
this.fadeInDuration,
|
||||
// 图片质量 默认1%
|
||||
this.quality = 1,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -37,7 +40,8 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
? 0
|
||||
: StyleString.imgRadius.x),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: src!.startsWith('//') ? 'https:${src!}' : src!,
|
||||
imageUrl:
|
||||
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality}q.webp',
|
||||
width: width ?? double.infinity,
|
||||
height: height ?? double.infinity,
|
||||
alignment: Alignment.center,
|
||||
|
@ -76,7 +76,7 @@ class VideoCardH extends StatelessWidget {
|
||||
Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: videoItem.pic + '@.webp',
|
||||
src: videoItem.pic,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
|
@ -69,7 +69,7 @@ class VideoCardV extends StatelessWidget {
|
||||
Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: videoItem.pic + '@.webp',
|
||||
src: videoItem.pic,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
|
@ -42,7 +42,7 @@ class FavItem extends StatelessWidget {
|
||||
return Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: favFolderItem.cover + '@.webp',
|
||||
src: favFolderItem.cover,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
|
@ -74,7 +74,7 @@ class FavVideoCardH extends StatelessWidget {
|
||||
Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: videoItem.pic + '@.webp',
|
||||
src: videoItem.pic,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
|
@ -82,9 +82,8 @@ class HistoryItem extends StatelessWidget {
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: (videoItem.cover != ''
|
||||
? videoItem.cover
|
||||
: videoItem.covers.first) +
|
||||
'@.webp',
|
||||
? videoItem.cover
|
||||
: videoItem.covers.first),
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
|
@ -65,7 +65,7 @@ class LiveCardV extends StatelessWidget {
|
||||
Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: '${liveItem.cover!}@.webp',
|
||||
src: liveItem.cover!,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
|
@ -1,6 +1,7 @@
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
|
||||
import 'package:dismissible_page/dismissible_page.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
@ -44,179 +45,189 @@ class _ImagePreviewState extends State<ImagePreview>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
DismissiblePage(
|
||||
backgroundColor: Colors.transparent,
|
||||
onDismissed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
// Note that scrollable widget inside DismissiblePage might limit the functionality
|
||||
// If scroll direction matches DismissiblePage direction
|
||||
direction: DismissiblePageDismissDirection.down,
|
||||
disabled: _dismissDisabled,
|
||||
isFullScreen: true,
|
||||
child: Hero(
|
||||
tag: _previewController
|
||||
.imgList[_previewController.initialPage.value],
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_previewController.visiable = !_previewController.visiable;
|
||||
setState(() {});
|
||||
},
|
||||
child: ExtendedImageGesturePageView.builder(
|
||||
controller: ExtendedPageController(
|
||||
initialPage: _previewController.initialPage.value,
|
||||
pageSpacing: 0,
|
||||
),
|
||||
onPageChanged: (int index) {
|
||||
_previewController.initialPage.value = index;
|
||||
_previewController.currentPage.value = index + 1;
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: AppBar(
|
||||
primary: false,
|
||||
toolbarHeight: 0,
|
||||
backgroundColor: Colors.black,
|
||||
systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
DismissiblePage(
|
||||
backgroundColor: Colors.transparent,
|
||||
onDismissed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
// Note that scrollable widget inside DismissiblePage might limit the functionality
|
||||
// If scroll direction matches DismissiblePage direction
|
||||
direction: DismissiblePageDismissDirection.down,
|
||||
disabled: _dismissDisabled,
|
||||
isFullScreen: true,
|
||||
child: Hero(
|
||||
tag: _previewController
|
||||
.imgList[_previewController.initialPage.value],
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_previewController.visiable = !_previewController.visiable;
|
||||
setState(() {});
|
||||
},
|
||||
canScrollPage: (GestureDetails? gestureDetails) =>
|
||||
gestureDetails!.totalScale! <= 1.0,
|
||||
preloadPagesCount: 2,
|
||||
itemCount: _previewController.imgList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return ExtendedImage.network(
|
||||
_previewController.imgList[index],
|
||||
fit: BoxFit.contain,
|
||||
mode: ExtendedImageMode.gesture,
|
||||
onDoubleTap: (ExtendedImageGestureState state) {
|
||||
final Offset? pointerDownPosition =
|
||||
state.pointerDownPosition;
|
||||
final double? begin = state.gestureDetails!.totalScale;
|
||||
double end;
|
||||
|
||||
//remove old
|
||||
_doubleClickAnimation
|
||||
?.removeListener(_doubleClickAnimationListener);
|
||||
|
||||
//stop pre
|
||||
_doubleClickAnimationController.stop();
|
||||
|
||||
//reset to use
|
||||
_doubleClickAnimationController.reset();
|
||||
|
||||
if (begin == doubleTapScales[0]) {
|
||||
setState(() {
|
||||
_dismissDisabled = true;
|
||||
});
|
||||
end = doubleTapScales[1];
|
||||
} else {
|
||||
setState(() {
|
||||
_dismissDisabled = false;
|
||||
});
|
||||
end = doubleTapScales[0];
|
||||
}
|
||||
|
||||
_doubleClickAnimationListener = () {
|
||||
state.handleDoubleTap(
|
||||
scale: _doubleClickAnimation!.value,
|
||||
doubleTapPosition: pointerDownPosition);
|
||||
};
|
||||
_doubleClickAnimation = _doubleClickAnimationController
|
||||
.drive(Tween<double>(begin: begin, end: end));
|
||||
|
||||
_doubleClickAnimation!
|
||||
.addListener(_doubleClickAnimationListener);
|
||||
|
||||
_doubleClickAnimationController.forward();
|
||||
},
|
||||
loadStateChanged: (ExtendedImageState state) {
|
||||
if (state.extendedImageLoadState == LoadState.loading) {
|
||||
final ImageChunkEvent? loadingProgress =
|
||||
state.loadingProgress;
|
||||
final double? progress =
|
||||
loadingProgress?.expectedTotalBytes != null
|
||||
? loadingProgress!.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null;
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: 150.0,
|
||||
child: LinearProgressIndicator(value: progress),
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
Text('${((progress ?? 0.0) * 100).toInt()}%'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
initGestureConfigHandler: (ExtendedImageState state) {
|
||||
return GestureConfig(
|
||||
inPageView: true,
|
||||
initialScale: 1.0,
|
||||
maxScale: 5.0,
|
||||
animationMaxScale: 6.0,
|
||||
initialAlignment: InitialAlignment.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
// height: 45,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom, top: 20),
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[
|
||||
Colors.transparent,
|
||||
Colors.black87,
|
||||
],
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 20, right: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Obx(
|
||||
() => Text.rich(
|
||||
TextSpan(
|
||||
style: const TextStyle(
|
||||
color: Colors.white, fontSize: 18),
|
||||
children: [
|
||||
TextSpan(
|
||||
text:
|
||||
_previewController.currentPage.toString()),
|
||||
const TextSpan(text: ' / '),
|
||||
TextSpan(
|
||||
text: _previewController.imgList.length
|
||||
.toString()),
|
||||
]),
|
||||
),
|
||||
child: ExtendedImageGesturePageView.builder(
|
||||
controller: ExtendedPageController(
|
||||
initialPage: _previewController.initialPage.value,
|
||||
pageSpacing: 0,
|
||||
),
|
||||
const Spacer(),
|
||||
ElevatedButton(
|
||||
onPressed: () => _previewController.onShareImg(),
|
||||
child: const Text('分享')),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () => _previewController.onSaveImg(),
|
||||
child: const Text('保存'))
|
||||
],
|
||||
onPageChanged: (int index) {
|
||||
_previewController.initialPage.value = index;
|
||||
_previewController.currentPage.value = index + 1;
|
||||
},
|
||||
canScrollPage: (GestureDetails? gestureDetails) =>
|
||||
gestureDetails!.totalScale! <= 1.0,
|
||||
preloadPagesCount: 2,
|
||||
itemCount: _previewController.imgList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return ExtendedImage.network(
|
||||
_previewController.imgList[index],
|
||||
fit: BoxFit.contain,
|
||||
mode: ExtendedImageMode.gesture,
|
||||
onDoubleTap: (ExtendedImageGestureState state) {
|
||||
final Offset? pointerDownPosition =
|
||||
state.pointerDownPosition;
|
||||
final double? begin = state.gestureDetails!.totalScale;
|
||||
double end;
|
||||
|
||||
//remove old
|
||||
_doubleClickAnimation
|
||||
?.removeListener(_doubleClickAnimationListener);
|
||||
|
||||
//stop pre
|
||||
_doubleClickAnimationController.stop();
|
||||
|
||||
//reset to use
|
||||
_doubleClickAnimationController.reset();
|
||||
|
||||
if (begin == doubleTapScales[0]) {
|
||||
setState(() {
|
||||
_dismissDisabled = true;
|
||||
});
|
||||
end = doubleTapScales[1];
|
||||
} else {
|
||||
setState(() {
|
||||
_dismissDisabled = false;
|
||||
});
|
||||
end = doubleTapScales[0];
|
||||
}
|
||||
|
||||
_doubleClickAnimationListener = () {
|
||||
state.handleDoubleTap(
|
||||
scale: _doubleClickAnimation!.value,
|
||||
doubleTapPosition: pointerDownPosition);
|
||||
};
|
||||
_doubleClickAnimation = _doubleClickAnimationController
|
||||
.drive(Tween<double>(begin: begin, end: end));
|
||||
|
||||
_doubleClickAnimation!
|
||||
.addListener(_doubleClickAnimationListener);
|
||||
|
||||
_doubleClickAnimationController.forward();
|
||||
},
|
||||
loadStateChanged: (ExtendedImageState state) {
|
||||
if (state.extendedImageLoadState == LoadState.loading) {
|
||||
final ImageChunkEvent? loadingProgress =
|
||||
state.loadingProgress;
|
||||
final double? progress =
|
||||
loadingProgress?.expectedTotalBytes != null
|
||||
? loadingProgress!.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null;
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: 150.0,
|
||||
child:
|
||||
LinearProgressIndicator(value: progress),
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
Text('${((progress ?? 0.0) * 100).toInt()}%'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
initGestureConfigHandler: (ExtendedImageState state) {
|
||||
return GestureConfig(
|
||||
inPageView: true,
|
||||
initialScale: 1.0,
|
||||
maxScale: 5.0,
|
||||
animationMaxScale: 6.0,
|
||||
initialAlignment: InitialAlignment.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
// height: 45,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom, top: 20),
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[
|
||||
Colors.transparent,
|
||||
Colors.black87,
|
||||
],
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 20, right: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Obx(
|
||||
() => Text.rich(
|
||||
TextSpan(
|
||||
style: const TextStyle(
|
||||
color: Colors.white, fontSize: 18),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: _previewController.currentPage
|
||||
.toString()),
|
||||
const TextSpan(text: ' / '),
|
||||
TextSpan(
|
||||
text: _previewController.imgList.length
|
||||
.toString()),
|
||||
]),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
ElevatedButton(
|
||||
onPressed: () => _previewController.onShareImg(),
|
||||
child: const Text('分享')),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () => _previewController.onSaveImg(),
|
||||
child: const Text('保存'))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
||||
Hero(
|
||||
tag: Utils.makeHeroTag(i.roomid),
|
||||
child: NetworkImgLayer(
|
||||
src: i.cover + '@.webp',
|
||||
src: i.cover,
|
||||
type: 'emote',
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
|
Reference in New Issue
Block a user