opt: 图片渲染内存

This commit is contained in:
guozhigq
2024-01-14 18:06:56 +08:00
parent db6662c980
commit 8f661337f5
5 changed files with 111 additions and 55 deletions

View File

@ -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),
),
),
); );
} }
} }

View File

@ -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 *

View File

@ -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(),
],
), ),
), ),
); );

View File

@ -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
View 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();
}
}