Flutter UI - 打造一个圆形滑块(Slider)

[译] 用 Flutter 打造一个圆形滑块(Slider) 可以看到,paint()方法获得一个和一个参数。提供一组方法可以让我们绘制任何形状:圆形、直线、圆弧、矩形等等。参数即是画布的尺寸,由画布适配的部件尺寸决定。我们还需要一个,允许我们定制样式、颜色以及其他东西。现在的功能用法已经不言自明,然而却有一点儿不寻常,现在我们不仅要绘制一个圆弧而非圆,还需要绘制 Handler。@override// 绘制 handlerPaint()??12.0; 阅读详情
原文地址:How to build a circular slider in Flutter
原文作者:David Anaya
译文出自:掘金翻译计划
本文永久链接:github.com/xitu/gold-m…
译者:DevMcryYu
校对者:MollyAredtana,JasonLinkinBright

你是否也曾想要通过为滑块添加双重滑块或修改其布局来让它看起来不那么无聊?

在这篇文章中我会展示如何通过整合 GestureDetector 以及 Canvas 来在 Flutter 中构建一个圆形滑块。

如果你对构建它的过程不感兴趣,仅仅是为了获取此部件并使用它,那么你可以使用我在 pub.dartlang.org/packages/fl… 发布的程序包。

https://pub.dartlang.org/packages/flutter_circular_slider.

为什么要用圆形滑块?

大多数情况下你并不会需要它。但想象一下:如果你想要用户选定一个时间段,或者只是想要一个比直线形状更有趣一点的常规滑块的场景时,就可以使用圆形滑块。

用什么来构建它?

我们要准备的第一件事就是创建一个真正的滑块。为此,我们要用一个完美的圆形作为背景,在它的基础上再画一个根据用户交互可以动态显示的圆。为了实现我们的想法,我们将用到一个名为 CustomPaint 的特殊部件,它提供一个允许让我们自由创作的画布(Canvas)。

当滑块渲染完成以后,我们希望用户能够和它进行交互,因此我们选择使用 GestureDetector 封装它来捕获点击及拖动事件。

完整流程是:

  • 绘制滑块
  • 当用户通过点击其中一个滑块并拖动它来与圆形滑块交互时识别此事件。
  • 将事件的附加信息向下传递给画布(Canvas),在这里我们将重新绘制顶部圆形。
  • 将新值一路向上传递给相应的 Handler,以便让用户观察到变化。(例如,更新滑块中心的文字显示)。

(只需关注上图黄色部分)

来画几个圆吧

我们要做的第一件事就是画两个圆。一个静态样式(无需改变),另一个则是动态的样式(响应用户交互),我使用两个 Painter 来分别绘制它们。

两个 Painter 都继承自 CustomPainter —— 一个由 Flutter 提供并实现 paint() 及 shouldRepaint() 方法的类。第一个方法用来绘制我们想要绘制的形状,第二个方法在有变化时进行重新绘制的时候调用。对于 BasePainter 而言我们永远不会需要重绘,因此它的返回值总是 false。而对于 SliderPainter 来说它总是返回 true,因为每次更改都意味着用户移动了滑块,必须更新所选择的项。

 

 

 

 

 

 

 

 

import 'package:flutter/material.dart';
 
class BasePainter extends CustomPainter {
  Color baseColor;
 
  Offset center;
  double radius;
 
  BasePainter({@required this.baseColor});
 
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
        ..color = baseColor
        ..strokeCap = StrokeCap.round
        ..style = PaintingStyle.stroke
        ..strokeWidth = 12.0;
 
    center = Offset(size.width / 2, size.height / 2);
    radius = min(size.width / 2, size.height / 2);
 
    canvas.drawCircle(center, radius, paint);
  }
 
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

可以看到,paint() 方法获得一个 Canvas 和一个 Size 参数。Canvas 提供一组方法可以让我们绘制任何形状:圆形、直线、圆弧、矩形等等。Size 参数即是画布的尺寸,由画布适配的部件尺寸决定。我们还需要一个 Paint,允许我们定制样式、颜色以及其他东西。

现在 BasePainter 的功能用法已经不言自明,然而 SliderPainter 却有一点儿不寻常,现在我们不仅要绘制一个圆弧而非圆,还需要绘制 Handler。

 

import 'dart:math';
 
