Flutter 颜色渐变及模仿淘宝渐变关注按钮

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 颜色渐变及模仿淘宝渐变关注按钮相关的知识,希望对你有一定的参考价值。

一、线性颜色渐变

实现一个容器,大多使用 Container Widget,边框 border 通过 decoration 属性控制,而 BoxDecoration 除了支持 border 的控制外,还支持 gradient 的控制。

  const BoxDecoration(
    this.color,
    this.image,
    this.border,
    this.borderRadius,
    this.boxShadow,
    this.gradient,
    this.backgroundBlendMode,
    this.shape = BoxShape.rectangle,
  )

一般情况下都是使用线性渐变,Fultter 中通过 LinearGradient 来支持实现线性渐变

// 线性渐变
Container(
  width: 200,
  height: 100,
  decoration: BoxDecoration(
    gradient: LinearGradient(
      // Alignment(x,y)  (0,0) 是由 rectangle 中心点计算的
      begin: Alignment(0.0, -1.0),
      end: Alignment(0.0, 1.0),
      colors: <Color>[
        Color(0xFFFF5000),
        Color(0xFFFF9000),
        Colors.black,
      ],
    ),
  ),
),

LinearGradient 定义如下:

  const LinearGradient(
    this.begin = Alignment.centerLeft,
    this.end = Alignment.centerRight,
    @required List<Color> colors,
    List<double> stops,
    this.tileMode = TileMode.clamp,
  )

colors 可以传入多个颜色,用来实现渐变

默认值是 Aligment.centerLeft 和 Aligment.centerRight

关于 Aligment 默认属性和 (x,y) 坐标的关系简单如下:

  /// The top left corner.
  static const Alignment topLeft = Alignment(-1.0, -1.0);

  /// The center point along the top edge.
  static const Alignment topCenter = Alignment(0.0, -1.0);

  /// The top right corner.
  static const Alignment topRight = Alignment(1.0, -1.0);

  /// The center point along the left edge.
  static const Alignment centerLeft = Alignment(-1.0, 0.0);

  /// The center point, both horizontally and vertically.
  static const Alignment center = Alignment(0.0, 0.0);

  /// The center point along the right edge.
  static const Alignment centerRight = Alignment(1.0, 0.0);

  /// The bottom left corner.
  static const Alignment bottomLeft = Alignment(-1.0, 1.0);

  /// The center point along the bottom edge.
  static const Alignment bottomCenter = Alignment(0.0, 1.0);

  /// The bottom right corner.
  static const Alignment bottomRight = Alignment(1.0, 1.0);

Flutter 中 View(典型表现是 Container Widget)中心点(0,0)实际上是真正的中心位置,而不是平常理解的 topLeft 这个点

所以上面渐变的效果如下:

二、横向线性渐变

上面实现的是纵向线性渐变,如果要使用横向的线性渐变,其实就是将 start 和 begin 的点改变一下:

Container(
  width: 200,
  height: 50,
  decoration: BoxDecoration(
    gradient: LinearGradient(
      begin: Alignment(-1, 0),
      end: Alignment(1.0, 0),
      colors: <Color>[
        const Color(0xFFFF5000),
        const Color(0xFFFF9000),
      ],
    ),
  ),
),

Alignment(-1, 0) 表示左边中间,Alignment(1.0, 0) 表示右边中间

效果:

三、模拟手机淘宝的关注按钮

 

这个关注按钮核心是一个 radius 的形状以及横向的线性颜色渐变

从 iconfont 找到一个微淘的关注图标(白色的,这里看不见):

 尴尬的是这个图标是白色的,贴在这里看不出来。搞个背景色如下所示:

颜色渐变分析下来是:const Color(0xFFFF5000), const Color(0xFFFF9000)

除此之外,点击关注后,变成已关注的文案,并且可以多次改变状态

实现:

class FollowWidget extends StatefulWidget 
  FollowWidget(Key key) : super(key: key);

  _FollowWidgetState createState() => _FollowWidgetState();


class _FollowWidgetState extends State<FollowWidget> 
  bool _followStatus = false;

  void _followClickHandle() 
    setState(() 
      this._followStatus = !this._followStatus;
    );
  

  List<Color> _getGradient() 
    if (this._followStatus) 
      return <Color>[Colors.grey, Colors.grey];
     else 
      return <Color>[const Color(0xFFFF5000), const Color(0xFFFF9000)];
    
  

  List<Widget> _getContent() 
    List<Widget> defaultContent = <Widget>[
      Text(
        this._followStatus ? '已关注' : '关注',
        style: TextStyle(
          fontSize: 16,
          color: Colors.white,
          letterSpacing: 1.2,
        ),
      )
    ];
    List<Widget> prefixContent = <Widget>[
      Image.network(
        'https://gw.alicdn.com/tfs/TB1OC0TXMMPMeJjy1XcXXXpppXa-108-84.png',
        height: 16,
      ),
      SizedBox(width: 3)
    ];
    if (!this._followStatus) 
      defaultContent.insertAll(0, prefixContent);
    
    return defaultContent;
  

  @override
  Widget build(BuildContext context) 
    return InkWell(
      child: Container(
        width: 90,
        height: 40,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(20)),
          gradient: LinearGradient(
            begin: Alignment(-1, 0),
            end: Alignment(1.0, 0),
            colors: this._getGradient(),
          ),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: this._getContent(),
        ),
      ),
      onTap: this._followClickHandle,
    );
  

