如何实现正方形图片在半高容器内平滑滚动展示
问题描述
需要开发一个Flutter应用,实现以下效果:
- 在高度为正方形图片一半的
SizedBox中展示图片 - 初始状态显示图片的顶部区域
- 图片平滑向下滚动,逐步展示出图片的下半部分
尝试过ClipRect和AnimatedBuilder但未实现预期效果,以下是之前尝试的代码:
import 'package:flutter/material.dart'; import 'dart:async'; import 'dart:ui' as ui; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: ImageScrollAnimation(), ), ); } } class ImageScrollAnimation extends StatefulWidget { @override _ImageScrollAnimationState createState() => _ImageScrollAnimationState(); } class _ImageScrollAnimationState extends State<ImageScrollAnimation> with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation<double> _animation; Size? _imageSize; @override void initState() { super.initState(); _initData(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 3), ); _animation = Tween<double>(begin: 0, end: 0.5).animate( CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, ), ); _animationController.forward(); } @override void dispose() { _animationController.dispose(); super.dispose(); } void _initData() async { final image = Image.asset('assets/main.webp'); _imageSize = await getImageSize(image); setState(() {}); } @override Widget build(BuildContext context) { double screenWidth = MediaQuery.of(context).size.width; if (_imageSize == null) { return Center(child: CircularProgressIndicator()); } double adjustedHeight = getAdjustedHeight(_imageSize!, screenWidth); return SingleChildScrollView( child: Column( children: [ AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.translate( offset: Offset(0, -_animation.value * adjustedHeight), child: child, ); }, child: Image.asset( 'assets/main.webp', fit: BoxFit.fitWidth, width: screenWidth, ), ), Container( height: 800, // 滚动后显示的内容 color: Colors.red, ), Container( height: 800, color: Colors.blue, ), ], ), ); } } Future<Size> getImageSize(Image image) async { final Completer<ui.Image> completer = Completer<ui.Image>(); image.image.resolve(ImageConfiguration()).addListener( ImageStreamListener((ImageInfo info, bool _) { completer.complete(info.image); }), ); final ui.Image img = await completer.future; return Size(img.width.toDouble(), img.height.toDouble()); } double getAdjustedHeight(Size imageSize, double screenWidth) { double result = imageSize.height * (screenWidth / imageSize.width); print('Adjusted Height: $result'); return result; }
解决方案
核心问题是之前的代码未对图片进行裁剪,导致图片超出容器范围。通过ClipRect配合Transform.translate,再用SizedBox固定显示区域高度,即可实现需求。
修改后的代码如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: ImageScrollAnimation(), ), ); } } class ImageScrollAnimation extends StatefulWidget { @override _ImageScrollAnimationState createState() => _ImageScrollAnimationState(); } class _ImageScrollAnimationState extends State<ImageScrollAnimation> with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation<double> _scrollOffset; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 3), ); // 动画值从0到1,对应图片向下滚动自身一半高度的偏移量 _scrollOffset = Tween<double>(begin: 0, end: 1).animate( CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), ); _animationController.forward(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; // 正方形图片,显示区域高度为屏幕宽度的一半 final displayHeight = screenWidth / 2; return SingleChildScrollView( child: Column( children: [ // 固定显示区域高度 SizedBox( height: displayHeight, width: screenWidth, // 裁剪超出区域的内容 child: ClipRect( child: AnimatedBuilder( animation: _scrollOffset, builder: (context, child) { // 向下偏移图片,偏移量为显示区域高度乘以动画值 return Transform.translate( offset: Offset(0, displayHeight * _scrollOffset.value), child: child, ); }, child: Image.asset( 'assets/main.webp', fit: BoxFit.fitWidth, width: screenWidth, ), ), ), ), Container(height: 800, color: Colors.red), Container(height: 800, color: Colors.blue), ], ), ); } }
关键修改点:
- 使用
SizedBox固定显示区域高度为screenWidth / 2(正方形图片适配屏幕宽度后,高度等于屏幕宽度,一半即需求的展示高度) - 通过
ClipRect裁剪超出SizedBox范围的图片部分 - 调整
Transform.translate偏移逻辑:动画值从0到1,偏移量为displayHeight * _scrollOffset.value,实现图片从顶部向下滚动、逐步展示下半部分的效果 - 移除不必要的图片尺寸获取逻辑,利用正方形图片的特性简化计算
内容的提问来源于stack exchange,提问作者한경완
相关产品推荐
相关产品推荐