import 'package:flutter/material.dart';
import 'package:flutter_circular_slider/src/utils.dart';
 
class SliderPainter extends CustomPainter {
  double startAngle;
  double endAngle;
  double sweepAngle;
  Color selectionColor;
 
  Offset initHandler;
  Offset endHandler;
  Offset center;
  double radius;
 
  SliderPainter(
      {@required this.startAngle,
      @required this.endAngle,
      @required this.sweepAngle,
      @required this.selectionColor});
 
  @override
  void paint(Canvas canvas, Size size) {
    if (startAngle == 0.0 && endAngle == 0.0) return;
 
    Paint progress = _getPaint(color: selectionColor);
 
    center = Offset(size.width / 2, size.height / 2);
    radius = min(size.width / 2, size.height / 2);
 
    canvas.drawArc(Rect.fromCircle(center: center, radius: radius),
        -pi / 2 + startAngle, sweepAngle, false, progress);
 
    Paint handler = _getPaint(color: selectionColor, style: PaintingStyle.fill);
    Paint handlerOutter = _getPaint(color: selectionColor, width: 2.0);
 
    // 绘制 handler
    initHandler = radiansToCoordinates(center, -pi / 2 + startAngle, radius);
    canvas.drawCircle(initHandler, 8.0, handler);
    canvas.drawCircle(initHandler, 12.0, handlerOutter);
 
    endHandler = radiansToCoordinates(center, -pi / 2 + endAngle, radius);
    canvas.drawCircle(endHandler, 8.0, handler);
    canvas.drawCircle(endHandler, 12.0, handlerOutter);
  }
 
  Paint _getPaint({@required Color color, double width, PaintingStyle style}) =>
      Paint()
        ..color = color
        ..strokeCap = StrokeCap.round
        ..style = style ?? PaintingStyle.stroke
        ..strokeWidth = width ?? 12.0;
 
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

再一次地,我们获取了 center 和 radius 的值,但我们这次绘制的是圆弧。SliderPainter 将根据用户交互反馈的值作为 start、end 和 sweap 属性的值,以便于我们根据这些参数来绘制圆弧。值得一提的是我们需要从初始角度中减去 pi/2,因为我们的滑块的圆弧的起始位置是在圆形的正上方,而 drawArc() 方法使用 x 轴正轴作为起始位置。

当我们绘制好圆弧以后我们就需要准备绘制 Handler 了。为此,我们将分别绘制两个圆,一个在内部填充,一个在外部包裹。我调用了一些工具集函数用来将弧度转换为圆的坐标。你可以在 Github 仓库内查阅这些函数。

https://github.com/davidanaya/flutter-circular-slider

让滑块响应交互

目前来看,仅仅使用 CustomPaint 以及两个 Painter 就已经足够绘制想要的东西了。然而它们还是不能够进行交互。因此就要使用 GestureDetector 来对它进行封装。这样一来我们就可以在画布上对用户事件做出相应处理。

一开始我们将为 Handler 赋初值,当获取这些 Handler 的坐标后,我们将按照以下策略执行操作:

  • 监听对于 Handler 的点击(按下)事件并更新相应 Handler 的状态。(_xHandlerSelected = true)。
  • 监听被选中 Handler 的拖动更新事件,更新其坐标,同时分别向下、向上传递给 SliderPainter 和我们的回调函数。
  • 监听 Handler 的点击(抬起)事件并重置未选中 Handler 的状态。

因为我们需要分别计算出坐标值、新的角度值再传递给 Handler 和 Painter,所以我们的 CircularSliderPaint 必须是一个 StatefulWidget。

import 'package:flutter/material.dart';
import 'package:flutter_circular_slider/src/base_painter.dart';
import 'package:flutter_circular_slider/src/slider_painter.dart';
import 'package:flutter_circular_slider/src/utils.dart';
 
class CircularSliderPaint extends StatefulWidget {
  final int init;
  final int end;
  final int intervals;
  final Function onSelectionChange;
  final Color baseColor;
  final Color selectionColor;
  final Widget child;
 
  CircularSliderPaint(
      {@required this.intervals,
      @required this.init,
      @required this.end,
      this.child,
      @required this.onSelectionChange,
      @required this.baseColor,
      @required this.selectionColor});
 
