我有一个由 CachedNetworkImage 和博客文本组成的 UI,它的想法是当文本超过图像的高度时使文本位于容器的开头。
我已经寻找了解决方案并实现了一些代码 该解决方案会产生一些问题:(如果您有更好的方法,只需评论它,您不必阅读问题)
我正在尝试实施的解决方案:
import 'dart:math';
import 'package:flutter/material.dart';
class DropCapText extends StatelessWidget {
final Widget dropCap;
final String text;
final TextStyle textStyle;
final EdgeInsets dropCapPadding;
const DropCapText({
super.key,
required this.dropCap,
required this.text,
required this.textStyle,
this.dropCapPadding = EdgeInsets.zero,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
//get the drop cap size
// final dropCapSpan = WidgetSpan(child: dropCap);
// final dropCapPainter = TextPainter(
// text: dropCapSpan, textDirection: Directionality.of(context));
// dropCapPainter.layout(maxWidth: constraints.maxWidth);
//get the position of the last bit of text next to the dropcap
final textSpan = TextSpan(text: text, style: textStyle);
final textPainter = TextPainter(
text: textSpan, textDirection: Directionality.of(context));
textPainter.layout(
maxWidth: max(constraints.minWidth,
constraints.maxWidth - dropCapPadding.horizontal - 150));
final lastPosition =
textPainter.getPositionForOffset(Offset(textPainter.width, 100));
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: dropCapPadding,
child: dropCap,
),
Expanded(
child: Text(
text.substring(0, lastPosition.offset),
style: textStyle,
softWrap: true,
),
),
],
),
Text(text.substring(lastPosition.offset), style: textStyle),
],
);
},
);
}
}
import 'dart:async';
import 'dart:developer';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class BlogImage extends StatefulWidget {
const BlogImage({super.key, required this.imageUrl});
final String imageUrl;
@override
State<BlogImage> createState() => _BlogImageState();
}
class _BlogImageState extends State<BlogImage> {
Size? imageSize;
@override
void initState() {
super.initState();
_fetchImageSize();
}
Future<void> _fetchImageSize() async {
try {
final Size size = await _calculateImageDimension(widget.imageUrl);
log("Fetched size: $size");
setState(() {
imageSize = size; // Update the state with the fetched size
});
} catch (e) {
log("Error fetching image size: $e");
}
}
@override
Widget build(BuildContext context) {
if (imageSize == null) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: 150,
width: 150,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CachedNetworkImage(
placeholderFadeInDuration: const Duration(milliseconds: 500),
fit: imageSize!.height > imageSize!.width
? BoxFit.fitWidth
: BoxFit.fitHeight,
imageUrl: widget.imageUrl,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
const Icon(Icons.image_not_supported),
),
),
);
}
}
Future<Size> _calculateImageDimension(String imageUrl) async {
Completer<Size> completer = Completer();
Image image = Image(image: CachedNetworkImageProvider(imageUrl));
image.image.resolve(const ImageConfiguration()).addListener(
ImageStreamListener(
(ImageInfo info, bool synchronousCall) {
var myImage = info.image;
Size size =
Size(myImage.width.toDouble(), myImage.height.toDouble());
completer.complete(size);
},
onError: (error, stackTrace) {
completer.completeError(error);
},
),
);
return completer.future;
}
我建议对课程进行一些细微的改变
BlogImage
。请注意,小部件状态变量 size
现在是 Future<Size>
并在 initState
中分配。
不再需要调用 setState
,构建方法现在返回 FutureBuilder
。
原则上,您可以组合功能
_calculateImageSize
和_fetchImageSize
。您可能想在记录后重新抛出在 _fetchImageSize
中捕获的错误。
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class BlogImage extends StatefulWidget {
const BlogImage({super.key, required this.imageUrl});
final String imageUrl;
@override
State<BlogImage> createState() => _BlogImageState();
}
class _BlogImageState extends State<BlogImage> {
late Future<Size> size;
@override
void initState() {
super.initState();
size = _fetchImageSize();
}
Future<Size> _fetchImageSize() async {
try {
final Size size = await _calculateImageSize(widget.imageUrl);
log("Fetched size: $size");
} catch (e) {
log("Error fetching image size: $e");
// rethrow; or return a default size
}
return Size(200, 200);
}
Future<Size> _calculateImageSize(String imageUrl) {
...
return completer.future;
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: size,
builder: (context, snapshot) => switch (snapshot.connectionState) {
ConnectionState.waiting => CircularProgressIndicator(),
ConnectionState.none => const Icon(Icons.image_not_supported),
ConnectionState.active || ConnectionState.done => SizedBox(
height: snapshot.data!.height,
width: snapshot.data!.width,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CachedNetworkImage(
placeholderFadeInDuration: const Duration(milliseconds: 500),
fit: snapshot.data!.height > snapshot.data!.width
? BoxFit.fitWidth
: BoxFit.fitHeight,
imageUrl: widget.imageUrl,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
const Icon(Icons.image_not_supported),
),
),
),
},
);
}
}
通过这些更改,我在滚动时没有观察到任何奇怪的行为。如果您使用
ListView
,当图像进入视图并调用 initState
时,将重新计算图像大小。