04_Flutter自定义Slider滑块

04_Flutter自定义Slider滑块

一.Slider控件基本用法
Column(
  mainAxisAlignment: MainAxisAlignment.start,
  children: <Widget>[
    Text(
      "sliderValue: ${_sliderValue.toInt()}"
    ),
    Slider(
      value: _sliderValue,
      min: 0,
      max: 100,
      divisions: 10,
      thumbColor: Colors.red,
      activeColor: Colors.red,
      onChanged: (value) {
        setState(() {
          _sliderValue = value;
        });
      }
    )
  ],
)

在这里插入图片描述

const Slider({
    super.key,
    required this.value,
    this.secondaryTrackValue,
    required this.onChanged,
    this.onChangeStart,
    this.onChangeEnd,
    this.min = 0.0,
    this.max = 1.0,
    this.divisions,
    this.label,
    this.activeColor,
    this.inactiveColor,
    this.secondaryActiveColor,
    this.thumbColor,
    this.overlayColor,
    this.mouseCursor,
    this.semanticFormatterCallback,
    this.focusNode,
    this.autofocus = false,
  })

几个比较重要的属性:

  • value:slider控件显示的值
  • min:slider控件滑动到最左边对应的值,即最小值
  • max: slider控件滑动到最右边对应的值,即最大值
  • divisions: 最小值到最大值之间被几等分
  • activeColor: 滑块划过部分的颜色值,即选中的颜色值
  • inactiveColor:滑块未划过部分的颜色值,即为选中的颜色值
  • thumbColor:滑块的颜色值
二.如何修改滑块的大小以及滑块轨迹的高度

从上面的示例可以看到,通过Slider控件为我们提供的属性,只支持改变滑块的颜色,以及滑块轨迹的颜色,那么我们想要改变滑块的大小以及滑块轨迹的高度,是不是只能重新自定义呢?

NO! NO! NO!,细心的您在使用Flutter的AppBar时,可能会发现,为AppBar控件指定样式时,除了使用AppBar控件提供的属性外,也可以使用AppBarTheme来为AppBar设置某些特定的样式,既然如此,不妨查看下Flutter sdk的源码与Slider对应的是否有一个叫SliderTheme的控件呢? 嘿嘿,还真有。

final SliderThemeData data;
const SliderTheme({
  super.key,
  required this.data,
  required super.child,
});

const SliderThemeData({
  this.trackHeight,
  this.thumbShape,
  ...
});

仔细找SliderThemeData的trackHeight以及thumbShape的属性注释:

/// The height of the [Slider] track.
final double? trackHeight;

/// The shape that will be used to draw the [Slider]'s thumb.
/// The default value is [RoundSliderThumbShape].
final SliderComponentShape? thumbShape;

此处省略…翻译软件的时间:

  • trackHeight:[滑块]轨迹的高度
  • thumbShape:默认值是一个RoundSliderThumbShape对象

看下RoundSliderThumbShape的源码怎么写的:

const RoundSliderThumbShape({
  this.enabledThumbRadius = 10.0,
  this.disabledThumbRadius,
  this.elevation = 1.0,
  this.pressedElevation = 6.0,
});

看到这里就不用做过多的解释了吧😂,因此要修改滑块的大小,可以重新指定thumbShape为RoundSliderThumbShape对象,并设置enabledThumbRadius的值。

在这里插入图片描述

Column(
  mainAxisAlignment: MainAxisAlignment.start,
  children: <Widget>[
    Text(
      "sliderValue: ${_sliderValue.toInt()}"
    ),
    SliderTheme(
      data: const SliderThemeData(
        trackHeight: 20,
        thumbShape: RoundSliderThumbShape(
          enabledThumbRadius: 20
        )
      ),
      child: Slider(
        value: _sliderValue,
        min: 0,
        max: 100,
        divisions: 10,
        thumbColor: Colors.red,
        activeColor: Colors.red,
        onChanged: (value) {
          setState(() {
            _sliderValue = value;
          });
        }
      )
    )
  ],
)
三.使用本地资源图片作为自定义滑块

