opt: 图片渲染内存
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/utils/extension.dart';
|
||||||
import '../../utils/storage.dart';
|
import '../../utils/storage.dart';
|
||||||
import '../constants.dart';
|
import '../constants.dart';
|
||||||
|
|
||||||
@ -12,55 +13,75 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
this.src,
|
this.src,
|
||||||
required this.width,
|
required this.width,
|
||||||
required this.height,
|
required this.height,
|
||||||
this.cacheW,
|
|
||||||
this.cacheH,
|
|
||||||
this.type,
|
this.type,
|
||||||
this.fadeOutDuration,
|
this.fadeOutDuration,
|
||||||
this.fadeInDuration,
|
this.fadeInDuration,
|
||||||
// 图片质量 默认1%
|
// 图片质量 默认1%
|
||||||
this.quality,
|
this.quality,
|
||||||
|
this.origAspectRatio,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? src;
|
final String? src;
|
||||||
final double? width;
|
final double width;
|
||||||
final double? height;
|
final double height;
|
||||||
final double? cacheW;
|
|
||||||
final double? cacheH;
|
|
||||||
final String? type;
|
final String? type;
|
||||||
final Duration? fadeOutDuration;
|
final Duration? fadeOutDuration;
|
||||||
final Duration? fadeInDuration;
|
final Duration? fadeInDuration;
|
||||||
final int? quality;
|
final int? quality;
|
||||||
|
final double? origAspectRatio;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final double pr = MediaQuery.of(context).devicePixelRatio;
|
final String imageUrl =
|
||||||
final int picQuality =
|
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? 100}q.webp';
|
||||||
setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10) as int;
|
int? memCacheWidth, memCacheHeight;
|
||||||
|
double aspectRatio = (width / height).toDouble();
|
||||||
|
|
||||||
// double pr = 2;
|
void setMemCacheSizes() {
|
||||||
return src != ''
|
if (aspectRatio > 1) {
|
||||||
|
memCacheHeight = height.cacheSize(context);
|
||||||
|
} else if (aspectRatio < 1) {
|
||||||
|
memCacheWidth = width.cacheSize(context);
|
||||||
|
} else {
|
||||||
|
if (origAspectRatio != null && origAspectRatio! > 1) {
|
||||||
|
memCacheWidth = width.cacheSize(context);
|
||||||
|
} else if (origAspectRatio != null && origAspectRatio! < 1) {
|
||||||
|
memCacheHeight = height.cacheSize(context);
|
||||||
|
} else {
|
||||||
|
memCacheWidth = width.cacheSize(context);
|
||||||
|
memCacheHeight = height.cacheSize(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMemCacheSizes();
|
||||||
|
|
||||||
|
if (memCacheWidth == null && memCacheHeight == null) {
|
||||||
|
memCacheWidth = width.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return src != '' && src != null
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.antiAlias,
|
||||||
borderRadius: BorderRadius.circular(type == 'avatar'
|
borderRadius: BorderRadius.circular(
|
||||||
|
type == 'avatar'
|
||||||
? 50
|
? 50
|
||||||
: type == 'emote'
|
: type == 'emote'
|
||||||
? 0
|
? 0
|
||||||
: StyleString.imgRadius.x),
|
: StyleString.imgRadius.x,
|
||||||
|
),
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl:
|
imageUrl: imageUrl,
|
||||||
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? picQuality}q.webp',
|
width: width,
|
||||||
width: width ?? double.infinity,
|
height: height,
|
||||||
height: height ?? double.infinity,
|
memCacheWidth: memCacheWidth,
|
||||||
maxWidthDiskCache: ((cacheW ?? width!) * pr).toInt(),
|
memCacheHeight: memCacheHeight,
|
||||||
// maxHeightDiskCache: (cacheH ?? height!).toInt(),
|
|
||||||
memCacheWidth: ((cacheW ?? width!) * pr).toInt(),
|
|
||||||
// memCacheHeight: (cacheH ?? height!).toInt(),
|
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
fadeOutDuration:
|
fadeOutDuration:
|
||||||
fadeOutDuration ?? const Duration(milliseconds: 200),
|
fadeOutDuration ?? const Duration(milliseconds: 120),
|
||||||
fadeInDuration:
|
fadeInDuration:
|
||||||
fadeInDuration ?? const Duration(milliseconds: 200),
|
fadeInDuration ?? const Duration(milliseconds: 120),
|
||||||
// filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
errorWidget: (BuildContext context, String url, Object error) =>
|
errorWidget: (BuildContext context, String url, Object error) =>
|
||||||
placeholder(context),
|
placeholder(context),
|
||||||
placeholder: (BuildContext context, String url) =>
|
placeholder: (BuildContext context, String url) =>
|
||||||
@ -72,9 +93,9 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
|
|
||||||
Widget placeholder(BuildContext context) {
|
Widget placeholder(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
width: width ?? double.infinity,
|
width: width,
|
||||||
height: height ?? double.infinity,
|
height: height,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.antiAlias,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.4),
|
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.4),
|
||||||
borderRadius: BorderRadius.circular(type == 'avatar'
|
borderRadius: BorderRadius.circular(type == 'avatar'
|
||||||
@ -88,9 +109,12 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
type == 'avatar'
|
type == 'avatar'
|
||||||
? 'assets/images/noface.jpeg'
|
? 'assets/images/noface.jpeg'
|
||||||
: 'assets/images/loading.png',
|
: 'assets/images/loading.png',
|
||||||
width: 300,
|
width: width,
|
||||||
height: 300,
|
height: height,
|
||||||
)),
|
cacheWidth: width.cacheSize(context),
|
||||||
|
cacheHeight: height.cacheSize(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ class _ContentState extends State<Content> {
|
|||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
|
double maxWidth = box.maxWidth.truncateToDouble();
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
@ -62,8 +63,8 @@ class _ContentState extends State<Content> {
|
|||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: pictureItem.url,
|
src: pictureItem.url,
|
||||||
width: box.maxWidth / 2,
|
width: maxWidth / 2,
|
||||||
height: box.maxWidth *
|
height: maxWidth *
|
||||||
0.5 *
|
0.5 *
|
||||||
(pictureItem.height != null && pictureItem.width != null
|
(pictureItem.height != null && pictureItem.width != null
|
||||||
? pictureItem.height! / pictureItem.width!
|
? pictureItem.height! / pictureItem.width!
|
||||||
@ -83,6 +84,7 @@ class _ContentState extends State<Content> {
|
|||||||
list.add(
|
list.add(
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
|
double maxWidth = box.maxWidth.truncateToDouble();
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
@ -95,8 +97,10 @@ class _ContentState extends State<Content> {
|
|||||||
},
|
},
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: pics[i].url,
|
src: pics[i].url,
|
||||||
width: box.maxWidth,
|
width: maxWidth,
|
||||||
height: box.maxWidth,
|
height: maxWidth,
|
||||||
|
origAspectRatio:
|
||||||
|
pics[i].width!.toInt() / pics[i].height!.toInt(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -107,7 +111,7 @@ class _ContentState extends State<Content> {
|
|||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
double maxWidth = box.maxWidth;
|
double maxWidth = box.maxWidth.truncateToDouble();
|
||||||
double crossCount = len < 3 ? 2 : 3;
|
double crossCount = len < 3 ? 2 : 3;
|
||||||
double height = maxWidth /
|
double height = maxWidth /
|
||||||
crossCount *
|
crossCount *
|
||||||
|
|||||||
@ -849,6 +849,14 @@ InlineSpan buildContent(
|
|||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints box) {
|
builder: (BuildContext context, BoxConstraints box) {
|
||||||
|
double maxHeight = box.maxWidth * 0.6; // 设置最大高度
|
||||||
|
double width = (box.maxWidth / 2).truncateToDouble();
|
||||||
|
double height = ((box.maxWidth /
|
||||||
|
2 *
|
||||||
|
pictureItem['img_height'] /
|
||||||
|
pictureItem['img_width']))
|
||||||
|
.truncateToDouble();
|
||||||
|
print('width: $width, height: $height');
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
@ -859,15 +867,28 @@ InlineSpan buildContent(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Container(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
constraints: BoxConstraints(maxHeight: maxHeight),
|
||||||
|
width: box.maxWidth / 2,
|
||||||
|
height: height,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: pictureItem['img_src'],
|
src: pictureItem['img_src'],
|
||||||
width: box.maxWidth / 2,
|
width: box.maxWidth / 2,
|
||||||
height: box.maxWidth *
|
height: height,
|
||||||
0.5 *
|
),
|
||||||
pictureItem['img_height'] /
|
),
|
||||||
pictureItem['img_width'],
|
height > maxHeight
|
||||||
|
? const PBadge(
|
||||||
|
text: '长图',
|
||||||
|
right: 8,
|
||||||
|
bottom: 8,
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -304,7 +304,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
body: ExtendedNestedScrollView(
|
body: ExtendedNestedScrollView(
|
||||||
controller: _extendNestCtr,
|
controller: _extendNestCtr,
|
||||||
headerSliverBuilder:
|
headerSliverBuilder:
|
||||||
(BuildContext _context, bool innerBoxIsScrolled) {
|
(BuildContext context, bool innerBoxIsScrolled) {
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
Obx(
|
Obx(
|
||||||
() => SliverAppBar(
|
() => SliverAppBar(
|
||||||
@ -459,10 +459,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
side: MaterialStateProperty
|
side: MaterialStateProperty
|
||||||
.resolveWith(
|
.resolveWith((states) {
|
||||||
(states) {
|
|
||||||
return BorderSide(
|
return BorderSide(
|
||||||
color: Theme.of(context)
|
color: Theme.of(
|
||||||
|
context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primary
|
.primary
|
||||||
.withOpacity(0.5),
|
.withOpacity(0.5),
|
||||||
|
|||||||
7
lib/utils/extension.dart
Normal file
7
lib/utils/extension.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
extension ImageExtension on num {
|
||||||
|
int cacheSize(BuildContext context) {
|
||||||
|
return (this * MediaQuery.of(context).devicePixelRatio).round();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user