You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter开发:如何实现目标UI中的斜向BoxShadow效果?

问题描述

目标UI:
目标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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:32:28