既然要自定义滑块,毫无疑问需要从SliderThemeData的thumbShape入手。

final SliderComponentShape? thumbShape;

thumbShape的类型为SliderComponentShape,继续查看SliderComponentShape源码:

abstract class SliderComponentShape {

  const SliderComponentShape();

  Size getPreferredSize(bool isEnabled, bool isDiscrete);

  void paint(
    PaintingContext context,
    Offset center, {
    required Animation<double> activationAnimation,
    required Animation<double> enableAnimation,
    required bool isDiscrete,
    required TextPainter labelPainter,
    required RenderBox parentBox,
    required SliderThemeData sliderTheme,
    required TextDirection textDirection,
    required double value,
    required double textScaleFactor,
    required Size sizeWithOverflow,
  });
}

因此我们可以定义一个类继承SliderComponentShape,并实现getPreferredSize和paint方法,getPreferredSize控制滑块大小,paint负责把滑块绘制到屏幕上。

  • 首先第一步我们需要将本地图片为一个ImageInfo,例如传入一个"lib/assets/images/ic_slider_thumb.png",最后得到一个ImageInfo,这里就直接奉上源码了,其实现也是参考了Image.asset的源码:
typedef AssertsWidgetBuilder = Widget Function(BuildContext context, ImageInfo? imageInfo);

class AssertsImageBuilder extends StatefulWidget {

  final String assertsName;
  final AssertsWidgetBuilder builder;

  const AssertsImageBuilder(
    this.assertsName,
    {
      super.key,
      required this.builder,
    }
  );

  
  State<StatefulWidget> createState() => _AssertsImageBuilderState();

}

class _AssertsImageBuilderState extends State<AssertsImageBuilder> {

  ImageInfo? _imageInfo;

  
  void initState() {
    super.initState();
    _loadAssertsImage().then((value) {
      setState(() {
        _imageInfo = value;
      });
    });

  }

  
  void didUpdateWidget(covariant AssertsImageBuilder oldWidget) {
    super.didUpdateWidget(oldWidget);
    if(oldWidget.assertsName != widget.assertsName) {
      _loadAssertsImage().then((value) {
        setState(() {
          _imageInfo = value;
        });
      });
    }
  }

  
  Widget build(BuildContext context) {
    return widget.builder!.call(context, _imageInfo);
  }

  Future<ImageInfo?> _loadAssertsImage() {
    final Completer<ImageInfo?> completer = Completer<ImageInfo?>();
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      final ImageProvider imageProvider = AssetImage(widget.assertsName);
      final ImageConfiguration config = createLocalImageConfiguration(context);
      final ImageStream stream = imageProvider.resolve(config);
      ImageStreamListener? listener;
      listener = ImageStreamListener(
            (ImageInfo? image, bool sync) {
          if (!completer.isCompleted) {
            completer.complete(image);
          }

          SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
            stream.removeListener(listener!);
          });
        },
        onError: (Object exception, StackTrace? stackTrace) {
          stream.removeListener(listener!);
          completer.completeError(exception, stackTrace);
        },
      );
      stream.addListener(listener);
    });

    return completer.future;
  }

}
  • 自定义SliderComponentShape
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

class ImageSliderThumb extends SliderComponentShape {

  final Size size;
  final ui.Image? image;

  const ImageSliderThumb({
    required this.image,
    Size? size
  }): size = size ?? const Size(20, 20);

  
  Size getPreferredSize(bool isEnabled, bool isDiscrete) {
    return size;
  }

  
  void paint(PaintingContext context, Offset center, {required Animation<double> activationAnimation, required Animation<double> enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow}) {
    
  }

}
  • 绘制图片滑块

