是否可以在Flutter中自定义绘制中给予渐变模糊效果,如下图所示?

mm5n2pyu  于 2023-06-30  发布在  Flutter
关注(0)|答案(5)|浏览(143)

bounty还有2天到期。此问题的答案有资格获得+50声望奖励。Hamed Hamedi希望引起更多的注意这个问题:在自定义绘图器中,可能会出现在形状后面模糊较低层的情况。请分享你对这个有趣的挑战的看法。

我编码下面的输出,我得到了我想要的设计,但无法得到画布内的模糊效果。
这是我试图构建的输出

这是我尝试过的

这里是代码,

class MyCustomPainter extends CustomPainter {

@override


 void paint(Canvas canvas, Size size) {
    Paint paint0 = Paint()
      ..color = const Color.fromARGB(128,255,255,255)
    ..style = PaintingStyle.fill
    ..strokeWidth = 2.0;

Path path0 = Path();
path0.moveTo(size.width * 0.1500300, size.height * 0.1238500);
path0.cubicTo(
    size.width * 0.0037200,
    size.height * 0.1023500,
    size.width * 0.0522600,
    size.height * 0.7552500,
    size.width * 0.1500500,
    size.height * 0.8761750);
path0.cubicTo(
    size.width * 0.2767600,
    size.height * 0.8761750,
    size.width * 0.7234100,
    size.height * 0.8735500,
    size.width * 0.8501100,
    size.height * 0.8735500);
path0.cubicTo(
    size.width * 0.9464300,
    size.height * 0.7575750,
    size.width * 0.9946900,
    size.height * 0.0944750,
    size.width * 0.8496900,
    size.height * 0.1268750);
path0.cubicTo(
    size.width * 0.7230200,
    size.height * 0.1268750,
    size.width * 0.5303400,
    size.height * 0.1263500,
    size.width * 0.1500300,
    size.height * 0.1238500);
path0.close();
canvas.drawPath(path0, paint0);

}

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

先谢谢你了!

7dl7o3gd

7dl7o3gd1#

您不需要为CustomPainter而烦恼,所有事情都可以用ContainerBackdropFilter小部件完成。

代码示例

class BlurredBottomBar extends StatelessWidget {
  const BlurredBottomBar({
    super.key,
    required this.items,
    required this.currentIndex,
    required this.onItemTapped,
  });

  final int currentIndex;
  final void Function(int index) onItemTapped;
  final List<IconData> items;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final bottomNavigationBarTheme = theme.bottomNavigationBarTheme;

    return Container(
      margin: const EdgeInsets.all(12),
      padding: const EdgeInsets.all(12),
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        border: Border.all(
          color: Colors.white,
          strokeAlign: BorderSide.strokeAlignOutside,
        ),
        borderRadius: const BorderRadius.vertical(
          top: Radius.circular(9),
          bottom: Radius.circular(36),
        ),
      ),
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 2, sigmaY: 2),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            for (int i = 0; i < items.length; i++)
              IconButton(
                onPressed: () => onItemTapped(i),
                icon: Icon(
                  items[i],
                  color: i == currentIndex
                      ? bottomNavigationBarTheme.selectedItemColor
                      : bottomNavigationBarTheme.unselectedItemColor,
                ),
              ),
          ],
        ),
      ),
    );
  }
}

用法

Scaffold(
  extendBody: true,
  bottomNavigationBar: BlurredBottomBar(
    currentIndex: 0,
    items: const [
      Icons.home,
      Icons.grid_view,
      Icons.notifications_outlined,
      Icons.settings_outlined,
    ],
    onItemTapped: (index) {
      // ...
    },
  ),
  body: ...,
);

截图

You can try the full example on zapp.run

eimct9ow

eimct9ow2#

我不知道你是否有严格的要求使用自定义绘制,但你可以使用BackdropFilter并使用ImageFilter.blur作为过滤器值来实现这种效果。

q3qa4bjr

q3qa4bjr3#

使用BackdropFilter。演示使用:

BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
                child: Container(
                  width: 334.66,
                  height: 212.66,
                  color: Colors.black.withOpacity(0.2),
                  child: Center(
                    child: Text(
                      'Demo Text',
                  ),
                ),
              ),
zpjtge22

zpjtge224#

首先,你必须将extendBody作为true传递给脚手架,这样主体将延伸到底部导航栏上,然后像这样添加带有BackDropFilter的bottomNavigationBar,