  @override
  _CircularSliderState createState() => _CircularSliderState();
}
 
class _CircularSliderState extends State<CircularSliderPaint> {
  bool _isInitHandlerSelected = false;
  bool _isEndHandlerSelected = false;
 
  SliderPainter _painter;
 
  /// 用弧度制表示的起始角度,用来确定 init Handler 的位置。
  double _startAngle;
 
  /// 用弧度制表示的结束角度,用来确定 end Handler 的位置。
  double _endAngle;
 
  /// 用弧度制表示的选择区间的绝对角度(夹角)
  double _sweepAngle;
 
  @override
  void initState() {
    super.initState();
    _calculatePaintData();
  }
 
  // 我们需要使用 gesture detector 来更新此部件,
  // 当父部件重建自己时也是如此。
  @override
  void didUpdateWidget(CircularSliderPaint oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.init != widget.init || oldWidget.end != widget.end) {
      _calculatePaintData();
    }
  }
 
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanDown: _onPanDown,
      onPanUpdate: _onPanUpdate,
      onPanEnd: _onPanEnd,
      child: CustomPaint(
        painter: BasePainter(
            baseColor: widget.baseColor,
            selectionColor: widget.selectionColor),
        foregroundPainter: _painter,
        child: Padding(
          padding: const EdgeInsets.all(12.0),
          child: widget.child,
        ),
      ),
    );
  }
 
  void _calculatePaintData() {
    double initPercent = valueToPercentage(widget.init, widget.intervals);
    double endPercent = valueToPercentage(widget.end, widget.intervals);
    double sweep = getSweepAngle(initPercent, endPercent);
 
    _startAngle = percentageToRadians(initPercent);
    _endAngle = percentageToRadians(endPercent);
    _sweepAngle = percentageToRadians(sweep.abs());
 
    _painter = SliderPainter(
      startAngle: _startAngle,
      endAngle: _endAngle,
      sweepAngle: _sweepAngle,
      selectionColor: widget.selectionColor,
    );
  }
 
  _onPanUpdate(DragUpdateDetails details) {
    if (!_isInitHandlerSelected && !_isEndHandlerSelected) {
      return;
    }
    if (_painter.center == null) {
      return;
    }
    RenderBox renderBox = context.findRenderObject();
    var position = renderBox.globalToLocal(details.globalPosition);
 
    var angle = coordinatesToRadians(_painter.center, position);
    var percentage = radiansToPercentage(angle);
    var newValue = percentageToValue(percentage, widget.intervals);
 
    if (_isInitHandlerSelected) {
      widget.onSelectionChange(newValue, widget.end);
    } else {
      widget.onSelectionChange(widget.init, newValue);
    }
  }
 
  _onPanEnd(_) {
    _isInitHandlerSelected = false;
    _isEndHandlerSelected = false;
  }
 
  _onPanDown(DragDownDetails details) {
    if (_painter == null) {
      return;
    }
    RenderBox renderBox = context.findRenderObject();
    var position = renderBox.globalToLocal(details.globalPosition);
    if (position != null) {
      _isInitHandlerSelected = isPointInsideCircle(
          position, _painter.initHandler, 12.0);
      if (!_isInitHandlerSelected) {
        _isEndHandlerSelected = isPointInsideCircle(
            position, _painter.endHandler, 12.0);
      }
    }
  }
}

这里有几点需要注意:

  • 我们想要在 Handler(以及选择区间)的位置更新时通知父部件,这也是该部件对外暴露了一个回调函数 onSelectionChange() 的原因。
  • 当用户与滑块进行交互时,该部件需要被重新渲染,当起始位置的参数值改变时也需如此。这就是为什么我们有必要使用 didUpdateWidget() 方法。
  • CustomPaint 同样可以接收一个 child 参数,这样我们就可以使用它在圆的内部渲染生成一些其他东西。只需要在 final widget 里暴露相同的参数,使用者就可以向其中传入任何想要的值。
  • 我们使用一个间隔用以设置滑块的值。我们可以以此方便的将选择区间以百分比的形式表示。
  • 再一次申明,为了在百分比、弧度以及坐标之间转换我调用了不同的工具集函数。画布(Canvas)中的坐标系与一般坐标系有一些不同,比如说画布坐标系是以左上角作为坐标原点,这样一来 x、y 的值都将一直是一个正值。同样的,弧度制的表示是以 x 正坐标轴开始并以顺时针方向(总是正值)从 0 到 2*pi 计量。
  • 最后,Handler 的坐标计算以画布的原点为参考,而 GestureDetector 的坐标则是相对设备而言的,是全局的,因此我们需要用到 RenderBox.globalToLocal() 方法来对它们进行转换。该方法使用部件的 Context 作为参考。