void paint(PaintingContext context, Offset center, {required Animation<double> activationAnimation, required Animation<double> enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow}) {
  //图片中心点
  double dx = size.width/2;
  double dy = size.height/2;

  if(image != null) {
    final Rect sourceRect = Rect.fromLTWH(0, 0, image!.width.toDouble(), image!.width.toDouble());
    //center会随着滑块的移动而改变,所以这里需要根据center计算图片绘制的位置
    double left = center.dx - dx;
    double top = center.dy - dy;
    double right = center.dx + dx;
    double bottom = center.dy + dy;
    Rect destinationRect = Rect.fromLTRB(left, top, right, bottom);

    final Canvas canvas = context.canvas;
    final Paint paint = new Paint();
    paint.isAntiAlias = true;
    //绘制滑块
    canvas.drawImageRect(image!, sourceRect, destinationRect, paint);
  }
}
四.怎么使用?
Column(
  mainAxisAlignment: MainAxisAlignment.start,
  children: <Widget>[
    Text(
      "sliderValue: ${_sliderValue.toInt()}"
    ),
    AssertsImageBuilder(
      "lib/assets/images/ic_slider_thumb.png",
      builder: (context, imageInfo) {
        return SliderTheme(
          data: SliderThemeData(
            trackHeight: 10,
            thumbShape: ImageSliderThumb(
              image: imageInfo?.image,
              size: const Size(30, 30)
            )
          ),
          child: Slider(
            value: _sliderValue,
            min: 0,
            max: 100,
            divisions: 10,
            thumbColor: Colors.red,
            activeColor: Colors.red,
            onChanged: (value) {
              setState(() {
                _sliderValue = value;
              });
            }
          )
        );
      }
    ),
  ],
)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/198063.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《微信小程序开发从入门到实战》学习三十四

4.2 云开发JSON数据库 MySQL、Oracle之类的“关系型数据库”。JSON数据库是“非关系型数据库”&#xff0c;没有行表列的概念。 4.2.1 JSON数据库基本概念 集合:一个数据库有多个集合&#xff0c;一个集合存储通常是同一类数据&#xff0c;可看作为JSON数组&#xff0c;数组…

webpack具体实现--未完

1、前端模块打包工具webpack webpack 是 Webpack 的核心模块&#xff0c;webpack-cli 是 Webpack 的 CLI 程序&#xff0c;用来在命令行中调用 Webpack。webpack-cli 所提供的 CLI 程序就会出现在 node_modules/.bin 目录当中&#xff0c;我们可以通过 npx 快速找到 CLI 并运行…

Chrome 访问不了项目?10080端口 ERR_UNSAFE_PORT:问题原因 / 解决方案

文章目录 被禁用端口列表解决方法方法一、更换端口 / 使用代理 / 使用域名方法二、对浏览器下手WindowsMac 最近有客户反馈&#xff0c;在chrome浏览器中访问不了项目&#xff0c;其他浏览器都是正常的。 &#xff1f;奇了怪了&#xff0c;难道客户对chrome做了什么操作&#x…

Asp.net core WebApi 配置自定义swaggerUI和中文注释

1.创建asp.net core webApi项目 默认会引入swagger的Nuget包 <PackageReference Include"Swashbuckle.AspNetCore" Version"6.2.3" />2.配置基本信息和中文注释&#xff08;默认是没有中文注释的&#xff09; 2.1创建一个新的controller using Micr…

直接插入排序和希尔排序

前言 我们前面几期介绍了线性和非线性的基本数据结构。例如顺序表、链表、栈和队列、二叉树等~&#xff01;本期和接下来的几期我们来详解介绍各个排序的概念、实现以及性能分析&#xff01; 本期内容 排序的概念以及其运用 常见的排序算法 直接插入排序 希尔排序 一、排序的…

Leetcode算法系列| 3. 无重复字符的最长子串

目录 1.题目2.题解C# 解法一&#xff1a;滑动窗口算法C# 解法二&#xff1a;索引寻找Java 解法一&#xff1a;滑动窗口算法Java 解法二&#xff1a;遍历字符串 1.题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例1: 输入: s "ab…

力扣141-环形链表

文章目录 力扣141-环形链表示例代码实现要点剖析 力扣141-环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测…

源码剖析 Spring Security 的实现原理