bottomNavigationBar: Container(
        margin: const EdgeInsets.all(12.0),
        child: ClipRRect(
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(12.0),
            topRight: Radius.circular(12.0),
            bottomLeft: Radius.circular(36.0),
            bottomRight: Radius.circular(36.0),
          ),
          child: BackdropFilter(
            filter: ImageFilter.blur(
              sigmaX: 2.0,
              sigmaY: 2.0,
            ),
            child: Container(
              decoration: BoxDecoration(color: Colors.orange.withOpacity(0.4)),
              child: BottomNavigationBar(
                elevation: 0,
                type: BottomNavigationBarType.fixed,
                unselectedItemColor: Colors.blue,
                backgroundColor: Colors.transparent,
                onTap: (int index) {
                  setState(() {
                    selectedIndex = index;
                  });
                },
                currentIndex: selectedIndex,
                items: const <BottomNavigationBarItem>[
                  BottomNavigationBarItem(
                      icon: Icon(Icons.home), label: "Home"),
                  BottomNavigationBarItem(
                      icon: Icon(Icons.person), label: "Profile"),
                ],
              ),
            ),
          ),
        ),
      ),

在这里,我尝试创建问题中共享的UI,我在BackDropFilter中添加了2个BottomNavigationBarItem,以便模糊底部导航栏的背景。here是完整的代码供参考.

zqdjd7g9

zqdjd7g95#

我已经能够达到你所需要的非常接近的结果。尤其是模糊效果。使用的关键元素是ClipRRectClipPathBackdropFilter。我也用过其他元素,但这取决于你。我们的想法是对角剪裁矩形,这样一边是模糊的,另一边是透明的。此外,剪裁限制了矩形外背景滤镜的效果,否则会导致整个屏幕上的模糊效果。如果你在不透明度的情况下使用各种颜色值,你可以得到确切的结果。您也可以在此Dartpad link中查找结果
main.dart file.

import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: false,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  Widget drawRowItem(BuildContext context) {
    return Expanded(
      child: Container(
        margin: const EdgeInsets.symmetric(vertical: 12),
        decoration: BoxDecoration(
            borderRadius: const BorderRadius.all(Radius.circular(16)),
            border: Border.all(
                width: 2, color: const Color.fromARGB(255, 40, 40, 40))),
        height: 200,
        child: const Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Icon(
                Icons.home_filled,
                color: Colors.white,
              ),
              SizedBox(height: 8),
              Text(
                "Samle Item",
                style: TextStyle(
                  color: Colors.white,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget drawRowItems(BuildContext context) {
    return Container(
      height: MediaQuery.of(context).size.width / 2 - 56,
      width: MediaQuery.of(context).size.width / 2 - 56,
      margin: const EdgeInsets.symmetric(horizontal: 22),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          drawRowItem(context),
          const SizedBox(
            width: 16,
          ),
          drawRowItem(context)
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    List<int> items = List.filled(40, 0);
    return Scaffold(
      backgroundColor: Colors.black,
      body: Stack(
        fit: StackFit.expand,
        children: [
          ListView(
            children: items.map((e) {
              return drawRowItems(context);
            }).toList(),
          ),
          Positioned(
            bottom: 18,
            left: 18,
            right: 18,
            height: 86,
            child: Container(
              decoration: BoxDecoration(
                  border: Border.all(
                      width: 2, color: const Color.fromARGB(255, 40, 40, 40)),
                  borderRadius: const BorderRadius.only(
                      topRight: Radius.circular(24),
                      topLeft: Radius.circular(24),
                      bottomLeft: Radius.circular(52),
                      bottomRight: Radius.circular(52)),
                 color: const Color.fromARGB(100, 40, 40, 40)
              ),
              child: ClipRRect(
                borderRadius: const BorderRadius.only(
                    topRight: Radius.circular(24),
                    topLeft: Radius.circular(24),
                    bottomLeft: Radius.circular(52),
                    bottomRight: Radius.circular(52)),
                child: ClipPath(
                  clipper: MyCustomClipper(),
                  child: BackdropFilter(
                    filter: ImageFilter.blur(sigmaY: 5, sigmaX: 5),
                    child: Container(),
                  ),
                ),
              ),
            ),
          ),
          const Positioned(
            bottom: 18,
            left: 22,
            right: 22,
            height: 86,
            child: Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Icon(
                  Icons.home_outlined,
                  color: Colors.amber,
                ),
                Icon(
                  Icons.apps,
                  color: Colors.white,
                ),
                Icon(
                  Icons.notifications_outlined,
                  color: Colors.white,
                ),
                Icon(
                  Icons.settings_outlined,
                  color: Colors.white,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class MyCustomClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.moveTo(0, 0);
    path.lineTo(size.width, 0);
    path.lineTo(64,size.height);
    path.lineTo(0,size.height);
    path.lineTo(0,0);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return true;
  }
}

相关问题