如何在Flutter的RoundedRectangleBorder按钮上应用类似玻璃的3D效果? [英] How to apply glass-like 3D effect on a RoundedRectangleBorder button on Flutter?

查看:291
本文介绍了如何在Flutter的RoundedRectangleBorder按钮上应用类似玻璃的3D效果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建具有以下效果的按钮:

目前,我有一个圆形的扁平按钮,如下所示:

  RaisedButton(颜色:颜色(0xFFA93EF0),形状:RoundedRectangleBorder(borderRadius:BorderRadius.circular(30.0),), 

如何应用此类效果使其看起来像3D?我不知道这种效果的名字,但看起来像玻璃

解决方案

最终输出:

我们可以使用 ClipPath CustomClipper

实现这种外观

如果您不习惯使用 CustomClipper ,一开始很难理解下面的代码,因此您可能需要花一些时间来了解 path.lineTo path.quadraticBezierTo 已实现.

一旦掌握了这一点,就可以轻松复制此按钮以及更复杂的形状.

完整源代码:

  import'package:flutter/material.dart';void main(){runApp(MyApp());}MyApp类扩展了StatelessWidget {@override窗口小部件build(BuildContext context){返回MaterialApp(标题:"Flutter演示",主题:ThemeData(primarySwatch:Colors.blue,visualDensity:VisualDensity.adaptivePlatformDensity,),主页:RoundedButton(标题:"Flutter演示首页"),);}}类RoundedButton扩展了StatefulWidget {RoundedButton({Key key,this.title}):super(key:key);最终的字符串标题;@override_RoundedButtonState createState()=>_RoundedButtonState();}类_RoundedButtonState扩展了State< RoundedButton>.{int计数器= 0;无效_counter(){setState((){计数器=计数器+ 1;});}@override窗口小部件build(BuildContext context){返回脚手架(appBar:AppBar(标题:文本(widget.title),),正文:列(mainAxisAlignment:MainAxisAlignment.center,孩子们: [中心(子代:ClipPath(快船:CustomClipperButton(),子代:Stack(孩子们: [功率onTap:_counter,子代:集装箱(宽度:300,高度:150,颜色:Colors.purple [900],子代:集装箱(宽度:200,高度:100,装饰:BoxDecoration(boxShadow:[BoxShadow(颜色:Colors.purple [300],spreadRadius:0,blurRadius:20.0,),],),),),),定位(底部:-70,右:-90,子代:集装箱(宽度:180,高度:180,装饰:BoxDecoration(颜色:Colors.transparent,形状:BoxShape.circle,boxShadow:[BoxShadow(颜色:Colors.purple [800],spreadRadius:5blurRadius:20.0,),],),),),],),),),SizedBox(高度:30),文本(您按下了$ counter次",样式:TextStyle(fontWeight:FontWeight.bold,fontSize:20),)],),);}}CustomClipperButton类扩展了CustomClipper< Path>.{@overridegetClip(大小大小){var path = Path();path.moveTo(0,size.height * 0.2);path.lineTo(0,size.height * 0.8);path.quadraticBezierTo(0,size.height,size.width * 0.1,size.height);path.lineTo(size.width * 0.7-10,size.height);path.quadraticBezierTo(size.width * 0.7,size.height,size.width * 0.7,size.height * 0.95);path.quadraticBezierTo(size.width * 0.7,size.height * 0.30,size.width-10,size.height * 0.3);path.quadraticBezierTo(size.width,size.height * 0.3,size.width,size.height * 0.3-10);path.lineTo(size.width,size.height * 0.2);path.quadraticBezierTo(size.width,0,size.width * 0.9,0);path.lineTo(size.width * 0.1,0);path.quadraticBezierTo(0,0,0,size.height * 0.2);返回路径;}@overridebool shouldReclip(CustomClipper oldClipper){返回true;}} 

列出文档:

  1. 这里唯一需要牺牲的是中心四分之一圆的起始处的小圆形边缘.

    如果需要,可以动态全局设置宽度和高度.

    如果您问我,我建议您采用这种方法,而不要使用 ClipPath ,它看起来很统一,不太复杂并且易于配置.

      import'package:flutter/material.dart';DpadButtons类扩展了StatefulWidget {@override_DpadButtonsState createState()=>_DpadButtonsState();}类_DpadButtonsState扩展了State< DpadButtons>{字符串按钮=";void _selectedButton(String selectedButton){setState((){button = selectedButton;});}@override窗口小部件build(BuildContext context){double heightMain = 100;double widthMain = 180;返回脚手架(appBar:AppBar(标题:文本("Dpad Button")),身体:容器(宽度:double.infinity,子代:Stack(孩子们: [柱子(mainAxisAlignment:MainAxisAlignment.center,crossAxisAlignment:CrossAxisAlignment.center,孩子们: [buildButtonRow(b1:按钮1",b2:按钮2",onClick:_selectedButton,宽度:widthMain,高度:heightMain,),SizedBox(高度:10,),buildButtonRow(b1:按钮3",b2:按钮4",onClick:_selectedButton,宽度:widthMain,高度:heightMain,),SizedBox(高度:10,),],),中心(子代:集装箱(宽度:高度主要* 1.5,高度:heightMain * 1.5,装饰:BoxDecoration(颜色:Colors.white,形状:BoxShape.circle,),),),定位(左:100,最高:200,子代:文字(您点击了:$ button",样式:TextStyle(fontSize:20.0,fontWeight:FontWeight.bold,),),),],),),);}行buildButtonRow({String b1,String b2,onClick函数,双倍宽度,双倍高度}){返回行(mainAxisAlignment:MainAxisAlignment.center,孩子们: [功率onTap:(){onClick(b1);},子代:集装箱(宽度:宽度,高度:高度装饰:BoxDecoration(颜色:Colors.purple [900],borderRadius:BorderRadius.circular(height * 0.2),),子代:集装箱(宽度:宽度* 0.80,高度:高度* 0.80,装饰:BoxDecoration(borderRadius:BorderRadius.circular(height * 0.15),boxShadow:[BoxShadow(颜色:Colors.purple [200],spreadRadius:-8,blurRadius:10.0,),],),子代:中心(子代:文字(b1)),),),),SizedBox(宽度:10,),功率onTap:(){onClick(b2);},子代:集装箱(宽度:宽度,高度:高度装饰:BoxDecoration(颜色:Colors.purple [900],borderRadius:BorderRadius.circular(height * 0.2),),子代:集装箱(宽度:宽度* 0.80,高度:高度* 0.80,装饰:BoxDecoration(borderRadius:BorderRadius.circular(height * 0.15),boxShadow:[BoxShadow(颜色:Colors.purple [200],spreadRadius:-8,blurRadius:10.0,),],),子代:中心(子代:文字(b2)),),),),],);}} 

    I'm trying to create a button with these effects:

    Currently I have a rounded flat button like this:

    RaisedButton(
        color: Color(0xFFA93EF0),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(30.0),
        ),
    

    How can I apply such effects to make it look 3D? I don't know the name of this effect but it looks like glass

    解决方案

    Final Output:

    We can achieve this look using ClipPath and CustomClipper,

    It will be a little hard to comprehend the code below at first if you are not accustomed to using CustomClipper so you might have to spend some time understanding how path.lineTo and path.quadraticBezierTo are implemented.

    Once you get a hang of it, you will be able to replicate this button as well as even more complex shapes with ease.

    Full Source Code:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: RoundedButton(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class RoundedButton extends StatefulWidget {
      RoundedButton({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _RoundedButtonState createState() => _RoundedButtonState();
    }
    
    class _RoundedButtonState extends State<RoundedButton> {
      int counter = 0;
    
      void _counter() {
        setState(() {
          counter = counter + 1;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Center(
                child: ClipPath(
                  clipper: CustomClipperButton(),
                  child: Stack(
                    children: [
                      InkWell(
                        onTap: _counter,
                        child: Container(
                          width: 300,
                          height: 150,
                          color: Colors.purple[900],
                          child: Container(
                            width: 200,
                            height: 100,
                            decoration: BoxDecoration(
                              boxShadow: [
                                BoxShadow(
                                  color: Colors.purple[300],
                                  spreadRadius: 0,
                                  blurRadius: 20.0,
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                      Positioned(
                        bottom: -70,
                        right: -90,
                        child: Container(
                          width: 180,
                          height: 180,
                          decoration: BoxDecoration(
                            color: Colors.transparent,
                            shape: BoxShape.circle,
                            boxShadow: [
                              BoxShadow(
                                color: Colors.purple[800],
                                spreadRadius: 5,
                                blurRadius: 20.0,
                              ),
                            ],
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              SizedBox(height: 30),
              Text(
                "You Pressed $counter times",
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
              )
            ],
          ),
        );
      }
    }
    
    class CustomClipperButton extends CustomClipper<Path> {
      @override
      getClip(Size size) {
        var path = Path();
        path.moveTo(0, size.height * 0.2);
        path.lineTo(0, size.height * 0.8);
        path.quadraticBezierTo(0, size.height, size.width * 0.1, size.height);
        path.lineTo(size.width * 0.7 - 10, size.height);
        path.quadraticBezierTo(
            size.width * 0.7, size.height, size.width * 0.7, size.height * 0.95);
        path.quadraticBezierTo(size.width * 0.7, size.height * 0.30,
            size.width - 10, size.height * 0.3);
        path.quadraticBezierTo(
            size.width, size.height * 0.3, size.width, size.height * 0.3 - 10);
        path.lineTo(size.width, size.height * 0.2);
        path.quadraticBezierTo(size.width, 0, size.width * 0.9, 0);
        path.lineTo(size.width * 0.1, 0);
        path.quadraticBezierTo(0, 0, 0, size.height * 0.2);
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper oldClipper) {
        return true;
      }
    }
    
    

    List To Documentations:

    1. ClipPath
    2. CustomClipper
    3. lineTo()
    4. quadraticBezierTo()

    PS: I have used a dimension of 300*150 for the button container, you might have to take into account the dimension of the button that you will be creating, so calculating x,y coordinates will be a bit cumbersome at times, but as I said in the beginning after you understand the lineTo() and quadraticBezierTo() then implementation button of any size and shape will be very easy.

    Update:

    New Example without using ClipPath and probably the easiest:

    Final Output:

    The only thing here you have to sacrifice is the tiny rounded edges where that central quarter-circle starts-ends.

    Width and heights can be dynamically set globally if required.

    If You ask me, I will suggest you follow this method instead of ClipPath, which looks uniform, less complex, and easy to configure.

    import 'package:flutter/material.dart';
    
    class DpadButtons extends StatefulWidget {
      @override
      _DpadButtonsState createState() => _DpadButtonsState();
    }
    
    class _DpadButtonsState extends State<DpadButtons> {
      String button = "";
    
      void _selectedButton(String selectedButton) {
        setState(() {
          button = selectedButton;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        double heightMain = 100;
        double widthMain = 180;
        return Scaffold(
          appBar: AppBar(title: Text("Dpad Button")),
          body: Container(
            width: double.infinity,
            child: Stack(
              children: [
                Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    buildButtonRow(
                      b1: "Button 1",
                      b2: "Button 2",
                      onClick: _selectedButton,
                      width: widthMain,
                      height: heightMain,
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    buildButtonRow(
                      b1: "Button 3",
                      b2: "Button 4",
                      onClick: _selectedButton,
                      width: widthMain,
                      height: heightMain,
                    ),
                    SizedBox(
                      height: 10,
                    ),
                  ],
                ),
                Center(
                  child: Container(
                    width: heightMain * 1.5,
                    height: heightMain * 1.5,
                    decoration: BoxDecoration(
                      color: Colors.white,
                      shape: BoxShape.circle,
                    ),
                  ),
                ),
                Positioned(
                  left: 100,
                  top: 200,
                  child: Text(
                    "You clicked : $button",
                    style: TextStyle(
                      fontSize: 20.0,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    
      Row buildButtonRow(
          {String b1, String b2, Function onClick, double width, double height}) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            InkWell(
              onTap: () {
                onClick(b1);
              },
              child: Container(
                width: width,
                height: height,
                decoration: BoxDecoration(
                  color: Colors.purple[900],
                  borderRadius: BorderRadius.circular(height * 0.2),
                ),
                child: Container(
                  width: width * 0.80,
                  height: height * 0.80,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(height * 0.15),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.purple[200],
                        spreadRadius: -8,
                        blurRadius: 10.0,
                      ),
                    ],
                  ),
                  child: Center(child: Text(b1)),
                ),
              ),
            ),
            SizedBox(
              width: 10,
            ),
            InkWell(
              onTap: () {
                onClick(b2);
              },
              child: Container(
                width: width,
                height: height,
                decoration: BoxDecoration(
                  color: Colors.purple[900],
                  borderRadius: BorderRadius.circular(height * 0.2),
                ),
                child: Container(
                  width: width * 0.80,
                  height: height * 0.80,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(height * 0.15),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.purple[200],
                        spreadRadius: -8,
                        blurRadius: 10.0,
                      ),
                    ],
                  ),
                  child: Center(child: Text(b2)),
                ),
              ),
            ),
          ],
        );
      }
    }
    

    这篇关于如何在Flutter的RoundedRectangleBorder按钮上应用类似玻璃的3D效果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