mod: 图片质量
This commit is contained in:
@ -43,7 +43,7 @@ class LiveCard extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: liveItem.cover + '@.webp',
|
src: liveItem.cover,
|
||||||
type: 'emote',
|
type: 'emote',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
|
|||||||
@ -11,6 +11,7 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
final String? type;
|
final String? type;
|
||||||
final Duration? fadeOutDuration;
|
final Duration? fadeOutDuration;
|
||||||
final Duration? fadeInDuration;
|
final Duration? fadeInDuration;
|
||||||
|
final int? quality;
|
||||||
|
|
||||||
const NetworkImgLayer({
|
const NetworkImgLayer({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -22,6 +23,8 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
this.type,
|
this.type,
|
||||||
this.fadeOutDuration,
|
this.fadeOutDuration,
|
||||||
this.fadeInDuration,
|
this.fadeInDuration,
|
||||||
|
// 图片质量 默认1%
|
||||||
|
this.quality = 1,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -37,7 +40,8 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
? 0
|
? 0
|
||||||
: StyleString.imgRadius.x),
|
: StyleString.imgRadius.x),
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: src!.startsWith('//') ? 'https:${src!}' : src!,
|
imageUrl:
|
||||||
|
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality}q.webp',
|
||||||
width: width ?? double.infinity,
|
width: width ?? double.infinity,
|
||||||
height: height ?? double.infinity,
|
height: height ?? double.infinity,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
|||||||
@ -76,7 +76,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: videoItem.pic + '@.webp',
|
src: videoItem.pic,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -69,7 +69,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: videoItem.pic + '@.webp',
|
src: videoItem.pic,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class FavItem extends StatelessWidget {
|
|||||||
return Hero(
|
return Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: favFolderItem.cover + '@.webp',
|
src: favFolderItem.cover,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: videoItem.pic + '@.webp',
|
src: videoItem.pic,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -82,9 +82,8 @@ class HistoryItem extends StatelessWidget {
|
|||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: (videoItem.cover != ''
|
src: (videoItem.cover != ''
|
||||||
? videoItem.cover
|
? videoItem.cover
|
||||||
: videoItem.covers.first) +
|
: videoItem.covers.first),
|
||||||
'@.webp',
|
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class LiveCardV extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: '${liveItem.cover!}@.webp',
|
src: liveItem.cover!,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// ignore_for_file: library_private_types_in_public_api
|
// ignore_for_file: library_private_types_in_public_api
|
||||||
|
|
||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:extended_image/extended_image.dart';
|
import 'package:extended_image/extended_image.dart';
|
||||||
@ -44,179 +45,189 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return Scaffold(
|
||||||
children: [
|
backgroundColor: Colors.black,
|
||||||
DismissiblePage(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
primary: false,
|
||||||
onDismissed: () {
|
toolbarHeight: 0,
|
||||||
Navigator.of(context).pop();
|
backgroundColor: Colors.black,
|
||||||
},
|
systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||||
// Note that scrollable widget inside DismissiblePage might limit the functionality
|
),
|
||||||
// If scroll direction matches DismissiblePage direction
|
body: Stack(
|
||||||
direction: DismissiblePageDismissDirection.down,
|
children: [
|
||||||
disabled: _dismissDisabled,
|
DismissiblePage(
|
||||||
isFullScreen: true,
|
backgroundColor: Colors.transparent,
|
||||||
child: Hero(
|
onDismissed: () {
|
||||||
tag: _previewController
|
Navigator.of(context).pop();
|
||||||
.imgList[_previewController.initialPage.value],
|
},
|
||||||
child: GestureDetector(
|
// Note that scrollable widget inside DismissiblePage might limit the functionality
|
||||||
onTap: () {
|
// If scroll direction matches DismissiblePage direction
|
||||||
_previewController.visiable = !_previewController.visiable;
|
direction: DismissiblePageDismissDirection.down,
|
||||||
setState(() {});
|
disabled: _dismissDisabled,
|
||||||
},
|
isFullScreen: true,
|
||||||
child: ExtendedImageGesturePageView.builder(
|
child: Hero(
|
||||||
controller: ExtendedPageController(
|
tag: _previewController
|
||||||
initialPage: _previewController.initialPage.value,
|
.imgList[_previewController.initialPage.value],
|
||||||
pageSpacing: 0,
|
child: GestureDetector(
|
||||||
),
|
onTap: () {
|
||||||
onPageChanged: (int index) {
|
_previewController.visiable = !_previewController.visiable;
|
||||||
_previewController.initialPage.value = index;
|
setState(() {});
|
||||||
_previewController.currentPage.value = index + 1;
|
|
||||||
},
|
},
|
||||||
canScrollPage: (GestureDetails? gestureDetails) =>
|
child: ExtendedImageGesturePageView.builder(
|
||||||
gestureDetails!.totalScale! <= 1.0,
|
controller: ExtendedPageController(
|
||||||
preloadPagesCount: 2,
|
initialPage: _previewController.initialPage.value,
|
||||||
itemCount: _previewController.imgList.length,
|
pageSpacing: 0,
|
||||||
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(),
|
onPageChanged: (int index) {
|
||||||
ElevatedButton(
|
_previewController.initialPage.value = index;
|
||||||
onPressed: () => _previewController.onShareImg(),
|
_previewController.currentPage.value = index + 1;
|
||||||
child: const Text('分享')),
|
},
|
||||||
const SizedBox(width: 10),
|
canScrollPage: (GestureDetails? gestureDetails) =>
|
||||||
ElevatedButton(
|
gestureDetails!.totalScale! <= 1.0,
|
||||||
onPressed: () => _previewController.onSaveImg(),
|
preloadPagesCount: 2,
|
||||||
child: const Text('保存'))
|
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(
|
Hero(
|
||||||
tag: Utils.makeHeroTag(i.roomid),
|
tag: Utils.makeHeroTag(i.roomid),
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: i.cover + '@.webp',
|
src: i.cover,
|
||||||
type: 'emote',
|
type: 'emote',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
|
|||||||
Reference in New Issue
Block a user