Spring Security 是一个轻量级的安全框架&#xff0c;可以和 Spring 项目很好地集成&#xff0c;提供了丰富的身份认证和授权相关的功能&#xff0c;而且还能防止一些常见的网络攻击。我在工作中有很多项目都使用了 Spring Security 框架&#xff0c;但基本上都是浅尝辄止&…

C语言——输入两个正整数 m 和 n。求其最大公约数和最小公倍数。

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int m, n;int i;int x 1;int y 0;printf("请输入两个正整数m和n&#xff1a;\n");scanf("%d,%d", &m, &n);for (i 1; i < m && i < n; i) {if (m % i 0 …

【doccano】文本标注工具——安装运行教程

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 【doccano】文本标注工具 doccano简介安装doccano1. 创建并激活虚拟环境2. 安装doccano 运行Doccano访问Doccano doccano简介 doccano是一个开源的文本注释工具。它为文本分类、序列标记和序列到序列任务提供注释…

Axios 并发请求指南 - 3 种简单实用的方法

在实际开发中&#xff0c;我们经常需要同时发送多个请求&#xff0c;并在所有请求完成后进行处理&#xff0c;这就是所谓的并发请求。实现 Axios 并发请求的关键是使用 Axios.all 方法&#xff0c;它接受一个 Promise 的数组作为参数&#xff0c;当这些 Promise 都 resolve 时&…

【C++】杨辉三角详解和C++代码示例

杨辉三角的每行第i个数是由上一行的第i-1个数和第i个数相加得到的&#xff0c;且每行的第一个数和最后一个数都是1&#xff0c;每行的中间个数等于它两肩上的数字相加。 目录 C代码输出结果8行输出15行输出25行输出 C代码 #include <iostream> #include <vector>…

Python Selenium 图片资源自动搜索保存 项目实践

实现访问首页 from os.path import dirnamefrom selenium import webdriverclass ImageAutoSearchAndSave:"""图片自动搜索保存"""def __init__(self):"""初始化"""self.driver webdriver.Chrome(executable_pa…

STK Components 二次开发- 卫星地面站

前期卫星地面站创建已经说过&#xff0c;本次说一下卫星和地面站可见性时卫星名称和轨迹线变色问题。 1.创建卫星 // Get the current TLE for the given satellite identifier. var tleList TwoLineElementSetHelper.GetTles(m_satelliteIdentifier, JulianDate.Now);// Us…

【VRTK】【VR开发】【Unity】9-瞬移

课程配套学习资源下载 https://download.csdn.net/download/weixin_41697242/88485426?spm=1001.2014.3001.5503 【移动的种类】 瞬移只是VR中移动的一种种类,其它还有连续移动,物理移动,摔臂移动等等。 瞬移自身也有多个分类,本篇介绍: 即时瞬移冲刺瞬移定点瞬移【瞬…

Linux CentOS_7解决无法上网的问题

参考视频&#xff1a;保姆式教学虚拟机联网liunx(centos)_哔哩哔哩_bilibili 配置网络&#xff1a;解决上网问题 第一步&#xff1a;选择网络模式 第二步&#xff1a;配置网卡命令&#xff1a;打开终端执行命令&#xff1a; 1、先切换到根目录下&#xff0c;防止在第执行cd …

在Mysql中,什么是回表,什么是覆盖索引,索引下推?

一、什么是回表查询&#xff1f; 通俗的讲就是&#xff0c;如果索引的列在 select 所需获得的列中&#xff08;因为在 mysql 中索引是根据索引列的值进行排序的&#xff0c;所以索引节点中存在该列中的部分值&#xff09;或者根据一次索引查询就能获得记录就不需要回表&#x…

进程和线程的关系

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;JavaEE &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 进程&线程 1. 什么是进程PCB 2. 什么是…

基于SSM的论文管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

位运算算法【1】

文章目录 &#x1f34a;面试题 01.01. 判定字符是否唯一&#x1f96d;题目&#x1f351;算法原理&#x1f95d;解法一&#xff1a;哈希表&#x1f95d;解法二&#xff1a;位图 &#x1f951;代码实现 &#x1f33d;268. 丢失的数字&#x1f96c;题目&#x1f344;算法原理&…