效果:

四、完整代码

FlutterHelloWorld/main.45-颜色渐变-follow组件.dart at dev1 · postbird/FlutterHelloWorld · GitHub

FlutterHelloWorld/main.45-2-渐变关注按钮.dart at dev1 · postbird/FlutterHelloWorld · GitHub

如果打不开github,看这里的源码即可。

import 'package:flutter/material.dart';

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  const MyApp(Key key) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('渐变'),
        ),
        body: HomeContent(),
      ),
    );
  


class HomeContent extends StatelessWidget 
  const HomeContent(Key key) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          // 径向渐变
          Container(
            width: 200,
            height: 100,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                // Alignment(x,y)  (0,0) 是由 rectangle 中心点计算的
                begin: Alignment(0.0, -1.0),
                end: Alignment(0.0, 1.0),
                colors: <Color>[
                  Color(0xFFFF5000),
                  Color(0xFFFF9000),
                  Colors.black,
                ],
              ),
            ),
          ),
          SizedBox(height: 10),
          // 横向渐变
          Container(
            width: 200,
            height: 50,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment(-1, 0),
                end: Alignment(1.0, 0),
                colors: <Color>[
                  const Color(0xFFFF5000),
                  const Color(0xFFFF9000),
                ],
              ),
            ),
          ),
          SizedBox(height: 10),
          FollowWidget(),
        ],
      ),
    );
  


class FollowWidget extends StatefulWidget 
  FollowWidget(Key key) : super(key: key);

  _FollowWidgetState createState() => _FollowWidgetState();


class _FollowWidgetState extends State<FollowWidget> 
  bool _followStatus = false;

  void _followClickHandle() 
    setState(() 
      this._followStatus = !this._followStatus;
    );
  

  List<Color> _getGradient() 
    if (this._followStatus) 
      return <Color>[Colors.grey, Colors.grey];
     else 
      return <Color>[const Color(0xFFFF5000), const Color(0xFFFF9000)];
    
  

  List<Widget> _getContent() 
    List<Widget> defaultContent = <Widget>[
      Text(
        this._followStatus ? '已关注' : '关注',
        style: TextStyle(
          fontSize: 16,
          color: Colors.white,
          letterSpacing: 1.2,
        ),
      )
    ];
    List<Widget> prefixContent = <Widget>[
      Image.network(
        'https://gw.alicdn.com/tfs/TB1OC0TXMMPMeJjy1XcXXXpppXa-108-84.png',
        height: 16,
      ),
      SizedBox(width: 3)
    ];
    if (!this._followStatus) 
      defaultContent.insertAll(0, prefixContent);
    
    return defaultContent;
  

  @override
  Widget build(BuildContext context) 
    return InkWell(
      child: Container(
        width: 90,
        height: 40,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(20)),
          gradient: LinearGradient(
            begin: Alignment(-1, 0),
            end: Alignment(1.0, 0),
            colors: this._getGradient(),
          ),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: this._getContent(),
        ),
      ),
      onTap: this._followClickHandle,
    );
  
import 'package:flutter/material.dart';

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  const MyApp(Key key) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedContainer Follow'),
        ),
        body: FollowWidget(),
      ),
    );
  


class FollowWidget extends StatefulWidget 
  FollowWidget(Key key) : super(key: key);

  _FollowWidgetState createState() => _FollowWidgetState();


class _FollowWidgetState extends State<FollowWidget> 
  bool _followStatus = false;

  void _followClickHandle() 
    setState(() 
      this._followStatus = !this._followStatus;
    );
  

  List<Color> _getGradient() 
    if (this._followStatus) 
      return <Color>[Colors.grey, Colors.grey];
     else 
      return <Color>[const Color(0xFFFF5000), const Color(0xFFFF9000)];
    
  

  List<Widget> _getContent() 
    List<Widget> defaultContent = <Widget>[
      Text(
        this._followStatus ? '已关注' : '关注',
        style: TextStyle(
          fontSize: 16,
          color: Colors.white,
          letterSpacing: 1.2,
        ),
      )
    ];
    List<Widget> prefixContent = <Widget>[
      Image.network(
        'https://gw.alicdn.com/tfs/TB1OC0TXMMPMeJjy1XcXXXpppXa-108-84.png',
        height: 16,
      ),
      SizedBox(width: 3)
    ];
    if (!this._followStatus) 
      defaultContent.insertAll(0, prefixContent);
    
    return defaultContent;
  

  @override
  Widget build(BuildContext context) 
    return InkWell(
      child: AnimatedContainer(
        width: 90,
        height: 40,
        duration: Duration(milliseconds: 500),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(20)),
          gradient: LinearGradient(
            begin: Alignment(-1, 0),
            end: Alignment(1.0, 0),
            colors: this._getGradient(),
          ),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: this._getContent(),
        ),
      ),
      onTap: this._followClickHandle,
    );
  

以上是关于Flutter 颜色渐变及模仿淘宝渐变关注按钮的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - 如何将图像与渐变颜色混合?

AE 里 如何给中间白色矩形添加一个从白色到透明的渐变效果

iOS 绘制颜色渐变圆环

Flutter InkWell 小部件未通过容器的渐变和颜色显示

cdr9中互动式透明工具渐变怎么让中间颜色轻两边重

Flutter Widget - LinearGradient 渐变