如何在鼠标悬停时缩放列表项,使其始终可见(Web 平台上的 Flutter)
问题是这样描述的。
在网络环境中,我必须构建一个水平图像列表(如在 Netflix 中),当用户将鼠标光标放在元素上时,它应该会增加元素的大小。为了实现这一点,我使用堆栈(clipBehavior等于Clip.none)来呈现列表中的每个项目,当我检测到鼠标悬停事件时,我添加一个新容器(大于原始项目的大小)来绘制一个 AnimatedContainer里面会长出来填满它。
动画效果很好,但是容器被定位到列表中的下一个正确的项目,但是,我需要它在项目上方。
这是代码:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
final double zoomTargetHeight = 320;
final double zoomTargetWidth = 500;
final double zoomOriginalHeight = 225;
final double zoomOriginalWidth = 400;
double _zoomHeight = 225;
double _zoomWidth = 400;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
children: [
Image.network("https://source.unsplash.com/random/1600x900?cars"),
Container(
color: Colors.black87,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 12,
),
const Text(
"List of items",
style: TextStyle(color: Colors.white),
),
const SizedBox(
height: 12,
),
SizedBox(
height: 235,
child: ListView.separated(
clipBehavior: Clip.none,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return buildCard(index);
},
separatorBuilder: (context, index) {
return const SizedBox(
width: 12,
);
},
itemCount: 10,
),
),
const SizedBox(
height: 200,
),
],
),
),
),
],
),
),
);
}
Map _showZoom = {};
Widget buildCard(int index) {
Stack stack = Stack(
clipBehavior: Clip.none,
children: [
MouseRegion(
onEnter: (event) {
setState(() {
_showZoom["$index"] = true;
});
},
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Stack(
children: [
Image.network(
"https://source.unsplash.com/random/400x225?sig=$index&cars"),
Container(
color: Colors.black.withAlpha(100),
height: zoomOriginalHeight,
width: zoomOriginalWidth,
),
],
),
),
),
if (_showZoom["$index"] != null && _showZoom["$index"]!)
Positioned(
left: (zoomOriginalWidth - zoomTargetWidth) / 2,
top: (zoomOriginalHeight - zoomTargetHeight) / 2,
child: MouseRegion(
onHover: (_) {
setState(() {
_zoomHeight = zoomTargetHeight;
_zoomWidth = zoomTargetWidth;
});
},
onExit: (event) {
setState(() {
_showZoom["$index"] = false;
_zoomHeight = zoomOriginalHeight;
_zoomWidth = zoomOriginalWidth;
});
},
child: SizedBox(
width: zoomTargetWidth,
height: zoomTargetHeight,
child: Center(
child: AnimatedContainer(
duration: const Duration(milliseconds: 400),
width: _zoomWidth,
height: _zoomHeight,
// color: Colors.green.withAlpha(100),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
image: DecorationImage(
image: NetworkImage(
"https://source.unsplash.com/random/400x225?sig=$index&cars"),
fit: BoxFit.cover,
),
),
),
),
),
),
),
],
);
return stack;
}
}
记住flutter config --enable-web