美文网首页
Flutter炫酷ui

Flutter炫酷ui

作者: 钉某人 | 来源:发表于2021-03-31 11:10 被阅读0次
    ui.png
    在闲暇冲浪的时候,无意间看到了这张设计图,眼睛一亮,感觉这个设计和创意非常酷,打算着手实现一下。关于设计图的作者没找到,如果有人知道的话,请告知我,我会添加设计引用的,欢迎来我的Github

    源码Github

    视频地址

    实战抖音

    分析设计,我们分两部分来实现:1.表盘部分 2.下部的开关部分。以下为了省略的逻辑代码,需要完整的代码的请移步Github

    1.表盘部分

    1.1 观察表盘的样式,把需要做的任务划分

    • 渐变的背景,用作秒针
    • 绘制白色的小圆点
    • 绘制带阴影的黄色大圆点,用作时针
    • 绘制时间文字
    • 开启定时器,让时钟动起来
      表盘部分很多都是需要我们自己绘制的,这里我们可以使用CustomPaint来实现所有的绘制。
      CustomPaint提供了自定义widget的能力,它会暴露一个canvas,可以通过这个canvas来绘制widget,有没有很熟悉,跟原生android的canvas是不是很相似。
      我们这里将表盘作为背景来绘制,也就是painter属性,自定义CustomPainter,重写paint(Canvas canvas,Size size)和shouldRepaint(covariant CustomPainter oldDelegate)函数,来完成我们所有的绘制操作。好的,让我们愉快的开始吧o( ̄▽ ̄)ブ。

    1.2 渐变的背景,用作秒针

    从图中我们可以知道,渐变色是扫描渐变,并布满屏幕,同时位置在整个屏幕的上半部分。首先创建扫描渐变对象,这个扫描渐变可以创建出一个着色器,然后我们将这个着色器附着在一个画笔上,通过画布去绘制。注意画布绘制时canvas.save()和canvas.restore()在对应时机的调用。要让秒针动起来,需要开启一个Timer定时任务,每一秒刷新一下视图。

        var circle = Rect.fromCircle(center: Offset(0, 0), radius: _screenHeight);
        //扫面渐变
        var sweepGradient = SweepGradient(
          colors: [
            _startColor,
            _endColor
          ],
        );
        //画笔对象
       Paint  _paintGradient = Paint()
          ..isAntiAlias = true
          ..shader = sweepGradient.createShader(circle)
          ..style = PaintingStyle.fill;
    
        //获取当前的时间
        DateTime dateTime = DateTime.now();
        var hour = dateTime.hour;
        var minute = dateTime.minute;
        var second = dateTime.second;
        //画布位移
        canvas.translate(_screenWidth / 2, _screenHeight / 100 * 35);
        //绘制渐变背景
        canvas.save();
        //每秒旋转对应角度,模拟秒针移动
        canvas.rotate(_getRotate(second));
        canvas.drawCircle(Offset(0, 0), _screenHeight, _paintGradient);
        canvas.restore();
    
    渐变的背景.gif

    1.3 绘制白色的小圆点

    这里需要绘制24个白色小圆点,平均分360度,通过画布的旋转来绘制不同角度的白色小圆点。

    for (double i = 0; i < _numPoint; i++) {
          canvas.save();
          //
          double deg = 360 / _numPoint * i;
          canvas.rotate(deg / 180 * pi);
            _paintDial.color = Colors.white;
           //绘制白色小圆点
           canvas.drawCircle(Offset(_radius, 0), 3, _paintDial);
           canvas.restore();
    
          ......
          canvas.restore();
        }
    
    绘制小圆点.png

    1.4 绘制带阴影的黄色大圆点,用作时针

    黄色的大圆点作为时针来处理,因为时针和画布的角度不是吻合的,需要换算,另外我们为大圆点添加阴影。

    for (double i = 0; i < _numPoint; i++) {
          canvas.save();
          double deg = 360 / _numPoint * i;
          canvas.rotate(deg / 180 * pi);
          _paintDial.color = Colors.white;
          canvas.drawCircle(Offset(_radius, 0), 3, _paintDial);
          //isShowBigCircle(hour, i)是判断当前圆点是不是当前的时针位置
          if (isShowBigCircle(hour, i)) {
            //绘制阴影
            Path path = Path()
              ..addArc(Rect.fromCircle(center: Offset(_radius, 0), radius: 8), 0,
                  pi * 2);
            canvas.drawShadow(path, Colors.yellow, 4, true);
            //绘制小时的圆点
            _paintDial.color = Colors.yellow;
            canvas.drawCircle(Offset(_radius, 0), 8, _paintDial);
          } else {
            _paintDial.color = Colors.white;
            canvas.drawCircle(Offset(_radius, 0), 3, _paintDial);
          }
          canvas.restore();
          ......
        }
    
    

    1.5 绘制时间文字

    画布绘制文字调用canvas.drawParagraph(Paragraph Offset)函数,Paragraph 对象通过ParagraphBuilder来创建,字体样式可以通过ParagraphBuilder来设置

       //设置文字样式
        _timeParagraphBuilder = ParagraphBuilder(ParagraphStyle(
            textAlign: TextAlign.center,
            fontSize: 70,
            maxLines: 1,
            fontWeight: FontWeight.bold));
       //获取当前的时间
       DateTime dateTime = DateTime.now();
        var hour = dateTime.hour;
        var minute = dateTime.minute;
        var second = dateTime.second;
          //绘制文字
          canvas.save();
          _timeParagraphBuilder.addText(_getTimeStr(hour, minute));
          Paragraph paragraph = _timeParagraphBuilder.build();
          paragraph.layout(ParagraphConstraints(width: 230));
          canvas.drawParagraph(paragraph, Offset(-115,-42));
          canvas.restore();
    

    2. 开关部分

    整体布局分析,开关部分的UI相对于整个屏幕来讲位于底部,使用 Stack 和 Align就可以实现这种布局样式。

      //整体布局
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Stack(
          children: [
            CustomPaint(painter: DialPlate(context,Color.fromARGB(255, 70, 0, 144),Color.fromARGB(255, 121, 83, 254))),
            _getAlarms(),
          ],
        ));
      }
    //下部视图
      _getAlarms() {
        return Align(
          alignment: Alignment.bottomLeft,
          child: Container(
            margin: EdgeInsets.only(left: 16, right: 16),
            height: 200,
            width: double.infinity,
            child: Column(
              children: [
                _getRow1(),
                _getRow2(),
                _getRow3(),
              ],
            ),
          ),
        );
      }
    //_getRow1()、_getRow2()、_getRow3()类似
     _getRow1() {
    
        return Container(
          alignment: Alignment.centerLeft,
          width: double.infinity,
          height: 50,
          child: Row(
            children: [
              Text(
                '06:45',
                style: TextStyle(
                    
                    fontSize: 25,
                    color: _firstSwitch == true ? _colorOn : _colorOff),
              ),
              Padding(
                padding: EdgeInsets.only(left: 18),
                child: Text(
                  'Wake up',
                  style: TextStyle(
                      
                      fontSize: 18,
                      color: _firstSwitch == true ? _colorOn : _colorOff),
                ),
              ),
              Expanded(child: SizedBox()),
              Container(
                width: 90,
                height: 10,
                child: Switch(
                    value: _firstSwitch,
                    onChanged: (onChanged) {
                      setState(() {_firstSwitch = onChanged;});
                    },
                    activeColor: _switchActiveColor,
                    activeTrackColor: Colors.black.withAlpha(100),
                    inactiveThumbColor: _switchInActiveColor,
                    inactiveTrackColor: Colors.black.withAlpha(20),
                ),
    
              )
            ],
          ),
        );
      }
    

    整体效果图如下:


    效果图.gif

    相关文章

      网友评论

          本文标题:Flutter炫酷ui

          本文链接:https://www.haomeiwen.com/subject/jqdohltx.html