Row下的两个Text文本自适应宽度,并以中间为基准滚动

              Row(
                children: [
                  Expanded(
                    child: LayoutBuilder(
                      builder:
                          (BuildContext context, BoxConstraints constraints) =>
                              IntlWidget(
                        maxWidth: constraints.maxWidth,
                        child: Text(
                          '你好啊啊封疆大吏你好你好啊啊封疆大吏你好啊啊封疆大吏啊啊封疆大吏',
                        ),
                      ),
                    ),
                  ),
                  // SizedBox(
                  //   width: 5,
                  // ),
                  // Spacer(),
                  SizedBox(
                    width: 5,
                  ),
                  IntlWidget(
                    maxWidth: calculateTextWidth('你好啊啊你好啊啊',
                        expandedText: '你好啊啊封疆大吏你好你好啊啊封疆大吏你好啊啊封疆大吏啊啊封疆大吏',
                        spacingWidth: 5,
                        maxWidth: MediaQuery.of(context).size.width,
                        maxLines: 1),
                    child: Text(
                      '你好啊啊你好啊啊',
                    ),
                  ),
                ],
              ),

滚动部件

import 'dart:math';

import 'package:flutter/material.dart';


/// ⚠️direction目前切换方向需要重新运行才会正常,不要代码动态控制方向切换
class IntlWidget extends StatefulWidget {
  final Widget child;

  /// 动画方向, horizontal时要设置maxWidth才有效, vertical时要设置maxHeight才有效
  final Axis direction;
  //
  // /// 固定宽
  // final double? width;
  //
  // /// 固定高
  // final double? height;

  /// 最大宽
  final double? maxWidth;

  /// 最大高
  final double? maxHeight;

  const IntlWidget(
      {super.key,
      required this.child,
      this.direction = Axis.horizontal,
      // this.width,
      // this.height,
      this.maxWidth,
      this.maxHeight});

  @override
  State<IntlWidget> createState() => _IntlWidgetState();
}