有了这些,我们也就拥有了打造圆形滑块的一切需要。

额外的功能

由于篇幅有限,在这里并没有展开讲解所有的细节。你可以查看本项目的仓库,我会乐于回答评论中的任何问题。

在最终的版本里我添加了一些额外的功能,比如自定义选择区间和 Handler 的颜色;如果你想实现类似时钟的样式(小时和分钟)你可以根据需求进行选择。为了方便各位使用,我同样将所有内容打包放进了一个最终的部件内。

你也可以通过从 pub.dartlang.org/packages/fl… 导入本库的方式来使用这个部件。

文章至此告一段落,感谢各位的阅读!

 

flutter-circular-slider:可定制的Flutter圆形滑块 flutter_circular_slider Flutter的可自定义圆形滑块。 入门 安装 添加 flutter_circular_slider : ^lastest_version 到您的pubspec.yaml,然后运行 flutter packages get 在项目的根目录中。 基本用法 使用命令创建一个新项目 flutter create myapp 像这样编辑lib / main.dart: import 'package:flutter/material.dart' ; import 'package:flutter_circular_slider/flutter_circular_slider.dart' ; void main () => runApp ( MyApp ()); class MyApp extends StatelessWidget { @o 立即下载

相关推荐

电子产品的老化测试有哪些类型?

因此,越来越多的人在寻找“已知合格的测试连接器”,即经过测试具有可靠性和有效性的测试连接器,可以在老化测试中制定优化方法。例如对于芯片来说,不同的封装有不同的老化测试座,QFP老化测试座,QFN老化测试座等。老化测试时,专用老化电路板上的元件将承受等于或高于其额定工作条件的压力,以消除任何在额定寿命之前过早失效的元件。静态老化:在此类系统中,将测试设备安装到老化板上的插座中,然后放入老化箱中,其中每隔12至24小时施加电源和高温。动态老化:在动态老化系统中,设备以老化速度设定的最大速率进行测试。

软件技术的专栏 1836

[应用相关]基于STM32 平台开发的超低成本界面系统

在预览图中,左键点击刚才添加的控件,软件的最右侧属性栏,将显示该控件的所有可编辑。在属性栏里,可以自由编辑,大部分的属性,在修改后,可以立即看到预览效果。软件右下角给出正在编译的提示,几秒钟后,自动弹出模拟器,查看运行效果。在软件的左下角,有一个大纲视图,可以查看底窗口下包含的所有控件。默认新建的项目,只有一个白色的窗口,参照下一章节添加控件。在软件的左侧,项目资源管理器区域,找到新建的项目。选中项目名,右键,在弹出的菜单中,选择模拟器运行。找到项目资源管理器,双击指定的项目名,打开目录。

ic2121的博客 1872

安卓ai自动化脚本 Yyds.Auto实现 示例 纯ai图像识别自动点击脚本

纯ai识别实现自动化脚本

专注安卓前沿科技 4771

Flutter Slider自定义滑块样式 Slider的label标签框常显示

Flutter Slider自定义滑块样式 Slider的label标签框常显示和自定义样式;

Choi晨的博客 2689

SoC设计中的RDC陷阱:如何避免异步复位导致的亚稳态问题

本文深入探讨了SoC设计中由异步复位引发的复位域交叉(RDC)陷阱及其导致的亚稳态风险。通过分析实际案例,文章系统性地提出了以同步复位优先、异步复位同步释放为核心的设计原则,并介绍了电路级加固与验证策略,为构建稳健的SoC复位架构提供了实用指南。

weixin_29280657的博客 418

【深大计算机网络】实验三 可靠传输机制 实验报告 附代码

【内附代码】【深大计算机网络】实验三 可靠传输机制 实验报告 附代码

m0_74268508的博客 1650

Flutter圆形滑块:打造个性化交互体验

Flutter圆形滑块:打造个性化交互体验 项目介绍 flutter_circular_slider 是一个高度可定制的圆形滑块组件,专为Flutter应用设计。无论你是开发一个时间选择器、进度指示器,还是任何需要圆形交互的界面,这个组件都能轻松满足你的需求。它提供了单手柄和双手柄两种模式,支持丰富的自定义选项,让你的应用界面更加生动和用户友好。 项目技术分析 技术栈 Flutter: 作为Go...

gitblog_00045的博客 996

CY7C68013 C0模式启动

C0或是C2启动的目的,是更改USB芯片的ID信息,并把固件载入到芯片内部RAM中运行。C0模式是指只从EEPROM中载入USB ID信息,因为只有ID信息(8个字节),所以只需要一个小容量的EEPROM即可。当然大容量的EEPROM也可以使用C0模式,但是小容量的EEPROM只能使用C0模式启动。如下图所示: 使用小容量的EEPROM要将A0,A1,A2接地。 这里我用了个24LC01,容量1

Fezl的博客 1735

【亲测免费】 探索创新:Flutter上的精致圆环滑块与进度条组件

在寻找一个高度可定制的圆形滑块或进度条组件来提升你的Flutter应用用户体验吗?别再犹豫了,[Sleek circular slider/progress bar & spinner](https://github.com/MatejNuckowski/sleek_circular_slider) 是你的理想选择!它以其优雅的设计和无尽的自定义可能性,为你的应用带来卓越的交互体验。 ## 项目

gitblog_00014的博客 716

【卫星语音通信】神经网络语音编解码算法:AudioDec

最新研究表明,通过深度学习模型对语音信号进行智能压缩与重建,可在12 kbps码率下实现48 kHz采样率的高保真语音还原,其主观音质评分(MOS)可达3.9分(满分4.27分),远超传统参数编码技术。这意味着未来卫星电话的通话质量有望达到CD级水准,而蜂窝网络的语音业务带宽需求可降低60%以上,为蜂窝和卫星网络中的大规模物联网语音交互铺平道路。它的诞生为神经音频编解码技术带来了一次重要升级,有助于让未来实时语音交互更流畅、更清晰,也让我们进一步体验到人工智能与音频信号处理结合所带来的创新可能性。

u011376987的博客 1465

YOLOv11改进目录一览 | 涉及卷积层、轻量化、注意力、损失函数、Backbone、SPPF、Neck、检测头等全方位改进

如今各种网络模型更新迭代越来越快,计算机视觉相关的文章也越来越多,多到一些普通,通用的改进点无法达到发表的要求。本专栏正是解决这个问题!如何寻找创新点?为什么要使用这个模块?如何才能提升模型的精度?这是贯穿我们研究始终的问题。创新点在这个专栏中我已经整理好了,这已经省去了大部分时间,但是当我们使用这些新的模块去优化已有的模型,如何才能提升模型的精度,才是我们要达到的最终目标。当然我们可以使用传统的A+B+C的方法去堆积模块,然后是进行大量的实验去排列组合以实现最终的精度提升,这无可厚非。

Limiiiing的博客 6万+

Flutter Slider自定义:掌握Shapes机制打造专属滑块

在Flutter UI开发中,自定义控件是提升界面精致度的重要手段,而Slider组件恰恰是视觉定制中最容易让人卡壳的部分。很多开发者只知道修改颜色,却忽略了真正决定滑块外观的核心——SliderThemeData中的Shapes体系。该体系由trackShape、thumbShape、overlayShape等绘制器组成,通过继承SliderComponentShape并重写paint与getPreferredSize方法,即可彻底掌控滑块轨道、拇指球、波纹和数值气泡的每个像素。理解这一机制,不仅能解决设

weixin_33978722的博客 307

【LIN总线测试】——LIN主节点数据链路层测试

LIN总线是基于UART/SCI(通用异步收发器/串行接口)的低成本串行通讯协议。其目标定位于车身网络模块节点间的低端通信,主要用于智能传感器和执行器的串行通信,而这正是CAN总线的带宽和功能所不要求的部分。本章节主要介绍LIN主节点数据链路层如何测试?

测试领域技术分享 2606

Flutter-Neumorphic终极指南:10分钟构建3D风格Flutter应用

Flutter-Neumorphic是一个完整的、即用型的Neumorphic UI工具包,专为Flutter打造,支持深色模式,让开发者能够轻松构建具有现代3D风格的应用界面。本指南将帮助你快速掌握这个强大工具的使用方法,从零开始创建令人印象深刻的Neumorphic设计应用。 ## 什么是Neumorphic设计? Neumorphic设计(新拟态设计)是一种结合了拟物化和扁平化设计特点的

gitblog_00297的博客 507

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Flutter for OpenHarmony 颜色选择器功能开发指南

通过本文的学习,我们一起完成了Flutter for OpenHarmony颜色选择器功能的开发。从需求分析到技术架构,从核心功能实现到代码详解,我们系统地掌握了Flutter滑块组件、颜色处理、剪贴板操作等核心技术。颜色选择器应用虽然功能相对简单,但涵盖了Flutter开发的诸多要点。Slider组件的使用、RGB与十六进制的转换、Color类的应用、Clipboard与SnackBar的操作,这些都是Flutter开发中经常用到的知识点。

jiejiejiejie_的博客 98

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Android 自定义圆形 IndeterminateProgressBar(四):用 AnimatorSet 驱动圆弧伸缩与旋转动画

本篇文章是《IndeterminateProgressbar 解析》系列的第四部分,紧接第三篇中"用 Canvas + Paint 绘制静态圆弧"的自定义 Drawable,为本系列的自研圆形 IndeterminateProgressBar 注入真正的动画能力。本文将完整讲解如何通过 `IndeterminateAnimatorFactory` 工具类创建三个 `ObjectAnimator`,

gitblog_00457的博客 437

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Flutter Slider 组件隐藏刻度

Flutter 里的 Slider 组件类似 Android 里的 SeekBar 组件,中间有一个滑块,可以滑动选择进度。最近在使用 Slider 组件时确碰到一个问题,具体如下图所示:

xiaokangss的博客 852

[译] 教你如何用 Flutter 的 GestureDetector 构建自定义滑块

原文地址:Building a Custom Slider in Flutter with GestureDetector 原文作者:RJS Tech 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:ALVINYEH 校对者: Flutter 的一大优点是,可以轻松创建自定义 UI。在本教程中,我们将看到这一点。 首先,我们先停下来思考一下,需要...

weixin_34417814的博客 705

java --- BaseColor颜色处理(PDF)

用ITEXT做后端的PDF导出遇到一个颜色问题,单元格要填充颜色,是这样做的: PdfPCell cell = new PdfPCell(); //设置单元格颜色为灰色 cell.setBackgroundColor(new BaseColor(245, 245, 245)); 效果如下(左边是设置后的颜色,右边的是正常的白色): 色域代码对照: https://tool.oschina.net/commons?type=3 ...

Ryan_black的博客 3666

lite源码阅读(三)基本内容

1. lite_open 主要做一些初始化的工作,创建IDirectFB, IDirectFBDisplayLayer,事件缓冲,字体、光标初始化。 2. lite_init_window 这里创建一个主窗口,拥有其它控件的性质 3.  lite_wind

向开源软件的开发者们致敬 1574

Flutter图片裁剪

做app登陆的时候,需要用到图片裁剪功能,由于刚接触Flutter不久,所以就萌生了自己写一个的想法,说干就干!让我们站在巨人的肩膀上前进吧。 flutter中图片裁剪主要采用drawImageRect(Image image, Rect src, Rect dst, Paint paint)方法来从图片上抠图,介绍如下: /// Draws the subset of the given ima...

qq627578198的博客 7190

第一站小红书图片裁剪控件,深度解析大厂炫酷控件

xxqxq

文淑的博客 7001

手势识别官方教程(6)识别拖拽手势用GestureDetector.SimpleOnGestureListener和onTouchEvent...

三种现实drag方式  1,在3.0以后可以直接用 View.OnDragListener (在onTouchEvent中调用某个view的startDrag())  2,onTouchEvent()  3,GestureDetector.SimpleOnGestureListener 的onScroll Dragging   This lesson describes ...

banyinlve3147的博客 341

个人学习笔记——庄懂的技术美术入门课(美术向)12

个人学习笔记——庄懂的技术美术入门课(美术向)121 作业示范2 答疑 该课是在13课之后上的,所以可以先去回顾第13课 1 作业示范 没有标准答案,按照过程做完shader就过关了 2 答疑

清清!的博客 2484
上一篇: Flutter UI基础 - 点击展开和关闭
下一篇: Flutter UI基础 - GridView
当走的路甚远
博客等级 码龄15年 149粉丝 233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值