123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- import 'dart:math';
- import 'package:fis_ui/define.dart';
- import 'package:flutter/material.dart';
- /// Animation that displays a [text] element, coloring it to look like sloshing
- /// water is filling it up.
- ///
- /// ![TextLiquidFill example](https://raw.githubusercontent.com/aagarwal1012/Animated-Text-Kit/master/display/text_liquid_fill.gif)
- class TextLiquidFill extends StatefulWidget implements FWidget {
- final Widget? child;
- /// Gives [TextStyle] to the text string.
- ///
- /// By default it is `TextStyle(fontSize: 140, fontWeight: FontWeight.bold)`
- final TextStyle textStyle;
- /// Gives [TextAlign] to the text string.
- ///
- /// By default it is [TextAlign.left].
- final TextAlign textAlign;
- /// Specifies the duration the text should fill with liquid.
- ///
- /// By default it is set to 6 seconds.
- final Duration loadDuration;
- /// Specifies the duration that one wave takes to pass the screen.
- ///
- /// By default it is set to 2 seconds.
- final Duration waveDuration;
- /// Specifies the height of the box around text
- ///
- /// By default it is set to 250
- final double boxHeight;
- /// Specifies the width of the box around text
- ///
- /// By default it is set to 400
- final double boxWidth;
- /// String which would be filled by liquid animation
- final String text;
- /// Specifies the backgroundColor of the box
- ///
- /// By default it is set to black color
- final Color boxBackgroundColor;
- /// Specifies the color of the wave
- ///
- /// By default it is set to blueAccent color
- final Color waveColor;
- /// Specifies the load limit: (0, 1.0]. This may be used to limit the liquid
- /// fill effect to less than 100%.
- ///
- /// By default, the animation will load to 1.0 (100%).
- final double loadUntil;
- /// Enable loop. Default false.
- final bool loop;
- const TextLiquidFill({
- Key? key,
- required this.text,
- this.child,
- this.textStyle =
- const TextStyle(fontSize: 140, fontWeight: FontWeight.bold),
- this.textAlign = TextAlign.left,
- this.loadDuration = const Duration(seconds: 6),
- this.waveDuration = const Duration(seconds: 2),
- this.boxHeight = 250,
- this.boxWidth = 400,
- this.boxBackgroundColor = Colors.black,
- this.waveColor = Colors.blueAccent,
- this.loadUntil = 1.0,
- this.loop = false,
- }) : assert(loadUntil > 0 && loadUntil <= 1.0),
- super(key: key);
- /// Creates the mutable state for this widget. See [StatefulWidget.createState].
- @override
- _TextLiquidFillState createState() => _TextLiquidFillState();
- }
- class _TextLiquidFillState extends State<TextLiquidFill>
- with TickerProviderStateMixin {
- final _textKey = GlobalKey();
- late AnimationController _waveController, _loadController;
- late Animation<double> _loadValue;
- @override
- void initState() {
- super.initState();
- _waveController = AnimationController(
- vsync: this,
- duration: widget.waveDuration,
- );
- _loadController = AnimationController(
- vsync: this,
- duration: widget.loadDuration,
- );
- _loadValue = Tween<double>(
- begin: 0.1,
- end: widget.loadUntil,
- ).animate(_loadController);
- if (1.0 == widget.loadUntil && widget.loop == false) {
- _loadValue.addStatusListener((status) {
- if (AnimationStatus.completed == status) {
- // Stop the repeating wave when the load has completed to 100%
- _waveController.stop();
- }
- });
- }
- _waveController.repeat();
- if (!widget.loop) _loadController.forward();
- if (widget.loop) _loadController.repeat();
- }
- @override
- void dispose() {
- _waveController.dispose();
- _loadController.dispose();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- return Stack(
- children: <Widget>[
- Container(
- margin: const EdgeInsets.symmetric(horizontal: 1), // 解决Mobile多出1像素问题
- height: widget.boxHeight,
- width: widget.boxWidth - 2, // 同上
- child: AnimatedBuilder(
- animation: _waveController,
- builder: (BuildContext context, Widget? child) {
- return CustomPaint(
- painter: _WavePainter(
- textKey: _textKey,
- waveValue: _waveController.value,
- loadValue: _loadValue.value,
- boxHeight: widget.boxHeight,
- waveColor: widget.waveColor,
- ),
- );
- },
- ),
- ),
- SizedBox(
- height: widget.boxHeight,
- width: widget.boxWidth,
- child: ShaderMask(
- blendMode: BlendMode.srcOut,
- shaderCallback: (bounds) => LinearGradient(
- colors: [widget.boxBackgroundColor],
- stops: const [0.0],
- ).createShader(bounds),
- child: Container(
- color: Colors.transparent,
- child: Center(
- child: widget.child != null
- ? Container(
- key: _textKey,
- child: widget.child,
- )
- : Text(
- widget.text,
- key: _textKey,
- style: widget.textStyle,
- textAlign: widget.textAlign,
- ),
- ),
- ),
- ),
- )
- ],
- );
- }
- }
- class _WavePainter extends CustomPainter {
- static const _pi2 = 2 * pi;
- final GlobalKey textKey;
- final double waveValue;
- final double loadValue;
- final double boxHeight;
- final Color waveColor;
- _WavePainter({
- required this.textKey,
- required this.waveValue,
- required this.loadValue,
- required this.boxHeight,
- required this.waveColor,
- });
- @override
- void paint(Canvas canvas, Size size) {
- final RenderBox? textBox =
- textKey.currentContext!.findRenderObject() as RenderBox;
- if (textBox == null) return;
- final textHeight = textBox.size.height;
- final baseHeight =
- (boxHeight / 2) + (textHeight / 2) - (loadValue * textHeight);
- final width = size.width;
- final height = size.height;
- final path = Path();
- path.moveTo(0.0, baseHeight);
- for (var i = 0.0; i < width; i++) {
- path.lineTo(i, baseHeight + sin(_pi2 * (i / width + waveValue)) * 8);
- }
- path.lineTo(width, height);
- path.lineTo(0.0, height);
- path.close();
- final wavePaint = Paint()..color = waveColor;
- canvas.drawPath(path, wavePaint);
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) {
- return true;
- }
- }
|