class _IntlWidgetState extends State<IntlWidget>
    with SingleTickerProviderStateMixin {
   GlobalKey _key = GlobalKey();

  AnimationController? _controller;
  Animation<double>? _animation;

  double? _autoWidth;

  set autoWidth(double value) {
    if (widget.maxWidth != null) {
      _autoWidth = min(value, widget.maxWidth!);
    } else {
      _autoWidth = value;
    }
  }

  double get autoWidth {
    // if (widget.width != null) {
    //   return widget.width!;
    // }
    return _autoWidth ?? 0;
  }

  double? _autoHeight;

  set autoHeight(double value) {
    if (widget.maxHeight != null) {
      _autoHeight = min(value, widget.maxHeight!);
    } else {
      _autoHeight = value;
    }
  }

  double get autoHeight {
    // if (widget.height != null) {
    //   return widget.height!;
    // }
    return _autoHeight ?? 0;
  }

  Size? _contentSize;

  /// 动画偏移值
  double offsetX = 0, offsetY = 0;

  /// 是否缩放
  bool scaleDown = false;

  /// 横向差值范围小于此值时做缩放
  final scaleDownValue = 6;

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      final renderBox = _key.currentContext?.findRenderObject() as RenderBox?;
      if (renderBox != null) {
        final size = renderBox.size;
        checkAnimate(size);
      }
    });
  }



  @override
  Widget build(BuildContext context) {
    return Container(
      // color: Colors.red,
      width: autoWidth,
      height: autoHeight,
      child: scaleDown
          ? FittedBox(
        fit: BoxFit.scaleDown,
        child: Container(
          key: _key,
          child: widget.child,
        ),
      )
          : Stack(
        // clipBehavior: Clip.none,
        children: [
          Positioned(
              top: offsetY,
              left: offsetX,
              // width: widget.direction == Axis.horizontal
              //     ? null
              //     : _autoWidth,//(widget.width ?? _autoWidth),
              // height: widget.direction == Axis.vertical
              //     ? null
              //     : _autoHeight,//(widget.height ?? _autoHeight),
              child: Container(
                key: _key,
                child: widget.child,
              ))
        ],
      ),
    );
  }


  @override
  void didUpdateWidget(covariant IntlWidget oldWidget) {
    if (oldWidget.direction != widget.direction) {
    //   direction = true;
    //   offsetY = 0;
    //   offsetX = 0;
    //   setState(() {});
    //   direction = false;
    //   setState(() {});
    }
    super.didUpdateWidget(oldWidget);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final renderBox = _key.currentContext?.findRenderObject() as RenderBox?;
      if (renderBox != null) {
        final size = renderBox.size;
        checkAnimate(size);
      }
    });
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  checkAnimate(Size contentSize) {
    _contentSize = contentSize;
    // if (widget.direction == )

    double? startAnimateWidth = widget.maxWidth;// ?? widget.width;
    double? startAnimateHeight = widget.maxHeight;// ?? widget.height;
    // print('child 宽度: ${contentSize.width};;;$startAnimateWidth, 高度: ${contentSize.height};; $startAnimateHeight');
    if (widget.direction == Axis.horizontal &&
        startAnimateWidth != null &&
        contentSize.width > startAnimateWidth) {
      _controller?.stop();
      final animateValue = contentSize.width - startAnimateWidth;

      autoWidth = startAnimateWidth;
      autoHeight = contentSize.height;
      scaleDown = animateValue.abs() < scaleDownValue;
      offsetY = (autoHeight - contentSize.height) * 0.5;
      // print("开始动画 scaleDownValue = $scaleDownValue");
      if (scaleDown) {
        offsetX = 0;
        setState(() {});
        return;
      }
      // print("开始动画 scaleDown = $scaleDown");
      final animateSec = (animateValue / 10).ceil();
      _controller ??= AnimationController(
        vsync: this,
      );
      _controller?.duration = Duration(seconds: animateSec);
      _animation?.removeListener(_updateOffsetX);
      _animation?.removeListener(_updateOffsetY);
      _animation =
          Tween<double>(begin: 0.0, end: animateValue).animate(CurvedAnimation(
        parent: _controller!,
        curve: Curves.easeInOutSine,
      ));

      _animation?.addListener(_updateOffsetX);
      // 启动动画
      _controller?.repeat(
        reverse: true,
      );
    } else if (widget.direction == Axis.vertical &&
        startAnimateHeight != null &&
        contentSize.height > startAnimateHeight) {
      _controller?.stop();
      final animateValue = contentSize.height - startAnimateHeight;

      autoWidth = contentSize.width;
      autoHeight = startAnimateHeight;
      scaleDown = animateValue.abs() < scaleDownValue;
      offsetX = (autoWidth - contentSize.width) * 0.5;
      // print("开始动画 vertical scaleDownValue = $scaleDownValue");
      if (scaleDown) {
        offsetY = 0;
        setState(() {});
        _controller?.stop();
        return;
      }
      // print("开始动画 vertical scaleDown = $scaleDown");
      final animateSec = (animateValue / 4).ceil();
      _controller ??= AnimationController(
        vsync: this,
      );
      _controller?.duration = Duration(seconds: animateSec);
      _animation?.removeListener(_updateOffsetX);
      _animation?.removeListener(_updateOffsetY);
      _animation =
          Tween<double>(begin: 0.0, end: animateValue).animate(CurvedAnimation(
        parent: _controller!,
        curve: Curves.easeInOutSine,
      ));
      _animation?.addListener(_updateOffsetY);
      // 启动动画
      _controller?.repeat(
        reverse: true,
      );
    } else {
      setState(() {
        autoWidth = contentSize.width;
        autoHeight = contentSize.height;
        offsetX = 0;
        offsetY = 0;
      });
      _controller?.stop();
    }
  }

  // 更新横向偏移
  void _updateOffsetX() {
    setState(() {
      offsetX = -_animation!.value;
    });
  }

  // 更新纵向偏移
  void _updateOffsetY() {
    setState(() {
      offsetY = -_animation!.value;
    });
  }
}

 

posted @ 2025-12-18 17:21  呢哇哦比较  阅读(3)  评论(0)    收藏  举报