Flutter开发技巧RadialGradient中radius计算详解

目录
  • 一、问题来源
  • 二、四种情况
    • 1、情况一
    • 2、情况二
    • 3、情况三
    • 4、情况四
  • 三、实现源码
  • 四、radiusOfRadialGradient 方法实现
  • 最后

一、问题来源

项目中遇到 json 模型映射成 RadialGradient 组件的需求,其他参数正常传递即可;

唯独 radius 参数效果有出入,总结记录一下;

二、四种情况

通过 RadialGradient 参数 center 可以分为四种情况,这四种情况分别对应四种 radius 的值:

1、情况一

Alignment.center,

贪婪模式下组件的宽高中最大值的一半/最小值的一半为 radius;否则radius是0.5;

【贪婪模式】

p>【非贪婪模式】

2、情况二

Alignment.centerLeft,
Alignment.centerRight,

【贪婪模式】

【非贪婪模式】

3、情况三

Alignment.topCenter,
Alignment.bottomCenter,

【贪婪模式】

【非贪婪模式】

4、情况四

Alignment.topLeft,
Alignment.topRight,
Alignment.bottomLeft,
Alignment.bottomRight,

【贪婪模式】

【非贪婪模式】

【使用对角线半径】(注意左下角的一点点留白,基本实现全覆盖)

三、实现源码

GradientOfRadialDemo

import 'package:flutter/material.dart';
import 'package:flutter_templet_project/basicWidget/SectionHeader.dart';
import 'package:flutter_templet_project/extension/alignment_ext.dart';
import 'package:tuple/tuple.dart';
class GradientOfRadialDemo extends StatefulWidget {
  GradientOfRadialDemo({ Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _GradientOfRadialDemoState createState() => _GradientOfRadialDemoState();
}
class _GradientOfRadialDemoState extends State<GradientOfRadialDemo> {
  var maxWidth = double.infinity;
  var maxHeight = double.infinity;
  /// 是否是贪婪模式
  var isGreed = true;
  /// 是否使用对角线做半径
  bool isDiagonal = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title ?? "$widget"),
          bottom: buildAppBottom(),
        ),
        body: Container(
          // height: 300,
          child: buildRadial(),
        ),
        // body: ListView(
        //   children: [
        //     SectionHeader.h4(title: 'RadialGradient',),
        //     buildRadial(),
        //   ],
        // )
    );
  }
  buildAppBottom() {
    return PreferredSize(
      preferredSize: Size(double.infinity, 50),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          _buildDropdownButton(),
          _buildButton(
            text: "isGreed: ${isGreed.toString()}",
            onPressed: () {
              this.isGreed = !this.isGreed;
              setState(() {});
            },
          ),
          _buildButton(
            text: "isDiagonal: ${isDiagonal.toString()}",
            onPressed: () {
              this.isDiagonal = !this.isDiagonal;
              setState(() {});
            },
          ),
        ],
      )
    );
  }
  var _dropValue = AlignmentExt.allCases[0];
  var _radius = 0.5;
  _buildDropdownButton() {
    return DropdownButton<Alignment>(
      value: _dropValue,
      items: AlignmentExt.allCases.map((e) => DropdownMenuItem(
        child: Text(e.toString().split('.')[1]),
        value: e,
      ),
      ).toList(),
      onChanged: (Alignment? value) {
        if (value == null) return;
        _dropValue = value;
        setState(() {});
      },
    );
  }
  _buildButton({
    required String text,
    required VoidCallback onPressed
  }) {
    return TextButton(
      onPressed: onPressed,
      child: Center(
          child: Text(text,
            style: TextStyle(color: Colors.white),
          )
      ),
    );
  }
  Widget _buildBox({
    required String text,
    required Decoration decoration,
    double height: 100,
    double? width,
  }) {
    return LayoutBuilder(
      builder: (context, constraints) {
        this.maxWidth = constraints.maxWidth;
        this.maxHeight = constraints.maxHeight;
        return Container(
          // width: width,
          // height: height,
          margin: const EdgeInsets.all(8.0),
          decoration: decoration,
          alignment: Alignment.center,
          child: Text(text, style: TextStyle(color: Colors.white, fontSize: 16.0)),
        );
      }
    );
  }
  buildRadial() {
    var tuples = <Tuple2<Color, double>>[      Tuple2(Colors.red, 0.1),      Tuple2(Colors.blue, 0.3),      Tuple2(Colors.yellow, 0.5),      Tuple2(Colors.green, 1),    ];
    _radius = _dropValue.radiusOfRadialGradient(
      width: this.maxWidth,
      height: this.maxHeight,
      isGreed: this.isGreed,
      isDiagonal: this.isDiagonal
    ) ?? 0.5;
    print("_dropValue:${_dropValue} _radius:${_radius} maxWidth:${maxWidth} maxHeight:${maxHeight}");
    print("_radius: $_radius");
    return _buildBox(
      height: 100,
      text: 'RadialGradient',
      decoration: BoxDecoration(
        border: Border.all(),
        gradient: RadialGradient(
          // tileMode: this.tileMode,
          // tileMode: TileMode.mirror,
          radius: _radius,
          tileMode: TileMode.decal,
          center: _dropValue,
          // focal: Alignment.bottomCenter,
          colors: tuples.map((e) => e.item1).toList(),
          stops: tuples.map((e) => e.item2).toList(),
        ),
      ),
    );
  }
}

