ShaderMask letting through some pixels flutter

Viewed 177

I have a background with some white lines animated from down to up, generated procedurally. Then, I have a ShaderMask who start to fade out the content at 0.5 and finish it at 0.7. The lines spawn on the bottom screen, goes up in around 4 seconds then fade out with the ShaderMask.

My problem is, for every lines, a 1 pixel right or left border will be still visible during and after the fading.

Here is a screenshot for better understanding: (zoom in if you don't see it)

screenshot

As you can guess, the other 1 pixel column you can see are other lines faded away.

Here is the code (I tried to simplify it as much as possible).

import 'package:flutter/material.dart';

import 'dart:math';

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Stack(children: [RandomLines()]),
    );
  }
}

class RandomLines extends StatefulWidget {
  RandomLines({Key key}) : super(key: key);

  @override
  _RandomLinesState createState() => _RandomLinesState();
}

class Line {
  double height;
  AnimationController controller;

  Line({@required this.height, @required this.controller});
}

class _RandomLinesState extends State<RandomLines>
    with TickerProviderStateMixin {
  List<Line> _lines = [];
  AnimationController _tick;
  Random _rand;

  @override
  void initState() {
    _rand = Random();

    for (int i = 0; i < 10; i++) {
      _lines.add(Line(height: 0, controller: null));
    }

    _tick = AnimationController(vsync: this, duration: Duration(seconds: 5));
    _tick
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _tick.reverse();
        } else if (status == AnimationStatus.dismissed) {
          _tick.forward();
        }
      });

    _tick.addListener(() {
      setState(() {});
    });

    _tick.forward();

    super.initState();
  }

  void reset() {
    _lines.forEach((line) {
      line.height = 0;
      if (line.controller != null) {
        line.controller.dispose();
        line.controller = null;
      }
    });
  }

  @override
  void dispose() {
    _lines.forEach((line) {
      if (line.controller != null) {
        line.controller.dispose();
      }
    });
    _tick.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    this._generateLines();
    return Positioned(
        width: MediaQuery.of(context).size.width,
        bottom: -200,
        child: Container(
            padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: _lines.map((line) {
                  if (line.controller == null) return Container(width: 28);
                  return _buildLine(line.height, line.controller.value);
                }).toList())));
  }

  void _generateLines() {
    for (int i = 0; i < _lines.length; i++) {
      if (_lines[i].controller == null) {
        int randomNumb = _rand.nextInt(200);
        if (randomNumb == 0) {
          _lines[i].height = 50 + _rand.nextInt(200 - 50).toDouble();
          _lines[i].controller = AnimationController(
              vsync: this,
              duration: Duration(seconds: 5),
              lowerBound: 0,
              upperBound:
                  (MediaQuery.of(context).size.height + 200).roundToDouble());
          _lines[i].controller
            ..addStatusListener((status) {
              if (status == AnimationStatus.completed) {
                _lines[i].height = 0;
                _lines[i].controller.dispose();
                _lines[i].controller = null;
              }
            });

          _lines[i].controller.addListener(() {
            setState(() {});
          });

          _lines[i].controller.forward();
        }
      }
    }
  }

  Widget _buildLine(double height, double offset) {
    return Container(
        padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
        child: ShaderMask(
            shaderCallback: (rect) {
              return LinearGradient(
                stops: [0, 0.5, 0.7],
                begin: Alignment.bottomCenter,
                end: Alignment.topCenter,
                colors: [Colors.white, Colors.white, Colors.transparent],
              ).createShader(Rect.fromLTRB(0, 0, rect.width, rect.height));
            },
            blendMode: BlendMode.dstIn,
            child: Container(
                width: 8,
                height: MediaQuery.of(context).size.height + 200,
                child: Wrap(runAlignment: WrapAlignment.end, children: [
                  Container(
                      height: height,
                      decoration: BoxDecoration(
                          color: Colors.white.withOpacity(0.8),
                          borderRadius: BorderRadius.circular(10))),
                  SizedBox(height: offset),
                ]))));
  }
}

Thanks you for reading!

0 Answers
Related