亲宝软件园·资讯

展开

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

SoaringHeart 人气:0

一、问题来源

项目中遇到 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

加载全部内容

相关教程
猜你喜欢
用户评论