当前位置: 首页 > news >正文

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});@overrideState<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;@overridevoid initState() {super.initState();WidgetsBinding.instance.addPostFrameCallback((_) {final renderBox = _key.currentContext?.findRenderObject() as RenderBox?;if (renderBox != null) {final size = renderBox.size;checkAnimate(size);}});}@overrideWidget 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,))],),);}@overridevoid 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);}});}@overridevoid 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;});}
}

 

http://www.jsqmd.com/news/110923/

相关文章:

  • Vite:现代前端构建工具的革命与实战指南
  • 立体相机标定
  • 2025年12月专业的株洲搬家公司实力榜:株洲家庭搬家、株洲厂房搬迁、株洲钢琴搬运、五家企业凭服务与口碑出圈 - 海棠依旧大
  • ABAP调用CDS AMDP:数据库存储过程封装
  • 专业解读|北京刑事律师事务所实力排名:2025靠谱机构辩护方案对比 - 老周说教育
  • 2025年12月株洲靠谱的搬家公司首选:株洲旺成搬家,株洲搬迁公司、株洲企业搬迁、株洲长途搬家、株洲居民搬家、专业团队护航全程无忧 - 海棠依旧大
  • 2025年煤粉仓防爆门实力厂家权威推荐榜单:重力防爆门/矩形防爆门/弹簧防爆门源头厂家精选 - 品牌推荐官
  • 网络工程毕设2026开题指导
  • 2025年12月楼梯/实木楼梯/实木衣柜门品牌推荐榜单精选 - 2025年品牌推荐榜
  • 2025年12月内蒙古呼和浩特驾校/摩托车驾校推荐榜单 - 2025年品牌推荐榜
  • 半导体测试之静态测试参数有哪些?为什么要进行测试?
  • 5分钟搞定AI字幕生成:ComfyUI终极配置完整指南 [特殊字符]
  • 2025年四机头数控钢筋弯曲中心供应商权威推荐榜单:钢筋智能弯曲中心/自动钢筋弯曲/数控式钢筋弯曲中心源头厂家精选 - 品牌推荐官
  • 极速组态!Profinet转Ethernet网关让ABB机器人主站秒连工业网络(下集)
  • Sketch 2025.3文件夹管理更新解析:支持无限嵌套与灵活组织
  • CentOS 7+ 安装 GreatSQL 8.0.32-26
  • Amazon EC2 实例类型命名约定
  • 实用参考!2025-2026北京律师事务所排行榜:全维度服务能力解析 - 老周说教育
  • P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III
  • 2025年耐气候老化性管材制造企业权威推荐榜单:PVDF管材/耐腐蚀性管材/耐热性管材制造企业精选 - 品牌推荐官
  • 低速离心机2025年市场调研与优质源头厂家(知名企业)推荐 - 品牌推荐大师1
  • Omnissa Horizon 8 2512 发布 - 虚拟桌面基础架构 (VDI) 和应用软件
  • 极速组态!Profinet转Ethernet网关让ABB机器人主站秒连工业网络(上集)
  • 精通Java LaTeX渲染:JLaTeXMath实战应用全解析
  • 期末复习:结构算法题
  • 减肥产品怎么选不踩雷?1000+用户真实反馈,2025 高口碑品牌无滤镜测评榜单 - 速递信息
  • 2025年市面上专业的自建房建设厂商口碑排行,别墅自建房/庭院/院墙/外墙仿石漆/自建房建设品牌如何选 - 品牌推荐师
  • 如何高效定制B站API认证凭证:全新Cookies配置指南
  • PyTorch中GRU与LSTM的构建与比较
  • 【前瞻预告】2025-2026北京律师事务所哪家好?预热榜单与核心机构解析 - 老周说教育