Flutter开发:如何实现目标UI中的斜向BoxShadow效果?
问题描述
目标UI:
我正在制作这个特定UI,但无法实现上图所示的BoxShadow设计。当前实现的BoxShadow偏居中,而我需要的是斜向偏移的阴影效果。
以下是我的main.dart文件:
import 'package:flutter/material.dart'; import 'package:secondlife_mobile/PageViewHolder.dart'; import 'package:provider/provider.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Perspective PageView', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { late PageViewHolder holder; late PageController _controller; double fraction = 0.57; // 通过这个参数,让PageView在显示当前页面的同时,展示前后页面50%的区域 @override void initState() { super.initState(); holder = PageViewHolder(value: 2.0); _controller = PageController(initialPage: 2, viewportFraction: fraction); _controller.addListener(() { holder.setValue(_controller.page); }); } @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( appBar: AppBar( title: const Text("Perspective PageView"), ), body: Container( child: Center( child: AspectRatio( aspectRatio: 1, child: ChangeNotifierProvider<PageViewHolder>.value( value: holder, child: PageView.builder( controller: _controller, itemCount: 4, physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { return MyPage( number: index.toDouble(), fraction: fraction, ); }), ), ), ), ), )); } } class MyPage extends StatelessWidget { final number; final double? fraction; const MyPage({super.key, this.number, this.fraction}); @override Widget build(BuildContext context) { double? value = Provider.of<PageViewHolder>(context).value; double diff = (number - value); // diff为负 = 左侧页面 // diff为0 = 当前页面 // diff为正 = 右侧页面 // 元素变换矩阵 final Matrix4 pvMatrix = Matrix4.identity() ..setEntry(3, 2, 1 / 0.9) // 放大90% ..setEntry(1, 1, fraction!) // 修改Y轴缩放比例 ..setEntry(3, 0, 0.004 * -diff); // 修改X轴透视效果 return Stack( fit: StackFit.expand, alignment: FractionalOffset.center, children: [ Transform( transform: pvMatrix, alignment: FractionalOffset.center, child: Container( decoration: const BoxDecoration(boxShadow: [ BoxShadow( color: Colors.grey, blurRadius: 2.0, spreadRadius: 4.0, offset: Offset(8.0, 20.0), // 阴影方向:右下 ) ]), child: Image.asset("assets/images/image_${number.toInt() + 1}.jpg", fit: BoxFit.fill), ), ), ], ); } }
当前模拟器运行效果:
解决方法
问题根源在于Container的阴影被Transform的透视矩阵变换干扰,导致偏移效果不符合预期。可以通过以下两种方式修复:
方法一:将阴影移到Transform外层
把阴影从Transform内部的Container剥离到外层独立容器中,避免透视变换影响阴影,确保斜向偏移效果稳定:
return Stack( fit: StackFit.expand, alignment: FractionalOffset.center, children: [ // 外层容器单独承载阴影 Container( decoration: const BoxDecoration(boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.6), // 增加透明度让阴影更自然 blurRadius: 10.0, spreadRadius: 1.0, offset: Offset(15.0, 28.0), // 调整偏移量匹配目标UI ) ]), ), Transform( transform: pvMatrix, alignment: FractionalOffset.center, child: Container( child: Image.asset("assets/images/image_${number.toInt() + 1}.jpg", fit: BoxFit.fill), ), ), ], );
方法二:根据页面位置动态调整阴影偏移
如果需要阴影跟随页面透视角度变化,可通过diff值动态修改BoxShadow的offset,让阴影与页面透视效果联动:
@override Widget build(BuildContext context) { double? value = Provider.of<PageViewHolder>(context).value; double diff = (number - value); // 根据页面偏移量动态调整阴影X轴偏移 Offset shadowOffset = Offset(8.0 + (diff * 6), 20.0); final Matrix4 pvMatrix = Matrix4.identity() ..setEntry(3, 2, 1 / 0.9) ..setEntry(1, 1, fraction!) ..setEntry(3, 0, 0.004 * -diff); return Stack( fit: StackFit.expand, alignment: FractionalOffset.center, children: [ Transform( transform: pvMatrix, alignment: FractionalOffset.center, child: Container( decoration: BoxDecoration(boxShadow: [ BoxShadow( color: Colors.grey, blurRadius: 2.0, spreadRadius: 4.0, offset: shadowOffset, ) ]), child: Image.asset("assets/images/image_${number.toInt() + 1}.jpg", fit: BoxFit.fill), ), ), ], ); }
推荐使用方法一,能更精准地匹配目标UI的斜向阴影效果,样式也更稳定。
内容的提问来源于stack exchange,提问作者loupdaniel
相关产品推荐
相关产品推荐