四、radiusOfRadialGradient 方法实现

//  AlignmentExtension.dart
//  flutter_templet_project
//
//  Created by shang on 2023/1/12 20:57.
//  Copyright  2023/1/12 shang. All rights reserved.
//
import 'dart:math' as math;
import 'package:flutter/cupertino.dart';
extension AlignmentExt on Alignment{
  /// 获取雷达渐进色 radius
  /// isGreed 是否贪婪模式(贪婪模式用大半径,否则小半径)
  /// isDiagonal 四角是否使用对角线(为 true 则 isGreed 参数无效)
  double? radiusOfRadialGradient({
    required double? width,
    required double? height,
    bool isGreed = true,
    bool isDiagonal = true,
  }) {
    if(width == null || height == null
        || width <= 0 || height <= 0) {
      return null;
    }
    final max = math.max(width, height);
    final min = math.min(width, height);
    double result = 0.5;
    if([
      Alignment.center,
    ].contains(this)){
      result = isGreed == true ? max/min * 0.5 : 0.5;
    } else if ([
      Alignment.topCenter,
      Alignment.bottomCenter,
    ].contains(this)) {
      result = isGreed == true ? max/min : 0.5;
    } else if ([
      Alignment.topLeft,
      Alignment.topRight,
      Alignment.bottomLeft,
      Alignment.bottomRight
    ].contains(this)) {
      if (isDiagonal) {
        final tmp = math.sqrt(math.pow(max, 2) + math.pow(min, 2)).ceil();
        // result = isGreed == true ? tmp/min : max/min;
        result = tmp/min;
      } else {
        result = isGreed == true ? max/min : 1;
      }
    } else if ([
      Alignment.centerLeft,
      Alignment.centerRight,
    ].contains(this)) {
      result = isGreed == true ? 1 : max/min * 0.5;
    }
    return result;
  }
}

最后

项目中为了方便查看差异使用了 TileMode.decal 模式,正常使用默认模式即可;

github

以上就是Flutter小技巧RadialGradient 中 radius 的计算的详细内容,更多关于Flutter RadialGradient radius计算的资料请关注我们其它相关文章!

(0)

相关推荐

  • flutter开发技巧自定页面指示器PageIndicator详解

    目录 一.来源 二.效果 三.源码实现 1.flutter_swiper_null_safety 使用示例: 2.PageIndicatorWidget 指示器源码: 三.总结 一.来源 项目中遇到多个需要自定义轮播图指示器的需求,封装成基础组件方便复用: 原理是通过 ValueListenableBuilder 实时监听轮播图的当前索引,然后更新指示器组件,达到最终效果: 二.效果 三.源码实现 1.flutter_swiper_null_safety 使用示例: import 'packag

  • Android flutter Dio锁的巧妙实现方法示例

    正文 看Dio库源码的时候,发现其拦截器管理的逻辑处用到了一个Lock,这个Lock巧妙地利用了Completer和Future的机制来实现,记录一下. /// Add lock/unlock API for interceptors. class Lock { Future? _lock; late Completer _completer; /// 标识拦截器是否被上锁 bool get locked => _lock != null; /// Lock the interceptor. /

  • Flutter Zone异常处理方法及基本原理

    目录 1. 认识Zone 1.1 ZoneValues 1.2 ZoneSpecification 1.3 通过runZoned快速创建Zone 2. 异步基本原理和异常捕获 3. HandleUncaughtErrorHandler 1. 认识Zone Zone像一个沙盒,是我们代码执行的一个环境. 我们的main函数默认就运行在Root Zone当中. 子Zone的构造有点像Linux中的进程,它支持从当前的Zone中Fork出一个子Zone: Zone myZone = Zone.curr

  • 替换so文件来动态替换Flutter代码实现详解

    目录 一.Flutter代码的启动起点 1.1 initTask对象 1.2 ResourceExtractor 1.3 FlutterJNI#loadLibrary 二.ensureInitializationComplete 2.1 ShellArgs 三.实践:自定义libapp.so的加载 3.1 flutterApplicationInfo和FlutterActivity#getShellArgs() 一.Flutter代码的启动起点 我们在多数的业务场景下,使用的都是FlutterA

  • Flutter有状态组件StatefulWidget生命周期详解

    目录 1.StatefulWidget的背后 2.StatefulWidget的生命周期 2.1创建阶段 2.2更新阶段 2.3销毁阶段 总结: 1.StatefulWidget的背后 flutter开发过程中,我们经常会用到两个组件StatelessWidget和StatefulWidget.前者为无状组件,后者为有状态组件,无状态组件通常在创建后内部的数据无法改变,而有状态组件可以维持内部的数据状态,适合动态组件使用. /// 无状态组件的定义 class MyApp extends Sta

  • 详解Flutter中key的正确使用方式

    目录 1.什么是key 2.key的更新原理 3.key的分类 GlobalKey LocalKey 总结 1.什么是key Widget中有个可选属性key,顾名思义,它是组件的标识符,当设置了key,组件更新时会根据新老组件的key是否相等来进行更新,可以提高更新效率.但一般我们不会去设置它,除非对某些具备状态且相同的组件进行添加.移除.或者排序时,就需要使用到key,不然就会出现一些莫名奇妙的问题. 例如下面的demo: import 'dart:math'; import 'packag

  • Flutter Widget移动UI框架使用Material和密匙Key实战

    目录 Flutter Material 更完整的示例 Key Flutter Flutter是谷歌的移动UI框架,可以在IOS和Android上快速构建高质量的本地用户界面.Flutter可以使用现有代码. 在世界上,Flutter正被越来越多的开发人员和组织使用,Flutter是完全免费和开源的.这也是构建未来Google Fuchsia应用程序的主要方式. import 'package:flutter/material.dart'; void main() { runApp( new Ce

  • flutter布局约束原理深入解析

    目录 引言 1.flutter的widget类型 2.Container是个组合类 3.flutter布局约束 4.Container布局行为解惑 总结 引言 刚开始接触flutter的时候,Container组件是用得最多的.它就像HTML中的div一样普遍,专门用来布局页面的. 但是使用Container嵌套布局的时候,经常出现一些令人无法理解的问题.就如下面代码,在一个固定的容器中,子组件却铺满了全屏. /// 例一 @override Widget build(BuildContext

  • Flutter开发技巧RadialGradient中radius计算详解

    目录 一.问题来源 二.四种情况 1.情况一 2.情况二 3.情况三 4.情况四 三.实现源码 四.radiusOfRadialGradient 方法实现 最后 一.问题来源 项目中遇到 json 模型映射成 RadialGradient 组件的需求,其他参数正常传递即可: 唯独 radius 参数效果有出入,总结记录一下: 二.四种情况 通过 RadialGradient 参数 center 可以分为四种情况,这四种情况分别对应四种 radius 的值: 1.情况一 Alignment.cen

  • Flutter开发setState能否在build中直接调用详解

    目录 两种情况 原理分析 总结 两种情况 setState() 能在 build() 中直接调用吗?答案是能也不能. 来看一段简单的代码: import 'package:flutter/material.dart'; class TestPage extends StatefulWidget { const TestPage({super.key}); @override State<TestPage> createState() => _State(); } class _State

  • Flutter开发技巧ListView去除水波纹方法示例

    正文 ScrollConfiguration( behavior: NoScrollBehaviorWidget(), child: ListView( ...... ...... ), ), 调用ScrollConfiguration官方类,实现behavior NoScrollBehaviorWidget用于去除水波纹的自定义Widget import 'package:flutter/material.dart'; /// 去除listview水印 /// ScrollConfigurat

  • Flutter利用Canvas绘制精美表盘效果详解

    目录 前言 初始化 面板 刻度 刻度线 刻度值 指针 时针 分针 秒针 动起来 前言 趁着周末空闲时间使用 Flutter 的 Canvas制作了一个精美表盘. 最终实现的效果还不错,如下: 前面说到使用 Canvas 实现该表盘效果,而在 Flutter 中使用 Canvas 更多的则是继承 CustomPainter 类实现 paint 方法,然后在 CustomPaint 中使用自定义实现的 CustomPainter. 比如这里创建的 DialPainter 使用如下: @overrid

  • Android开发使用HttpURLConnection进行网络编程详解【附源码下载】

    本文实例讲述了Android开发使用HttpURLConnection进行网络编程.分享给大家供大家参考,具体如下: --HttpURLConnection URLConnection已经可以非常方便地与指定站点交换信息,URLConnection下还有一个子类:HttpURLConnection,HttpURLConnection在URLConnection的基础上进行改进,增加了一些用于操作HTTP资源的便捷方法. setRequestMethod(String):设置发送请求的方法 get

  • Flutter应用框架搭建之屏幕适配详解

    目录 原理 flutter_screenutil 添加依赖 初始化 使用 效果 其他 Api 因移动设备的多样性,特别是 Android 的碎片化严重,存在各种各样的分辨率,而 Flutter 跨平台开发又需同时支持 Android 和 iOS ,为尽可能的还原设计图效果提升用户体验,屏幕适配就势在必行了. Flutter 暂时没有官方的屏幕适配方案,在 Flutter 项目开发中目前大部分的适配方案都是通过比例来进行适配,是一个通用的适配方法,该适配方法也在前端.Android.iOS.小程序

  • spring在IoC容器中装配Bean详解

    1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean实例放入bean缓存池: 应用程序使用bean. 1.2.基于xml的配置 (1)xml文件概述 xmlns------默认命名空间 xmlns:xsi-------标准命名空间,用于指定自定义命名空间的schema文件 xmlns:xxx="aaaaa"-------自定义命名空间,xx

  • Android开发使用URLConnection进行网络编程详解

    本文实例讲述了Android开发使用URLConnection进行网络编程.分享给大家供大家参考,具体如下: URL的openConnection()方法将返回一个URLConnection,该对象表示应用程序和URL之间的通信连接,程序可以通过URLConnection实例向该URL发送请求,读取URL引用的资源.通常创建一个和URL的连接,并发送请求,读取此URL引用的资源. 需要如下步骤: a)通过调用URL对象openConnection()方法来创建URLConnection对象 b)

  • redis配置文件中常用配置详解

    此次安装的版本为: 5.0.3 [root@localhost local]# redis-server --version Redis server v=5.0.3 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=afabdecde61000c3 打开redis.cof NETWORK # 指定 redis 只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求 bind 127.0.0.1 #是否开启保护模式,默认开启.要是配置

随机推荐